Diagnosed two independent faults reported from the running editor.
1. Terminal plugin (market) did not work as shipped.
Root cause: it is a port of Fluidbyte/Codiad-Terminal and calls Common::execute(),
which Atheos REMOVED (renamed to raw_execute with a different return shape).
plugin calls : Common::execute(...) -> MISSING
available : Common::raw_execute(...) -> ["code"=>int, "output"=>array]
Fixed to raw_execute + implode of the output array. Verified against the real
method: pwd/echo return correctly, and after chdir it reports the blog repo path,
so the terminal opens in the right directory.
Two further upstream defects found and deliberately NOT fixed, but documented so
nobody trusts them: the block-list is dead code (`explode(",", BLOCKED === true)`
passes a boolean, so ssh/telnet are never blocked), and `strtolower($command ===
"exit")` means typing exit does not clear the auth flag.
Security note recorded: this plugin is a browser shell as the php-fpm user, gated
by a hardcoded in-app constant (PASSWORD="terminal"). It is not a secret. Strongest
argument yet for keeping the container LAN-only.
2. Settings -> Macro dialog returned HTTP 500.
Root cause: Atheos's own components/macro/dialog.php:35 does implode(", ",
$macro["fTypes"]) which requires an ARRAY, but class.macro.php::save() writes the
STRING "N/A" for folder macros. So ANY folder-type macro makes that dialog fatal:
PHP Fatal error: Uncaught TypeError: implode(): Argument #2 ($array) must be of
type ?array, string given in .../components/macro/dialog.php:35
Our writer macros are all folder-scoped, so adding them exposed it.
Fixed by correcting the DATA (fTypes ""N/A"" -> "[]") rather than patching Atheos's
code, so the fix survives an Atheos update. Verified implode() now succeeds for all
8 macros. The trade-off and the proper upstream fix are both documented.
Also records the diagnostic order for any future plugin problem, and the rule of
thumb: a Codiad-era plugin's Common:: calls are probably stale.
Adds:
docs/known-issues.md both faults, root causes, fixes, verification
patches/Terminal-terminal.php.patched the patched plugin file
files/macros.db.php regenerated with the corrected fTypes
155 lines
4.9 KiB
Plaintext
155 lines
4.9 KiB
Plaintext
<?php
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
// Atheos Terminal
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
// Copyright (c) 2020 Liam Siira (liam@siira.io), distributed as-is and without
|
|
// warranty under the MIT License. See [root]/docs/LICENSE.md for more.
|
|
// This information must remain intact.
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
// Copyright (c) 2013 Codiad & Kent Safranski
|
|
// Source: https://github.com/Fluidbyte/Codiad-Terminal
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
require_once("../../common.php");
|
|
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
// Verify Session or Key
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
Common::checkSession();
|
|
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
// Globals
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
$project = SESSION("project");
|
|
|
|
define("PASSWORD", "terminal");
|
|
define("ROOT", Common::getWorkspacePath($project));
|
|
define("BLOCKED", "ssh,telnet");
|
|
// define("ENABLED", "java,ls,cd,echo,javac");
|
|
define("ENABLED", false);
|
|
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
// Terminal Class
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
|
|
class Terminal {
|
|
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
// Constructor
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
public function __construct() {
|
|
if (!isset($_SESSION["activeDir"]) || !isset($_SESSION["activeDir"][$_SESSION["project"]]) || empty($_SESSION["activeDir"][$_SESSION["project"]])) {
|
|
if (ROOT === "") {
|
|
$output = Common::raw_execute("pwd");
|
|
$_SESSION["activeDir"] = array([$_SESSION["project"]] => implode("\n", (array)$output["output"]));
|
|
} else {
|
|
$this->changeDir(ROOT);
|
|
}
|
|
} else {
|
|
$this->changeDir($_SESSION["activeDir"][$_SESSION["project"]]);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
// Primary call
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
public function process($str) {
|
|
$cmd = $this->parseCommand($str);
|
|
if ($cmd === "") return;
|
|
$output = Common::raw_execute($cmd);
|
|
return implode("\n", (array)$output["output"]);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
// Parse command for special functions, blocks
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
public function parseCommand($str) {
|
|
|
|
// Explode command
|
|
$command_parts = explode(" ", $str);
|
|
|
|
// Handle "cd" command
|
|
if (in_array("cd", $command_parts)) {
|
|
$cd_key = array_search("cd", $command_parts);
|
|
$cd_key++;
|
|
|
|
$dir = $command_parts[$cd_key];
|
|
|
|
$this->changeDir($dir);
|
|
// Remove from command
|
|
$str = str_replace("cd $dir", "", $str);
|
|
}
|
|
|
|
// Replace text editors with cat
|
|
$editors = array("vim", "vi", "nano");
|
|
$str = preg_replace("/^(".join("|", $editors).")/", "cat", trim($str));
|
|
|
|
|
|
if (ENABLED) {
|
|
// Handle enabled commands
|
|
$enabled = explode(",", ENABLED);
|
|
if (in_array($command_parts[0], $enabled) === false) {
|
|
$str = "echo ERROR: Command not allowed";
|
|
}
|
|
} else {
|
|
// Handle blocked commands
|
|
$blocked = explode(",", BLOCKED === true);
|
|
if (in_array($command_parts[0], $blocked)) {
|
|
$str = "echo ERROR: Command not allowed";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Update exec command
|
|
return $str . " 2>&1";
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
// Chnage Directory
|
|
//////////////////////////////////////////////////////////////////////////80
|
|
public function changeDir($dir) {
|
|
chdir($dir);
|
|
// Store new directory
|
|
$_SESSION["activeDir"][$_SESSION["project"]] = exec("pwd");
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
// Processing
|
|
//////////////////////////////////////////////////////////////////////////////80
|
|
|
|
$activeUser = SESSION("user");
|
|
$command = POST("command");
|
|
|
|
if (strtolower($command === "exit")) {
|
|
$_SESSION["term_auth"] = false;
|
|
$output = "[EXIT]";
|
|
|
|
} else if (!isset($_SESSION["term_auth"]) || $_SESSION["term_auth"] !== true) {
|
|
if ($command === PASSWORD) {
|
|
$_SESSION["term_auth"] = true;
|
|
$output = "[AUTHENTICATED]";
|
|
} else {
|
|
$output = "Enter Password:";
|
|
}
|
|
|
|
} else {
|
|
$Terminal = new Terminal();
|
|
$output = "";
|
|
$command = explode(" && ", $command);
|
|
debug($command);
|
|
foreach ($command as $c) {
|
|
$output .= $Terminal->process($c);
|
|
// $output .= implode("\n",$Terminal->process($c));
|
|
}
|
|
}
|
|
|
|
$output = array(
|
|
"data" => htmlentities($output),
|
|
"dir" => htmlentities(exec("pwd")),
|
|
"prompt" => "<span class=\"user\">$activeUser</span>:<span class=\"path\">" . exec("pwd") . "</span>$ "
|
|
);
|
|
|
|
Common::send("success", $output);
|
|
|
|
?> |