Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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