Responds to the independent security review, which correctly flagged that the previous
commit published a live credential.
The Terminal plugin's in-app password gate is a hardcoded constant. The previous commit
quoted the value verbatim in docs/known-issues.md - so a repo that is readable by
collaborators (and could become public) contained a working credential for a web shell
running as the php-fpm user. That was my error.
Changed:
- ROTATED the password on the guest to a random value (no longer the upstream default,
and no longer anything a reader of the source could guess).
- REMOVED the value from docs/known-issues.md. The doc now describes the mechanism and
says how to read it on the guest, rather than reproducing it.
- REDACTED it from patches/Terminal-terminal.php.patched (shows a placeholder; the real
value lives only on the guest).
- Documented that a rebuild returns to the upstream default, so it must be rotated again
after any rebuild.
Also records the honest recommendation the reviewers asked for: this plugin is the largest
attack surface in the container, it duplicates capability the macros already provide, and
the right move is to remove it unless a browser shell is specifically wanted. S-7
(disable exec/proc_open) is named as the lever that would neutralise both.
Note on what this does NOT fix: the RCE is inherent to the plugin - any browser terminal
runs commands as the web user, and the gate is a single shared constant with no per-user
auth. Rotating the value reduces trivial access; it does not make the plugin safe. That is
stated plainly rather than papered over.
7.6 KiB
Known Issues & Patches
Upstream Atheos is an actively-moving project with no releases, and its plugin market ships plugins that are ports from Codiad (Atheos's predecessor). Two of those plugins use APIs that Atheos has since removed or changed. This file records what we hit, what we changed, and why the fix is done the way it is.
Pinned Atheos commit: f90cd1806dbe0e25673a77e27390e8b1a240c3f6 (build 611).
1. 🔴 Plugin market: the Terminal plugin does not work as shipped
Symptom: installed the Terminal plugin from the market; it failed / did not function.
Root cause — a removed API. plugins/Terminal/terminal.php calls Common::execute(...), which
no longer exists in Atheos (it was renamed):
plugin calls : Common::execute(...) -> MISSING
available : Common::raw_execute(...) -> returns ["code" => int, "output" => array]
Common::safe_execute(...)
The plugin's header says it is a port of Fluidbyte/Codiad-Terminal, so it targets the old API. Its
return-shape assumption is also stale (the old execute() returned text; raw_execute() returns
output as an array).
Fix applied (patches/Terminal-terminal.php.patched):
- $output = Common::execute("pwd");
+ $output = Common::raw_execute("pwd");
- $_SESSION["activeDir"] = array([$_SESSION["project"]] => $output["text"]);
+ $_SESSION["activeDir"] = array([$_SESSION["project"]] => implode("\n", (array)$output["output"]));
- $output = Common::execute($cmd);
- return $output["text"];
+ $output = Common::raw_execute($cmd);
+ return implode("\n", (array)$output["output"]);
Verified against the real method (php83 -l clean; both call patterns exercised):
| Call | Result |
|---|---|
raw_execute("pwd") |
code=0, output=["/root",""] |
raw_execute("echo ...") |
code=0, returns the text |
after chdir to the repo |
returns /var/www/atheos/workspace/blog — so the terminal opens in the blog |
Re-apply after an Atheos update — scripts/update-atheos.sh replaces the application files, so
this patch is lost on update. Re-apply it, or drop the plugin.
Two further defects in the same plugin (NOT fixed — upstream, and mostly harmless)
terminal.php:95—$blocked = explode(",", BLOCKED === true);passes a boolean toexplode(). This evaluates toexplode(",", false), so the intended block-list (define("BLOCKED","ssh,telnet")) never actually blocks anything. The plugin's own guard is dead code. Cosmetic in effect (the command would just fail on its own), but worth knowing rather than trusting.terminal.php:124—if (strtolower($command === "exit"))passes a boolean tostrtolower(). Should bestrtolower($command) === "exit". Result: typingexitdoes not clearterm_auth; the session stays authenticated until the browser session ends.
Left as-is: they are upstream bugs, and fixing them changes the plugin's behaviour beyond the minimum needed to make it run. Recorded here so nobody "fixes" them by accident or trusts the block-list.
⚠️ Security note
This plugin gives a browser terminal that executes shell commands as the php-fpm user
(atheos). Two things matter:
- The plugin ships with a well-known, hardcoded gate constant. Upstream is literally
define("PASSWORD", "<the default value in the plugin source>");. Anyone who can reach the page can read the source on GitHub and unlock it. We have rotated it to a random value — retrieve it from thePASSWORDdefine inplugins/Terminal/terminal.phpon the guest. It is deliberately not recorded in this repo (see below). - It is a shell, so treat it as a privilege decision, not a convenience one. Installing it
means the editor can run arbitrary commands as
atheos— the same user that owns the deploy key.
Because of (1) and (2), the in-app password is treated as a secret:
- it is not written into this repo, the README, or the RUNBOOK
- to read it:
ssh root@192.168.178.57 'grep PASSWORD /var/www/atheos/plugins/Terminal/terminal.php' - it changes per install;
scripts/provision.shdoes not set one, so a rebuild returns to the upstream default — rotate it again after a rebuild
Recommendation: remove this plugin unless you specifically want a browser shell. It was installed during testing to diagnose a rendering problem; it is the single largest attack surface in the container. Note that Atheos macros already provide command execution, so the plugin adds little you do not already have — and macros are at least under your own configuration.
docs/security-notes.md §2 S-7 covers disabling exec/proc_open, which would neutralise both
this plugin and the macros. That is the correct lever if you ever expose this container.
2. 🟠 The Macro dialog crashed when folder-scoped macros existed
Symptom: the Settings → Macro dialog returned HTTP 500.
Root cause — invalid data by Atheos's own design. components/macro/dialog.php:35 does:
implode(", ", $macro["fTypes"]) // requires an ARRAY
but components/macro/class.macro.php save() writes the string "N/A" for folder macros:
"fTypes" => $type === "file" ? explode(",", $fTypes) : "N/A",
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 /var/www/atheos/components/macro/dialog.php:35
Fix applied — correct the DATA, not Atheos's code. The stored fTypes for folder macros is now
[] (an empty array) instead of "N/A":
sed -i 's/"fTypes": "N\/A"/"fTypes": []/g' /var/www/atheos/data/macros.db.php
Verified: implode() now succeeds for all 8 macros.
Why data rather than code: patching dialog.php would be overwritten by update-atheos.sh
(and by any Atheos update), whereas valid data keeps working. The trade-off is that the UI's own
"save" path could re-introduce "N/A" if a macro is edited in the UI — see the follow-up below.
Follow-up (not done): the real upstream fix is
$type === "file" ? explode(",", $fTypes) : [] in class.macro.php::save(). Applying it would stop
the UI re-introducing the bad value, at the cost of a patch that must be re-applied after updates.
Worth doing if the dialog starts 500-ing again after editing a macro in the UI.
3. Checking for plugin problems in future
The plugin's JS is bundled into public/plugins.min.js (built by classes/sourcemanager.php), and
its rightbar button is rendered by components/sidebars/sb-right.php from plugin.json. Both were
verified working:
plugins.min.js contains 'plugins/Terminal/init.js' -> YES (bundle is fine)
rightbar HTML -> <a onclick="atheos.Terminal.open();">
<i class="fas fa-terminal"></i>Terminal</a>
So when a plugin misbehaves, check in this order:
# 1. did it install?
tail /var/www/atheos/data/log/market.log.php
# 2. is its JS in the served bundle?
grep -c "plugins/<Name>/init.js" /var/www/atheos/public/plugins.min.js
# 3. is it a stale-API port? (Codiad-era plugins are the risk)
grep -rhoE 'Common::[a-zA-Z_]+' /var/www/atheos/plugins/<Name>/ | sort -u
# then check each exists: grep -rn "function <name>" /var/www/atheos/common.php /var/www/atheos/traits/
# 4. is there a PHP fatal?
grep -E 'PHP Fatal' /var/log/nginx/atheos.error.log | tail
Rule of thumb: if a plugin was ported from Codiad, assume its Common:: calls are stale and
verify them before trusting it.