fix: Terminal plugin (removed API) + Macro dialog crash; document both #3

Merged
hermes merged 3 commits from fix/plugin-and-macro-dialog into master 2026-09-29 16:22:39 +10:00
3 Commits
Author SHA1 Message Date
hermes 976b3f0005 docs: record the review follow-ups, with evidence for the one we declined
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.
2026-09-29 16:22:25 +10:00
hermes 8ea645386f fix: rotate the terminal plugin password, remove it from the docs
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.
2026-09-29 16:20:53 +10:00
hermes 5ff6a06e82 feat: document plugin failures and fix the Terminal plugin + macro dialog crash
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
2026-09-29 16:19:34 +10:00