Skip to content

Fix three masking leaks (read rules, value cap, regex case) - #8

Merged
vfofanov merged 5 commits into
mainfrom
fix/r1-leaks
Oct 5, 2026
Merged

vfofanov merged 5 commits into
mainfrom
fix/r1-leaks

Conversation

@vfofanov

@vfofanov vfofanov commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Fixes the three masking leaks found in the 2.0 review, failing tests first.

  • Read rules no longer write their value in clear: the next rule on the same match or the default policy writes it. ReadX(f).Unmasked() keeps clear text, ReadX(f).MaskAny(...) reads and masks; a read rule runs wherever it stands among the rules.
  • MaxValueBytes applies to values written unmasked only: a masking function receives the whole value (***4444, not the digits at the cap), and mask output — hash, function result, allow-list stars — is never cut.
  • PropMatches.Regex follows PropertyNameCaseInsensitive; a function matcher can take the comparison via a new PropMatchingStrategy(Func<string?, StringComparison, bool>) constructor.

Breaking

Read rules under AllowList/NullList now write "***"/null; a masking function decodes a long value in full. Both listed under 2.0 "Changed — breaking".

Verification
  • LeakTests: 29 cases (K1 under AllowList / BlockList / NullList / Relative / array, read+mask both orders, Explain; K2 three function kinds, segmented input, hash, long mask output; K3 truth table as a Theory). 20 red before the fix.
  • Each fix toggled off once: K1 → 8 red; K2 output cap → 2, input cut → 3 + 1 (segments); K3 regex → 2, function → 1.
  • Full solution suite green on net8 / net9 / net10.

- read rules write in clear under AllowList; read and mask on one match
- MaxValueBytes cuts the value before a mask function and cuts mask output
- PropMatches.Regex and function matchers ignore the case option
- API surface the tests need: chainable read rules, comparison-aware
  function matcher (no behaviour change yet)
A read rule no longer writes its value in clear: it hands the value to
the context and the next rule on the same match, or the default policy,
writes it. Chain Unmasked() for clear text or a mask method to read and
mask one match; read rules run wherever they stand among the rules.
A masking function now receives the whole value, so a Last4-style
function sees the real last digits, and mask output - a hash, a
function's result, the allow-list stars - is never cut by the cap.
PropMatches.Regex matched exact case even under the default
case-insensitive option, so DRiverLicensE escaped a driverLicense rule.
It now also matches case variants when the call ignores case; a function
matcher can take the comparison through a new constructor.
CHANGELOG lists the read-rule and value-cap changes as 2.0 breaking
changes and the regex case fix; README and the masking skill describe
the new read semantics and where MaxValueBytes applies.
@vfofanov
vfofanov marked this pull request as ready for review October 5, 2026 02:16
@vfofanov
vfofanov merged commit 6c077ff into main Oct 5, 2026
1 check passed
@vfofanov
vfofanov deleted the fix/r1-leaks branch October 5, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant