Cybersecurity-Projects/PROJECTS/beginner/deserialization-gadget-lab/scripts
CarterPerez-dev bfb0ab36e8 feat(rube): M7 - a version floor is a measurement, not a preference
required_ruby_version claimed ">= 3.3" while every gate stage ran on Ruby 4.0
images only. The claim was false. Marshal.load did not validate the bignum sign
byte until 3.4, so on 3.3 real Ruby accepts "!", "\x00", "\xFF" and "0" in the
sign position and reads them all as positive, where 3.4 and 4.0 raise
ArgumentError. The parser accepts "+" and "-" only, so it models 3.4+, and on
3.3 parser_test.rb goes red at its own liveness guard: the differential oracle
finds nothing rejected and says so instead of passing vacuously.

3.4.10 runs all five suites green at the same counts as 4.0 and prints ALL
CONTROLS PASSED. That makes 3.4 the oldest release actually proven, so the floor
is ">= 3.4". TargetRubyVersion moves with it, since those two must stay equal.

Teaching the parser two Marshal models to keep 3.3 was rejected. It buys a branch
in security maintenance only, and it pays with a second sign-validation path in
the one component whose whole job is modelling Marshal.load correctly.

The untracked rube-0.1.0.gem sitting in the repo root turned out to be built from
pre-B17 source: 12 lib files instead of 13, no float_body.rb, read_float still
using Float() with a bare rescue, no frozen_string_literal lines, declaring
">= 3.3". It installed and required without error, so nothing caught it. Two
artifacts with the same name and version and no way to tell them apart. Deleted.

package-gate.sh therefore asserts every shipped lib file is byte-identical to
the worktree rather than merely present, builds from the declared manifest alone
so an omitted file cannot produce a gem that builds anyway, installs the
artifact on the floor and current images and exercises it from the installed
copy, and re-proves the floor in both directions each run. Three negative
controls: a gem shipping the vulnerable target must be rejected, a gem with a
drifted lib file must be rejected, and RubyGems must refuse to install below the
declared floor. Aimed at the stale artifact it fails 6 of 23; on a fresh build it
passes 23 of 23. Both executed.

just build now writes to tmp/build as the invoking user instead of leaving a
root-owned gem in the tree, and just package audits an artifact you already have.

Full gate: 56 PASS, 0 FAIL across six stages. 194 tests. Lint 0 across 30 files.
2026-07-29 15:04:21 -04:00
..
audit_gem.rb feat(rube): M7 - a version floor is a measurement, not a preference 2026-07-29 15:04:21 -04:00
detector-gate.sh fix(rube): clear the entire S2 backlog tier - a non-answer is never an answer 2026-07-29 14:35:48 -04:00
exploit-gate.sh fix(rube): clear the entire S2 backlog tier - a non-answer is never an answer 2026-07-29 14:35:48 -04:00
package-gate.sh feat(rube): M7 - a version floor is a measurement, not a preference 2026-07-29 15:04:21 -04:00
render_matrix.rb fix(rube): clear the entire S2 backlog tier - a non-answer is never an answer 2026-07-29 14:35:48 -04:00
target-gate.sh fix(rube): clear the entire S2 backlog tier - a non-answer is never an answer 2026-07-29 14:35:48 -04:00
version-matrix.sh fix(rube): clear the entire S1 backlog tier - depth, budgets, gates, fidelity 2026-07-29 05:12:25 -04:00