Cybersecurity-Projects/PROJECTS/beginner
CarterPerez-dev 8d7b114fb9 fix(rube): clear the entire S2 backlog tier - a non-answer is never an answer
B17 through B30, fourteen items. Every one reproduced before it was touched
and mutation-proven after. 194 tests from 119, rubocop 903 offenses to 0,
all six gate stages green.

One rule runs through all of it: nothing may present a guess, a default, or
a swallowed error as a verdict.

Decision states (B19). Under POLICY_OBSERVE_AND_LOG a payload carrying a live
Gem::Requirement#marshal_load snapshot reported accepted? true AND rejected?
false, so both obvious caller shapes loaded it. The predicate pair could not
express the third outcome, so there was no safe branch to pick - the prior
note claiming target/app.rb sidestepped this by branching on rejected? was
wrong, and both forms were byte-for-byte equivalent in outcome. accepted?,
rejected? and would_reject? are removed rather than redefined, so copying
`if d.accepted?` now raises NoMethodError instead of silently changing
meaning. One state validated in the constructor, three exclusive predicates,
and proceed? is the only one that may gate a Marshal.load. Observe-and-log
stays non-blocking and still hands back its snapshot; the monitoring caller
writes `proceed? || observed?` and names the state out loud.

Scanner error accounting (B18). Five rescues returned nil, [] or false and
told nobody. They now record site, subject and error class, and Report
exposes suppressed_count, suppressions_by_site, complete? and
candidates_lost?. Wiring the counter immediately surfaced 3 suppressions on
a stock image that had always been invisible: <internal:symbol>,
<internal:pathname_builtin> and <internal:ractor> all fail Prism.parse_file
with ENOENT because Ruby hands out those paths but they are not files.

The fourth state (B29). Those 3 suppressions were also 7 wrong answers -
candidates scored "does not touch state", indistinguishable from analysed
and inert, and 4 zero-arity ungated ones silently dropped from reachable.
touches_state is now four-state. A source that was given and could not be
parsed fails OPEN and stays reachable, because a scanner that discards what
it failed to analyse is the exact failure mode B18 names. A C-defined method
with no Ruby source at all is reported as unanalysable instead: 132 of 173
candidates, and failing open there would take reachable from 25 to 74 of 165
ungated and stop the filter filtering. Report#unanalysable and
#fully_analysed? state the real coverage - 33 of 173 - rather than implying
the filter saw everything. The 5 recovered candidates were verified by
executing them, not by reading source we could not read; one of them,
ERB::Compiler::PercentLine#to_s, is an alias of an attr_reader, which is a
second and distinct analysis gap.

Reason escaping (B20). Reject reasons interpolated raw attacker bytes into a
caller-supplied reporter. A class name carrying CR, LF, ESC and NUL turned
one reporter call into three log lines, the middle one forging a successful
authentication. All three interpolation sites now truncate at 96 bytes and
inspect the binary form, so no byte below 0x20 survives and the value is
quote-delimited.

Target hardening (B21, B22). The defended endpoint returned HTTP 500 with a
source line for three roots the detector had just accepted, leaking paths the
same way the B3 mutant did. show_exceptions is off, the shape is checked, and
the gate now greps every error body for source paths. ALLOWED_CLASSES could
never match anything, measured: a benign session cookie carries zero class
names. It is PERMITTED_CLASS_NAMES = [] now, which is what the app actually
requires and is strictly tighter - the old list admitted a C-wrapped String,
and under a mutant restoring it only the new shape check stopped that payload.

Gated agreement (B23). GATED_SINK_TAGS omitted TAG_DATA while the scanner
listed _load_data. The scanner was right, and this is now execution evidence
rather than a reading of marshal.c: a hand-built d stream naming Thread::Mutex,
a real C-level T_DATA, raises TypeError naming the missing _load_data, while
the same stream naming String dies earlier at "dump format error" - which is
why the previous attempt could not see it. A test compares both definitions
directly so they cannot drift again.

