Context
Follow-up from the security review of #3360 (--repo / spawn.md api_key steps). Finding: MEDIUM — deferred shell injection.
The primitive
packages/cli/src/shared/spawn-md.ts:394:
await runner.runServer(
`mkdir -p /etc/spawn && printf 'export %s="%s"\\n' '${escapedName}' "\" >> /etc/spawn/secrets && chmod 600 /etc/spawn/secrets`,
);
The user-supplied api_key value is base64-encoded locally, then decoded back on the VM inside a printf format that writes export NAME="VALUE" to /etc/spawn/secrets. The file is then sourced from ~/.bashrc.
If the pasted value contains " or a newline, the written file becomes sourceable-but-broken:
# user pastes: foo"; ls /etc; echo "bar
# file ends up: export NAME="foo"; ls /etc; echo "bar"
# next login: ls /etc runs
Impact
- Self-inflicted: user types their own value, so no external attacker surface today
- But: becomes a trust-transfer vector if a template's
description/guide_url tells the user "paste this exact string" — then the malicious string lands in a shell-sourceable file and executes on next login
- Class: deferred code execution, not prompt/input validation
Fix options
-
Keep values base64-encoded at rest and decode at source-time via a wrapper. Removes the whole class — quotes, newlines, $, backticks all pass through opaquely.
# /etc/spawn/secrets.b64 — not shell-sourceable, written format:
NAME=BASE64VALUE
# ~/.bashrc instead sources a loader:
while IFS='=' read -r k v; do export "$k=$(base64 -d <<<"$v")"; done < /etc/spawn/secrets.b64
-
Dotenv-style loader with a parser that does not re-interpret quotes. Still has to handle = / CRLF / line continuation carefully.
-
(weakest) Sanitize the value before writing — strip or escape ", \\, $, backtick, and newline. Fragile; likelier to leave a hole than close one.
Option 1 is simplest and eliminates the class. Also: the b64 file can keep chmod 600 unchanged.
Related
Filed from Slack by SPA
Context
Follow-up from the security review of #3360 (
--repo/spawn.mdapi_keysteps). Finding: MEDIUM — deferred shell injection.The primitive
packages/cli/src/shared/spawn-md.ts:394:The user-supplied api_key value is base64-encoded locally, then decoded back on the VM inside a
printfformat that writesexport NAME="VALUE"to/etc/spawn/secrets. The file is then sourced from~/.bashrc.If the pasted value contains
"or a newline, the written file becomes sourceable-but-broken:Impact
description/guide_urltells the user "paste this exact string" — then the malicious string lands in a shell-sourceable file and executes on next loginFix options
Keep values base64-encoded at rest and decode at source-time via a wrapper. Removes the whole class — quotes, newlines,
$, backticks all pass through opaquely.Dotenv-style loader with a parser that does not re-interpret quotes. Still has to handle
=/ CRLF / line continuation carefully.(weakest) Sanitize the value before writing — strip or escape
",\\,$, backtick, and newline. Fragile; likelier to leave a hole than close one.Option 1 is simplest and eliminates the class. Also: the b64 file can keep
chmod 600unchanged.Related
Filed from Slack by SPA