Skip to content

Latest commit

 

History

History
98 lines (78 loc) · 5.6 KB

File metadata and controls

98 lines (78 loc) · 5.6 KB

Security Policy

Reporting a vulnerability

Please do not open a public issue for security vulnerabilities.

Report privately via GitHub Security Advisories — open the repository's Security → Report a vulnerability form — or email daonhan@gmail.com. Include a description, reproduction steps, and the affected version. You'll get an acknowledgement within a few days and a fix or mitigation plan.

Supported versions

Only the latest published minor of each package (@daonhan/ralph, @daonhan/ralph-core) and the latest ralph-sandbox image are supported with security fixes. Pin by digest for the image and by exact version for the packages if you need reproducibility.

Threat model — read before running

Ralph is an autonomous agent harness. By design it runs the selected coding agent without interactive approval inside the sandbox container:

  • Claude uses --permission-mode bypassPermissions.
  • Codex uses --dangerously-bypass-approvals-and-sandbox.

Treat everything Ralph ingests as instructions the selected agent may execute. The trust boundary is:

  • Only run Ralph against repositories, plans/PRDs, and GitHub issues you trust. The plan/PRD string ({{ INPUTS }}), issue bodies/comments (ralph-ghafk), and commit messages are all fed to the selected agent running without interactive approval. ralph-ghafk in particular pulls public GitHub issues — text authored by strangers — into that agent. Do not point it at a repo whose open issues you have not vetted.

  • The host Docker socket is bind-mounted by default, which grants the sandbox root-equivalent access to the host Docker daemon (it can start a sibling container that bind-mounts the host filesystem as root). This is on so Testcontainers inside the sandbox can spawn sibling containers. Disable it with RALPH_DOCKER_SOCK=0 when running anything you do not fully trust. Disabling it removes host-Docker control, but persistent host-write exposure still includes the bind-mounted workspace and, for Claude, the read-write credential store. ~/.config/gh remains read-only.

  • Selected-provider host credentials are bind-mounted. Claude mounts ~/.claude and ~/.claude.json read-write; the agent can read or overwrite those reusable credentials. Codex mounts ~/.codex read-only at /mnt/codex-creds and copies auth.json (plus config.toml and AGENTS.md when present) into a container-local CODEX_HOME; the agent cannot modify the host Codex store, but auth.json remains a readable reusable secret. ~/.config/gh is mounted read-only. Isolated Codex configuration prevents personal config, MCP, and hook loading; it does not conceal ~/.codex/auth.json from the process.

  • Ralph's own shipped skills are mounted read-only. The templates/skills/ directory of the installed @daonhan/ralph-core (today one skill, ralph-tdd) is bind-mounted into every stage at a container-local path — Claude /home/agent/ralph-skills/.claude/skills, Codex /home/agent/.agents/skills. It holds only Ralph's own shipped files, contains no secrets, and the agent cannot modify it or the host copy. No host directory is created by the mount.

  • The Claude sandbox fetches its CLI from the network at run time. Every Claude stage runs claude update before its own command, downloading the Claude Code binary with the same trust as the curl … install.sh the image build runs, and caches it in the host-wide named Docker volume ralph-claude-home (shared by every workspace on the host, mounted at /home/agent/.local). Set RALPH_CLAUDE_UPDATE=0 to pin the stage to the image's baked copy and drop the volume mount.

  • The Codex sandbox does the same. Every Codex stage runs codex update before its own command, fetching from the npm registry with the same trust as the build-time npm install --global "@openai/codex@${CODEX_VERSION}", and caches it in the host-wide named Docker volume ralph-codex-cli (mounted at the agent-owned npm prefix /home/agent/.npm-global). Set RALPH_CODEX_UPDATE=0 to pin the stage to the copy the image ships and drop the volume mount.

  • The run event log is advisory, not a security boundary. Each run's .ralph/history/<runId>.jsonl sits in the bind-mounted workspace the agent can write to, so the agent can forge, edit or delete records. The one-run-per-workspace check and any supervisor reading the log trust what it says; a forged live log can refuse relaunches (exit 75) until it is removed, and a deleted one lets a second run start. The log repeats inputs (the plan/PRD string) and every stage's agent output, so it carries the same sensitivity as the Markdown history beside it.

Reducing blast radius

  • Set RALPH_DOCKER_SOCK=0 unless you specifically need Testcontainers.
  • Run Ralph on a disposable VM / dedicated machine, not your primary workstation, for untrusted inputs.
  • Review open issues before running ralph-ghafk.
  • Use a scoped, short-lived gh token.

Template authoring (contributors)

The prompt-template renderer (render.ts) executes the command bodies of the !`cmd`, !?`cmd`, and @spill tags on the host shell. The shipped templates only ever use static command strings, and {{ INPUTS }} is substituted last (written to a file the agent reads inside the container, never re-shelled on the host) — so there is no host command-injection vector today. This invariant must be preserved: never interpolate runtime or untrusted data into a tag command body. Doing so would create direct host RCE.