Skip to content

Orchestrator-owned guardrails: protected config, spawn-time hook packs, injection and secret scanning - #423

Merged
currentbits merged 9 commits into
mainfrom
coder/orchestrator-owned-guardrails-protected--a32742
Aug 17, 2026
Merged

currentbits merged 9 commits into
mainfrom
coder/orchestrator-owned-guardrails-protected--a32742

Conversation

@currentbits

@currentbits currentbits commented Aug 17, 2026 •

Copy link
Copy Markdown
Owner

Closes #409.

The agent cannot police itself: a prompt injection that lands in its context can rewrite the very hooks meant to stop it. Solenta is the one layer that sits outside the agent, so policy lives there.

electron/guardrails.js is a pure, dependency-free module with three functions. Every enforcement point shares it, so there is one policy rather than one per call site.

Protected config and the spawn-time hook pack

classifyTool() runs at the Claude can_use_tool seam in runner.js. A deny is answered immediately and never reaches the user — the whole point is that an injected agent doesn't get to social-engineer a yes. An ask falls through to the normal permission prompt with the reason shown, so the user isn't approving blind.

  • deny writes to .claude/hooks, .claude/settings*.json, .claude/agents, .mcp.json, .github/workflows, .git/hooks, .git/config, coder-store.json
  • deny reads of .env, *.pem/*.key, .ssh, .aws, .npmrc and private keys (.env.example and friends still pass)
  • deny curl-pipe-to-shell, force-push, sudo, and rm -rf outside the worktree
  • ask on network egress and on writes landing outside the worktree

Inbound injection scanning

scanInjection() covers override phrases, fake system tags, concealment, exfil and credential-harvest imperatives, zero-width and bidi characters, and hidden HTML comments.

The memory server is a cross-agent propagation channel — poison one entry and every future thread that loads it is compromised — so poisoned memory_store and memory_supersede writes are rejected with the matched rule named. The MCP tools now set source themselves; previously an agent could label its own write app and walk straight past a check keyed on source === 'mcp'.

Issue bodies are banner-prefixed rather than blocked: the user still needs the issue, and the agent needs to read it as data.

Outbound secret scanning

scanSecrets() blocks gh pr create, git push and generated commit messages on AWS/GitHub/Anthropic/OpenAI/Slack/Google keys, PEM headers, JWTs and generic secret assignments, with a placeholder filter to keep your-api-key-here from tripping it. Matches are redacted everywhere they are reported — a guardrail report that prints the secret has just leaked it. A first push diffs from the fork point rather than HEAD~1, which would have covered one commit out of however many the branch carried.

Safety properties

CODER_GUARDRAILS=off is the single kill switch. Every path fails open: a guardrail bug must not brick tool calls, issue fetches, the memory server, or pushing. The 2 MB diff-scan cap logs what it skipped rather than letting silence read as coverage.

Verification

  • electron 1146/1147 — the one failure is the pre-existing web-integration reconnect baseline on main
  • memory-server 160/160, renderer 883/883, tsc --noEmit clean

Built by three parallel grok workers off a shared contract commit; the joins were reviewed and hardened here.

  • PR created by the Claude Code agent

Willem and others added 9 commits August 17, 2026 22:13
Pure, dependency-free policy shared by every enforcement point: tool-call
classification (protected config, credential reads, shell tiers, writes
outside the worktree), inbound injection scanning, outbound secret scanning.
CODER_GUARDRAILS=off disables; every path fails open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deny answers the CLI immediately (no pending prompt, no awaitingInput)
and writes a transcript event. Ask still prompts, with {rule,reason}
attached for the UI. classifyTool throws fail open to today's prompt.
Reject agent-written (source mcp) memory entries that match prompt-injection
patterns. Prefix poisoned GitHub issue bodies with an untrusted-content
banner so the agent still sees the issue, as data.
Block gh pr create, git push, and generated/used commit messages when
scanSecrets finds credentials. Hits throw on the existing Error channels
with redacted excerpts. Scanner and diff failures log and fail open.
The ask tier only helps if the user sees why the call was flagged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…into coder/orchestrator-owned-guardrails-protected--a32742
The MCP tools now set source themselves: an agent that could label its own
write 'app' walked straight past the scan, which is keyed on source 'mcp'.
Scan supersede too — rewriting a live entry poisons the same channel as
writing a new one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…into coder/orchestrator-owned-guardrails-protected--a32742
Share one assertNoOutboundSecrets instead of a second copy in commitmsg.
Diff from the fork point on a first push: HEAD~1..HEAD covered one commit
of however many the branch carried, which reads as coverage it did not have.
Log the 2 MB truncation for the same reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@currentbits currentbits changed the title Orchestrator-owned guardrails (#409) Orchestrator-owned guardrails: protected config, spawn-time hook packs, injection and secret scanning Aug 17, 2026
@currentbits
currentbits merged commit 14812e3 into main Aug 17, 2026
1 check failed
@currentbits
currentbits deleted the coder/orchestrator-owned-guardrails-protected--a32742 branch October 3, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Orchestrator-owned guardrails: protected config, spawn-time hook packs, memory-write scanning

1 participant