fix(danger): close pipe-fed xargs bypass, gate root-level and git data-loss verbs#103
Merged
Merged
Conversation
…a-loss verbs
Fixes three classifier findings:
1. Destructive-command bypass via pipe-fed xargs (HIGH): echo "/" | xargs
rm -rf classified as local_write (auto-allow) because piped stdin was
never modelled as xargs arguments. When a pipeline's sink is xargs
invoking a destructive/system verb, statically determinable upstream
payloads (echo/printf args) are now composed onto the inner command
before classification; when the payload is not statically determinable
and the inner verb is dangerous-capable, the pipeline fails closed
(unknown -> deny).
2. Recursive chmod at system roots not treated as system_write (MEDIUM):
ClassifyPath("/") fell through to local_write, so chmod -R 777 / and
mv / /tmp/x ran without approval while chown -R nobody / prompted.
The filesystem root now classifies as system_write, and chattr is
handled with the same operand scan as chmod.
3. Irreversible git data-loss verbs classified safe (MEDIUM): git
clean -f*, reset --hard, checkout -f/--/<pathspec>, restore (worktree),
branch -D / --delete --force, stash drop|clear, and reflog expire now
classify as system_write (prompt-by-default) instead of safe. Dry-run
and non-destructive forms (clean -n, checkout <branch>, branch -d,
restore --staged, stash pop) stay safe.
Regression tests pin the new classifications and the default-policy
actions; docs updated (SECURITY.md, CLI.md, AGENTS.md).
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
odek | 1afb04d | Commit Preview URL Branch Preview URL |
Jul 26 2026, 08:13 AM |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes three danger-classifier findings, all verified against the current code before fixing:
1. Destructive-command bypass via pipe-fed
xargs— HIGHecho "/" | xargs rm -rfwas classifiedlocal_write→ allow, executing a root-destructive command with no prompt and no deny. The classifier composed here-strings and command substitutions into the downstream command, but did not model piped stdin as arguments toxargs.Fix: when a pipeline's sink is
xargsinvoking a destructive/system verb, statically determinable upstream literal payloads (echo/printfargs) are composed onto the inner command before classification —echo "/" | xargs rm -rfnow classifies exactly likerm -rf /(destructive→ deny). When the payload is not statically determinable (cat file | …,find … | …,$VARS, multi-stage producers) and the inner verb is dangerous-capable (rm,shred,dd,chmod,chown,chattr,mv,cp,ln,install,tee,mkfs.*family), the pipeline fails closed asunknown(deny-by-default). Benign sinks (… | xargs grep,… | xargs wc -l) and benign payloads (echo ./tmpfile | xargs rm) keep their original classification.2. Recursive
chmodat system roots not treated assystem_write— MEDIUMchmod -R 777 /waslocal_write→ allow, while the equivalentchown -R nobody /wassystem_write→ prompt. Related:mv / /tmp/xwaslocal_writewhilemv ~/.ssh /tmp/stolenprompted.Fix:
ClassifyPath("/")now returnssystem_write, so mutations aimed at the filesystem root can never downgrade tolocal_write, regardless of verb.chattrwas added towritePrefixesso recursive attribute flips (chattr -R +i /) get the same operand scan aschmod/chown(it previously fell through tounknown).3. Irreversible git data-loss verbs classified safe — MEDIUM
git clean -fdx,git reset --hard,git checkout -- ., andgit branch -Dweresafe→ allow, so a prompt-injection payload (or a confused model) could wipe a working tree with zero friction.Fix: these now classify as
system_write→ prompt by default (policy change — affects agent ergonomics, documented):git clean -f*/--force(unless-n/--dry-runis also given)git reset --hardgit checkout -f/git checkout -- <path>/git checkout .git restore <pathspec>(worktree;--staged-only stays safe)git branch -D/--delete --forcegit stash drop/git stash cleargit reflog expireNon-destructive forms stay
safe:git clean -n,git checkout main,git checkout -b,git branch -d,git stash,git stash pop,git reflog,git restore --staged.Tests
internal/danger/hardening_test.go:TestHardening_XargsPipedPayload— composed-payload variants (echo "/" | xargs rm -rf,-0,-I P, wrapper chains), fail-closed variants (cat,find,yes,$VAR, multi-stage upstream), and benign no-regression cases; pins the default-policy action (deny).TestHardening_RootLevelMutationTargets—chmod -R 777 /,chattr -R +i /,mv / /tmp/x,ClassifyPath("/"), plus benignchmod/chattrunchanged.TestHardening_GitDataLossVerbs— every reclassified verb plus safe-form no-regression cases; pins the default-policy action (prompt).go test ./... -count=1✅,go test -race ./internal/danger/✅,golangci-lint run internal/danger/...→ 0 issues ✅Docs updated:
docs/SECURITY.md,docs/CLI.md,AGENTS.md.