fix(tests): the at-rest plaintext probe was both flaky and blind — decode before matching - #8
Merged
Merged
Conversation
…code before matching tests/test_store_aad_binding.py asserted `"DOE" not in on_disk` against an mfenc cell. The cell's payload is base64 of a random nonce + AES-GCM ciphertext, so matching a 3-character needle against that TEXT is a coin flip: measured ~0.12% of runs at this payload size, about 1 in 850. It red-X'd PR #7, whose diff touched release.yml and the leak-gate docs and nothing within reach of the store. The flake is the smaller half. The same check is also BLIND to the thing it exists to catch. Handed a cell that base64-encodes the plaintext verbatim, `"DOE" not in on_disk` evaluates TRUE -- "no plaintext here" -- because base64 of "DOE^JANE" contains no literal "DOE". So it fired at random on safe data and would have passed a real PHI-at-rest leak. Demonstrated both directions in the commit's test run. And the obvious fix is a trap. Reaching for a longer, more distinctive needle -- "DOE^JANE" -- makes it VACUOUS: '^' is not in the base64 alphabet, so the assertion could never match, would pass unconditionally, and would look stronger than what it replaced. So: decode first, then match the FULL plaintext against the at-rest BYTES. A false positive now needs a collision across the whole message rather than three characters, and a cell carrying its plaintext fails loudly. `test_the_plaintext_probe_can_actually_fail` pins the non-vacuity directly: it feeds the probe a plaintext-bearing cell and asserts the probe says so. Without it the round-trip test would pass no matter what the store wrote at rest -- which is exactly the state this repo keeps rediscovering. NOT changed, because not every short needle is wrong: * tests/test_support_bundle.py:81 (`"2575" not in blob`) matches PLAINTEXT json.dumps output. It is a deterministic redaction check and a short needle is exactly right there. * tests/test_uploads.py:52 (`"MRN123" not in blob`) is the same ciphertext shape as this one, but a 6-character needle is ~400,000x safer (~3e-7 %). Left alone rather than churn it; the pattern to avoid is a SHORT needle against ciphertext, not the length itself.
wshallwshall
enabled auto-merge (squash)
July 27, 2026 14:53
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.
tests/test_store_aad_binding.pyasserted"DOE" not in on_diskagainst anmfenccell. The payload is base64 of a random nonce + AES-GCM ciphertext, so matching a 3-character needle against that text is a coin flip.It flaked
Measured at this payload size: ~0.12% of runs, about 1 in 850. It red-X'd PR #7, whose diff touched
release.ymland leak-gate docs — nothing within reach of the store.The flake is the smaller half — it was also blind
Handed a cell that base64-encodes the plaintext verbatim, the old check evaluates
True— "no plaintext here" — because base64 ofDOE^JANEcontains no literalDOE.So it fired at random on safe data and would have passed a real PHI-at-rest leak. For an ASVS 11.3.3 at-rest binding test, that's the wrong failure mode in both directions.
And the obvious fix is a trap
Reaching for a longer, more distinctive needle —
"DOE^JANE"— makes it vacuous:^is not in the base64 alphabet, so the assertion could never match, would pass unconditionally, and would look stronger than what it replaced.The fix
Decode first, then match the full plaintext against the at-rest bytes. A false positive now needs a collision across the whole message rather than three characters, and a cell carrying its plaintext fails loudly.
test_the_plaintext_probe_can_actually_failpins the non-vacuity directly — it feeds the probe a plaintext-bearing cell and asserts the probe reports it. Without that, the round-trip test would pass no matter what the store wrote at rest.Not changed — not every short needle is wrong
test_support_bundle.py:81—"2575" not in blobblobisjson.dumps(summary), plaintext. A deterministic redaction check; a short needle is exactly right. Left alone.test_uploads.py:52—"MRN123" not in blobThe pattern to avoid is a short needle against ciphertext, not needle length itself.
🤖 Generated with Claude Code