fix(core-engine): shell reflection counts dispatch, not command substitution (#2722) - #2725
Merged
Merged
Conversation
…itution (#2722) `shell`'s `reflection_metaprogramming` rule counted two pieces of ordinary shell as metaprogramming. Since #2719 that count IS the file's dynamism -- read by documentation risk and cognitive-load heat on top of the security metrics that already read it -- so the over-match became structural. * `\$\{!?[a-zA-Z0-9_]+\}` made the indirection marker OPTIONAL, so plain `${var}` matched: 4,117 of the crucible's 4,863 shell hits (85%) were variable expansions, while `${!var}` itself never occurs in the corpus. * `$(...)` and backticks counted every command substitution, one hit per ~33 lines of real shell. Running a program yields data, not code, and no other language's rule counts invocation -- python's does not fire on `subprocess.run`. Same shape as the `control_flow_ratio` leak in #2705. Both dropped. What remains is dispatch decided at runtime by a name, which is what the rule's own comment always claimed it was: inline sub-language programs and `${!var}`, plus (per the issue) `eval` unanchored -- the old `\beval\s+\$` missed `eval "$cmd"` -- namerefs, and `source`/`.` of a computed path. 4,863 -> 227 hits, 73 of 255 files still carrying one. Golden master re-blessed, both venvs: 508 diffs, 0 topological, all of them shell files (the `shell/` tree plus wrf's .csh/.ksh scripts and tigerbeetle/download.sh), every other language zero. Exactly one value rises: `tclConfigShToMake.sh` gains a hit for `. "$1"`, the new computed-source alternative doing its job. `reflection_metaprogramming` is unplanted in keyword-rosetta, so no corpus re-bless is owed. Part of #2716. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
27 tasks
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.
What
shell'sreflection_metaprogrammingrule counted two pieces of ordinary shell as metaprogramming.Since #2719 that count is the file's dynamism -- read by documentation risk (weight 1.0) and
cognitive-load heat (x5), on top of the security metrics that already read it -- so the over-match
became structural rather than cosmetic. Closes #2722.
Measured over the crucible's 255 shell files (26,274 coding LOC), each alternative counted
independently (the sum exceeds the rule's 4,863 hits because
$(...)swallows nested matches):${var}!?made the indirection marker optional$(...)`...`awk|sed|perl|python|ruby '...'eval $$anchor missedeval "$cmd"${!var}${!var}never occurs in the corpus at all: every one of those 4,117 hits was a plain variable.The registry settles the
$(...)question. Every other language's rule counts name-based dynamicdispatch and introspection -- python
getattr/setattr/inspect., goreflect./unsafe./cgo,ruby
method_missing/define_method, jsReflect/Proxy/__proto__. None counts invoking anotherprogram; python's rule does not fire on
subprocess.run.$(date)is that same call, and its resultis data, not code. It is the same shape as the
control_flow_ratiovocabulary leak in #2705.The rule
Before:
After:
Dropped:
$(...), backticks, and the!?that admitted${var}. Kept: the inline sub-languageprograms and
${!var}-- which is exactly what the rule's own comment always claimed it was,"sub-languages and indirect expansion". Added, per the issue's suggested fix:
evalunanchored,namerefs
declare/typeset/local -n, andsource/.of a computed path.The overlaps with
pointers(${!,declare -n) andsafety_bypasses(eval) are deliberate andpre-existing -- opacity signals overlap across the registry and each consumer reads its own.
Considered and left out:
trap '...' SIG(code in a string, but owned bycleanup) and a"runs a command" home for
$(...), which would be a new signal, not this fix.Effect: 4,863 -> 227 hits, 73 of 255 files still carrying one.
configure-helper.sh, the issue'sworst case, goes 1,019 -> 15.
Tests
test_shell_strict.py's signature pair is now('reflection_metaprogramming', 'eval "$cmd"', 'output=$(date)')-- yesterday's positive is today's negative, which is the finding itself.$(...)regression becametest_shell_reflection_metaprogramming_is_dispatch_not_vocabulary:${var},$(date),`ls`and theDIR=$(cd "$(dirname "$0")" && pwd)idiom must NOT match;eval,${!name}, namerefs, computedsourceand inlineawkmust.${!,source $,awk 'runs) andkeeps the historical
$(input, now asserted immune and unmatched.Gauntlet
audit_check.py --regenerate: all clear (ruff 70-finding baseline + format clean, mypy 2-errorbaseline, dead-key, ast-accuracy) -- no baseline regenerated.
tree_sitter_accuracy_audit --ci --all: 30/30 OK.tri_comparison_chart --all --ci: 3/3 OK.rosetta_audit: 46 languages, 0 regressions.Golden master
Both fixtures re-blessed. 508 diffs, 0 topological, and every substantive one is a shell file:
shell/treewrf's.sh/.csh/.kshbuild and regression scriptstigerbeetle/download.shEvery other language in the fixture has zero diffs. Movement is down, as expected -- Metaprogramming
& Reflection mean -37.87 (max -1004 on
configure-helper.sh), Cognitive Load mean -19.07,Documentation mean -34.70. 20 files' Documentation Exposure falls by >=25, which is the reversal of the
Exactly one value in the whole corpus goes up:
shell/sqlite/tclConfigShToMake.shgains 1reflection hit for
. "$1"-- dot-sourcing a path taken from$1, in a file whose own header commentcalls itself "a level of indirection". That is the new
source/.alternative doing its job.reflection_metaprogrammingis not in anyexpected_signals.json, so keyword-rosetta is blind to thechange and no corpus re-bless is owed; the two hits the new rule finds there are both
eval :inshell/b.sh, planted assafety_bypasses.Part of #2716.