security: isolate agent behavior from the caller repo
The agent runs inside the CALLER repo's checkout and opencode auto-discovers project-level config from the working tree (opencode.json / .opencode/ for provider/model/permission/mcp/plugins, and AGENTS.md/CLAUDE.md for instructions). opencode has no flag to disable that discovery, so any caller repo could swap the model, unlock skills/permissions, register MCP servers or plugins (arbitrary code execution on the runner, which holds ANTHROPIC_API_KEY/AGENT_TOKEN/TOKEN_*/SSH_PRIV_KEY), or rewrite the rules the agent follows. The trigger gate only restricts who triggers, not what repo content defines behavior. - run-agent.sh: quarantine caller-controlled opencode.json/opencode.jsonc/.opencode/AGENTS.md/CLAUDE.md for the duration of each run (moved aside, restored on EXIT via trap). Restore keeps the committed tree/PR diff unchanged even when the agent commits mid-run. - run-agent.sh: dev-agent golden rules now come from scripts/agent-rules.md (platform-authoritative), injected into the prompt, instead of "read the caller's AGENTS.md and follow it exactly". - scripts/agent-rules.md: new repo-agnostic authoritative rules. - SECURITY.md: documents the isolation model and the requirement that callers pin agent.yml@main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9a3ca95f9e
commit
06df6320a1
+36
@@ -0,0 +1,36 @@
|
||||
# Security model — agent behavior isolation
|
||||
|
||||
Agent behavior — which models run, what skills / permissions / MCP servers they get, and the golden
|
||||
rules they follow — is defined **only** by this `agents` repository. A caller repository must not be
|
||||
able to change it.
|
||||
|
||||
## For caller repos
|
||||
Call the shared workflow pinned to this repo's `main`:
|
||||
|
||||
```yaml
|
||||
uses: ffaerber/agents/.gitea/workflows/agent.yml@main
|
||||
secrets: inherit
|
||||
```
|
||||
|
||||
Do **not** point `uses:` at a fork or a non-`main` ref. The trigger gate, the agent registry, and the
|
||||
isolation controls all live here on `main`; a caller that repoints it opts out of these protections.
|
||||
|
||||
## How isolation is enforced at runtime
|
||||
The agent runs inside the caller repo's checkout, and opencode auto-discovers project-level config
|
||||
from the working tree. Because opencode has **no flag to disable that discovery**, the run
|
||||
(`scripts/run-agent.sh`) quarantines the caller-controlled behavior files for the duration of each
|
||||
run so they cannot override the agent's configuration or execute code on the runner (which holds the
|
||||
workflow secrets):
|
||||
|
||||
- `opencode.json` / `opencode.jsonc` — provider, model, permission, `mcp`
|
||||
- `.opencode/` — plugins, skills, agents, commands, tools
|
||||
- `AGENTS.md` / `CLAUDE.md` — instructions
|
||||
|
||||
They are moved aside before opencode starts and restored after it exits, so the committed tree is
|
||||
unchanged. The authoritative configuration comes from `~/.config/opencode/` (written by
|
||||
`scripts/install-opencode.sh`), and the authoritative golden rules from `scripts/agent-rules.md`,
|
||||
injected into the agent prompt. A caller repo's own `AGENTS.md` is informational to humans only and is
|
||||
never followed as rules by the agent.
|
||||
|
||||
**Consequence:** an agent cannot durably edit these quarantined files *during a run* — its changes to
|
||||
them are not persisted. Change them via a normal human PR instead.
|
||||
Reference in New Issue
Block a user