fix: a target that matches everything, a leak guard that never failed, and two CLI answers - #109
Merged
Merged
Conversation
`targeting_is_set` asked whether the expression had any positive term. That catches `!chrome.exe`, which names only things to spare, and misses `*`, which is a positive term naming the whole machine. Measured before the fix: `--loss 100 --target *` printed no warning while `--loss 100` alone did, so the expression that bounded nothing looked safer than no expression at all. That is the scenario the project's own rule about never breaking traffic globally exists for. `Matcher.bounds_nothing` now carries both halves in one property, and the one property is the point - reaching for either alone is how this got through. It answers by asking the compiled terms against groups of probe values rather than by reading their text, because "matches everything" is not a syntactic property: `>0` and `0-999999` cover every process without a wildcard in sight, and `*.*` covers only names containing a dot despite looking universal. The probes are grouped by address family, because a rule can cover the whole of IPv4 and none of IPv6 and still bound nothing worth having. It is a heuristic and says so. The direction is deliberate: a false positive costs one advisory line, a false negative costs the user's network. The guard checks each unbounded form beside a genuinely narrow one, so a fix that merely warns more often does not pass. The existing live rig for this warning still reports five of five, including two real captures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ssed `check_public_text.py` stands between this project and the one surface it cannot take back, it runs in CI, and it had reported clean many times - which proves it ran, not that it looks. The first canary found it catching a user-profile path, an e-mail address, a quoted person and a token shape, and letting a private network address straight through, while the convention it enforces names addresses first. A pattern for private address ranges was measured and rejected: 36 tracked files legitimately carry one, including the CIDR examples in both READMEs, the help text, the error messages and this project's own probes. A guard that fires on correct content is switched off within a week and takes the true hits with it. So the check is split the way a leak detector has to be. The engine stays in the repository and knows only the shape; the literals live outside version control and are read at run time. The report names the file and the line and never the matched value, because its output goes into CI logs, which are exactly as public as the commit. A missing list is announced rather than passed over in silence. The canary is now a test rather than something somebody runs once. One of its cases pins a known gap: text in the other language written without diacritics passes, because that check is a diacritics detector - the first draft of the canary assumed otherwise and would have reported the rule as covered. Another one was caught by an existing guard while being written: a leak detector may not spell out the strings it hunts for, so every address in it is assembled at run time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A ceiling on the worst single item watches one thing growing to a record. It is blind to the shape this project actually drifts into: several files climbing together, none of them a record, all of them past the point where a person can follow them. Proven rather than argued. A 950-line module split into fifty small functions passes every ceiling in the file and is caught only by the new count of items already in the top band. The band and both counts are measurements, not choices. The distribution here is steeply skewed - one module sits at the ceiling and the next is at sixty percent of it - so a tighter band would be a constant and would say nothing, while at seventy percent the next candidate is real and has about a hundred and thirty lines to go. A second test keeps the counts exact rather than generous, because a ratchet parked one above the truth admits the next arrival in silence, which is the drift it exists to catch. Failure messages now carry the remaining headroom. The largest module had twelve lines left and nothing said so until it ran out. Also completes the test that keeps the metric honest. Proving comments do not move the measure is satisfied perfectly by a measure that counts nothing, and a ceiling standing on a stuck metric passes for ever while the code grows underneath it. The symmetric half is now checked too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A command line arrives FINISHED, so one problem per run means the user fixes it, runs again and meets the next one. Measured before the change: `--loss 500 --latency -5 --dup 900` named the latency and stopped. Three mistakes, three runs. `settings.range_errors` is a second reader of the same field registry, not a replacement. The form still fails on the first field, because it is typed into LIVE and complaints about fields nobody has reached yet are noise; `validate_ranges` keeps its contract, so no GUI caller moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An unknown --preset answered with all seventeen canonical ids and left the reader to spot the difference. The tool already offers a nearest match for a mistyped config key and a mistyped scenario key, so a preset name was the one closed vocabulary still without it. The suggestion is drawn from what a person can TYPE, which is why it searches the translated names as well as the ids: somebody reaching for "modem56k" writes that, not "presets.modem56k", and a search over ids alone finds nothing close and stays silent exactly when it is needed. A second, folded pass catches a near-miss that differs only in case or diacritics - the same tolerance an exact match already gets. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Five changes, each its own commit. Two are safety or guard holes found by reading a set of
general engineering rule documents against this code; two make the CLI answer a bad input the
way the rest of the tool already does; one gives the size ratchet the knob it was missing.
Everything here was checked against the code before it was written: a rule that this project
already followed was not turned into a change (that is what most of the reading produced -
nothing).
A target that matches everything is not a target
Start with impairment on and
--target *, and the tool stayed quiet, because a target WAS set -to every process on the machine. The blast-radius warning is the one that keeps a run from taking
the network down with the shell that started it, and an expression that narrows nothing is exactly
the case it exists for.
Matcher.covers_everythinganswers by PROBING, not by pattern-matching the text: each matcherclass carries a small group of representative values, and an expression that accepts the whole
group covers everything.
*,re:.*,1-65535,0.0.0.0/0and a PID range spanning the spaceall trip it; a real target does not, and neither does a real target with an exclusion beside it.
Probing is why a NEW spelling of "everything" cannot slip past - it never had to be listed.
Show the leak guard failing, and teach it the class it missed
tools/check_public_text.pyruns in CI and had reported "clean" many times, which proved it runs -not that it catches anything. There was no test that ever saw it FAIL.
tests/test_public_text_guard.pynow feeds it each thing it is supposed to catch and asserts itsays no. Writing those cases found the gap: the guard knew patterns (a path shape, an address
shape), so a value that is only private because it is THIS machine's was invisible to it. The list
of those values lives outside the repository, in an ignored file, and the guard reports a hit
without printing the value - printing it would put it in the CI log, which is the thing being
prevented. With no list present the guard behaves exactly as before.
One known gap is pinned by a test rather than papered over: Polish written without diacritics
passes the "is this English" check, because it is indistinguishable from English by that check.
The test records it so the next reader learns it from the suite instead of from a merged PR.
Report every bad value in one run
--loss 500 --latency -5 --dup 900named the latency and stopped. Three mistakes, three runs.settings.range_errorsis a second reader of the same field registry, not a replacement. The formstill fails on the first field: it is typed into LIVE, and complaints about fields nobody has
reached yet are noise. A command line arrives FINISHED, where the opposite is true.
validate_rangeskeeps its contract, so no window caller moved.Suggest the nearest preset when one is mistyped
An unknown
--presetanswered with all seventeen canonical ids. The tool already offers a nearestmatch for a mistyped config key and a mistyped scenario key, so preset names were the one closed
vocabulary left without it.
The suggestion is drawn from what a person can TYPE - the translated names as well as the ids.
Somebody reaching for "modem56k" writes that, not "presets.modem56k", and a search over ids alone
finds nothing close and stays silent exactly when it is needed.
The size ratchet gets the knob that sees creep
The ratchet capped the largest file and the largest function, so a hundred files could climb to
one line under the ceiling and it would not notice. It now also caps how many files and functions
sit in the top band, and every failure message states the remaining headroom, so the number in the
message is the one you would have to justify raising. A third test keeps the crowd counts honest:
it fails if they are set so far above reality that they could never fire.
Verification
python -m pytest tests- full suite, green (run as the last action, after the prose edits).python smoke_gui.py- green.python tools/check_public_text.py --commits origin/master..HEAD- 5 blocks, clean.Three mutations SURVIVED on the first attempt and each one found a real defect in the test rather
than in the product - a fake missing
winfo_toplevel, a fallback the fake never reached, and afirst regex narrow enough to walk around. All three are fixed and now fail as intended.
🤖 Generated with Claude Code