Skip to content

[Bug]: /etc/spawn/secrets is shell-sourced — escape user-supplied api_key values or stop sourcing #3361

@la14-1

Description

@la14-1

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

  1. 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
  2. Dotenv-style loader with a parser that does not re-interpret quotes. Still has to handle = / CRLF / line continuation carefully.

  3. (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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingin-progressIssue is being actively worked onsecuritySecurity vulnerabilities and concernsunder-reviewIssue is being reviewed by the team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions