Skip to content

fix(tests): the at-rest plaintext probe was both flaky and blind — decode before matching - #8

Merged
wshallwshall merged 1 commit into
mainfrom
flake-aad-needle
Jul 27, 2026
Merged

fix(tests): the at-rest plaintext probe was both flaky and blind — decode before matching#8
wshallwshall merged 1 commit into
mainfrom
flake-aad-needle

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

tests/test_store_aad_binding.py asserted "DOE" not in on_disk against an mfenc cell. 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.yml and 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 of DOE^JANE contains no literal DOE.

plaintext-bearing cell -> new probe says absent? False   (correct: it IS present)
same cell, old base64-text check:                 True   (MISSED IT)

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_fail pins 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

Site Verdict
test_support_bundle.py:81"2575" not in blob blob is json.dumps(summary), plaintext. A deterministic redaction check; a short needle is exactly right. Left alone.
test_uploads.py:52"MRN123" not in blob Same ciphertext shape, but a 6-char needle is ~400,000× safer (~3×10⁻⁷%). Left alone rather than churn it.

The pattern to avoid is a short needle against ciphertext, not needle length itself.

🤖 Generated with Claude Code

…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
wshallwshall enabled auto-merge (squash) July 27, 2026 14:53
@wshallwshall
wshallwshall merged commit 2bd50f9 into main Jul 27, 2026
31 checks passed
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