Second review round asked for three things. Two adopted, one measured and declined.
ADOPTED
- Removing the Terminal plugin is recommended in writing as the right move (it is the
largest attack surface, and macros already provide command execution). Left installed
because it is the operator's plugin - removing a tool unilaterally is not the agent's
call. Documented how: rename to Terminal.disabled (readDirectory() skips *.disabled).
- Automatic password rotation and the deploy-key-read-only suggestion are recorded as
operator decisions with their trade-offs.
DECLINED, WITH EVIDENCE
- "php.ini should disable exec/shell_exec/passthru/system by default." Measured, this
would break the editor's core function, so it cannot be a default:
components/codegit/* -> Common::safe_execute 31 call sites (every git op)
components/macro/* -> Common::raw_execute 1 call site
common.php raw_execute -> function_exists("system") / function_exists("exec")
Every commit, push and fetch from the editor runs through exec/system. It stays a
deliberate exposure lever (S-7), not a default.
CORRECTED
- Review claimed the redacted patch file had a syntax error. Verified on the guest with
php83 -l: "No syntax errors detected". The placeholder is a plain string literal inside
a double-quoted PHP string. That finding was wrong, and is recorded as such rather than
"fixed" to satisfy a false positive.
Also fixed the section ordering in the doc.
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.
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