Float fidelity (B17). read_float returned nil for seven body forms
Marshal.load accepts, two more than the finding listed. Ruby uses its own
ruby_strtod, so "INF" is 0.0 while "inf" is Infinity, and String#to_f turns
out to be that same function. Ruby's legacy binary mantissa is NOT decoded:
a model fitted to four oracle points passed a 25-case table and then failed
930 of 5000 randomised cases, and marshal.c is not available in these images.
Since 0 of 209 Marshal.dump outputs contain a NUL, no living Ruby emits that
form, so the parser records the strtod prefix and flags Node#undecoded_tail
instead. A plausible wrong number is worse than a labelled non-answer.
Final differential: 2919 agreed exactly, 2081 flagged, 0 claimed-and-wrong.

Hygiene (B24, B25, B27, B28). PRIMITIVE_CLASS_NAMES and NAMESPACE_SEPARATOR
had one reference each, their own definition. BIGNUM_SIGN_POSITIVE is live
and stays. The width > FIXNUM_MAX_WIDTH guard is unreachable for all 256
possible marker bytes, checked exhaustively, and raised the wrong error class;
it is replaced by a test that derives widths from real Marshal.dump output.
The symlink and object-link bounds checks no longer lean on negative-index
wraparound. The parse graph is sealed before it is returned - every node, its
collections and its scalars frozen - and the whole suite stayed green first
try, which proves nothing downstream was mutating it. exploit-gate.sh pins
both sides now, 4.0.2-slim erb 6.0.1 FIRED against 4.0.6-slim erb 6.0.1.1
BLOCKED; the finding's claim about detector-gate.sh was wrong, it never had
a patched side.

Lint (B30). just lint used a --network none runner, so gem install could
never reach RubyGems, the && short-circuited, and the recipe exited 2 while
printing absolutely nothing. That is the fourth instance of a dropped return
value hiding a failure in this project. It is loud now. The config had never
been validated against a real run: 903 offenses, dominated by a quote style
the codebase does not use. frozen_string_literal was verified safe by running
the whole suite under RUBYOPT=--enable-frozen-string-literal BEFORE the change,
so ~247 offenses were retired by fixing code rather than silencing a cop.
Every remaining disabled cop carries a reason.
2026-07-29 14:35:48 -04:00
..
base64-tool Update DEMO.md 2026-04-28 18:01:21 -04:00
c2-beacon chore(deps): bump starlette in /PROJECTS/beginner/c2-beacon/backend 2026-06-04 17:25:29 +00:00
caesar-cipher chore: add demos for projects, update haskell-reverse-proxy modules, refresh siem assets 2026-04-26 23:12:48 -04:00
canary-token-generator style(canary): format config_test.go with golines (max-len 80) 2026-07-19 03:18:17 -04:00
deserialization-gadget-lab fix(rube): clear the entire S2 backlog tier - a non-answer is never an answer 2026-07-29 14:35:48 -04:00
dns-lookup chore: add demos for projects, update haskell-reverse-proxy modules, refresh siem assets 2026-04-26 23:12:48 -04:00
firewall-rule-engine feat: sbom generator & vulnerability matcher + docstrings across 6 projects 2026-04-08 23:53:40 -04:00
hash-cracker cracked 2026-05-23 05:01:01 -04:00
keylogger Update README.md 2026-06-02 09:16:11 -04:00
linux-cis-hardening-auditor cracked 2026-05-23 05:01:01 -04:00
linux-ebpf-security-tracer add: AGPL 3.0 license files to hash-cracker, ebpf tracer, dlp scanner 2026-04-11 05:56:43 -04:00
metadata-scrubber-tool chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool 2026-06-18 15:28:54 +00:00
network-traffic-analyzer ci(network-traffic-analyzer): drop ruff preview to keep lint green across versions 2026-07-19 03:18:03 -04:00
simple-port-scanner cracked 2026-05-23 05:01:01 -04:00
simple-vulnerability-scanner Delete PROJECTS/beginner/simple-vulnerability-scanner/hf_readme.gif 2026-07-19 23:26:24 -04:00
steganography-multi-tool docs(crypha): add learn/ track and surface the built project (M9) 2026-07-19 03:06:49 -04:00
systemd-persistence-scanner chore: add demos for projects, update haskell-reverse-proxy modules, refresh siem assets 2026-04-26 23:12:48 -04:00