A large correctness, feature, and hardening release from a full issue-triage
pass followed by an adversarial fresh-eyes review of that same work (which
caught, and this release fixes, four false-negatives the first pass had
introduced). It closes eighteen tracker issues, adds two new configuration and
pack-authoring surfaces, and lands the first two parts of the segment-scoped
evaluation design (#289). The evaluation hot path is unchanged for ordinary
commands; the new all-dialect fan-out and per-rule scoping only do extra work
when a command actually carries dialect-divergent syntax or an executable-scoped
pack is enabled.
Added
- Per-rule target-path exemptions (#284). A new
[rules."<pack>:<pattern>"]
config table withexempt_target_globslets a specific rule stand down when
its operation targets a statically-literal path under an allowed glob (e.g.
~/.claude/jobs/*/tmp/**for theredirect-truncate/rmrules that agent
job-scratch directories collide with). Every other rule still evaluates the
full command, so a destructive suffix is still caught — unlike an[overrides] allow, which is a whole-command bypass. Literal targets only (dynamic-path
rules are deliberately unsupported);..traversal is rejected; the setting
reduces coverage, so it is ignored from an automatically-discovered.dcg.toml
and honored only from user/system/DCG_CONFIG.dcg doctorwarns when it is
configured on a rule that does not support it. - Rules can declare their executables (#289 part B). Destructive patterns
(built-in and external YAML packs, via a newexecutables:key) may name the
executables they apply to; the engine then only evaluates such a rule against a
command segment whose resolvedargv0matches.system.permissionsis the
first migrated pack, so achmodrule no longer fires on agrep -relsewhere
in the line.executablesomitted preserves prior behavior exactly. - Diagnostics honesty (#289 part C).
dcg test/dcg explainnow report when
a denial comes only from the all-dialect analysis and the Bash hook (posix
dialect) would allow — with the--dialect posixreproduction line and an
additivedialect_divergenceJSON field.dcg test/dcg explaininvocations
wrapped in loops, conditionals, or&&chains are no longer blocked on their
own quoted argument. - Cross-pack regression corpus (#289 part D). False-positive shapes from the
closed issues are now replayed against every registered pack across all shell
dialects on each test run, so a shape fixed under one rule is re-checked against
the others. It found four new false positives on its first run (all fixed
here).
Security
- Unknown-dialect evaluation no longer under-matches regex packs (#294). A
command inert under POSIX quoting but destructive undercmd.exe/PowerShell
quoting (echo 'ok & docker system prune -af) is now caught under the default
all-dialect analysis via a deny-wins fan-out, including caret- and
backtick-obfuscated executables (doc^ker). The extra views run only when the
command carries a dialect-divergent byte and an enabled-pack keyword. - Oversized hook input no longer silently skips evaluation (#290). Padding a
destructive command pastmax_hook_input_bytespreviously failed open by
default; the already-read buffer (drained up to a bounded cap) is now scanned
for every embedded command and a destructive one is denied. The default
fail-open posture for genuinely benign oversized input is unchanged. - The protocol denial is emitted before pending-exception persistence (#291),
so a slow or contended allow-once store can no longer delay or suppress a
block.
Fixed
- False positives:
chmod/chown/setfaclno longer pair their flags with
a different command in the same line, in any dialect (#287); the Cloudflare
Wrangler semantic fallback requires actual Wrangler evidence, socommand -v foo,env, andtime fooare no longer denied (#283), and fully-dynamic
executables ($cmd $arg) are no longer misattributed to it either;
core.git:branch-dynamic-tokenno longer fires on non-git commands (#281); a
variable expansion followed by a command substitution inside one double-quoted
word parses correctly (#279);git commit -F -message bodies are treated as
data on every scanning path (#277); literal paths under the per-user Windows
temp directory are exempted like/tmp(#285). - Windows enforcement:
rd /s/del /sare blocked through the live
PowerShell hook path, not only indcg test/explain(#280). - Self-heal robustness (#292):
~/.claude/settings.jsonis now rewritten
atomically (temp + fsync + rename) under a bounded lock, preserving symlinks
and file mode, so a crash or a concurrent writer can no longer truncate it or
drop its permissions. - External pack loading (#293) is bounded by the hook deadline, caps glob
matches and per-file size, and surfaces load failures without--verbose. - Installer: the PowerShell profile check no longer prints a spurious "hook
missing" warning when the hook command is present in its quoted-invocation form
(#282).
Internal
- The evaluation body was factored so the unknown-dialect path can replay it per
dialect (#294);argv0resolution now walks the tokenizer and skips grouping
punctuation, reserved words,casepattern lists, assignments, and wrappers so
executable-scoped rules see the real command word inside compound constructs. - Design decisions #289 A/B are staged: the corpus (D) is the regression net and
executables=(B) migrates pack-by-pack; #288/#289 remain open as the anchors.