PII Shield v2.2.5 - Scanner Redaction and JSON Validity


pii-shield v2.2.5 was published on 27 September 2026 at 21:06:48 UTC. The change that shows up first in an ETL load is compact JSON: a sensitive key is still masked, and the line stays valid JSON. Card checks, long base64 blobs, URL passwords, and the Python SDK floor moved in the same tag.

The full release notes and downloads are on the GitHub release page.

A masked line that no longer parses fails the next load, even when the secret is gone. Compact JSON stays valid after a sensitive key is masked (af521832990c8cc347045e09ccd4b795fa95a104, #217). A JSON string that holds escaped JSON is scanned in decoded form, so the outer line stays valid JSON and the inner secrets are hidden (1b60556e06789719332a4768f0c77debd15fb83f, #220). Scanning that redacted output again leaves it unchanged (9f058026016bbbcd8a47525f05fbda1d112d009a, #219). A retry can pass the same line through the cleaner twice and keep the bytes stable.

A name pair or a setting pair marks the next value as sensitive, in JSON and in logfmt. Only the value pair spends the mark (ed0c4d99a02a573f8dab3a62b2b6f2ad81e17bd2, #221).

Quoted values were slipping past the same tokenizer. A quoted multi word value after key= is tokenized again (3c6a8cd3704dfd895005fbe65900e87ae41aa163, #204, labeled B15). A query parameter value stops at whitespace and at the closing quote (1be0960ae6c03b1cb22dc686e7ba104d2a17e6ea, #213, labeled B17). The password in user:password@ is hidden, including one that contains /, and markers inside quoted URLs are not hashed a second time (e09e6c738c14d886e4e2544477377500a96d1b7d, #241). Request dumps split on a literal \r\n, including a b prefix, are scanned, and Telegram bot tokens in URL paths are found (ab964aecc1da33667907c83731552c35a71239c6, #236).

A Luhn match is not called a card until it also has an issuer prefix and a length check (7bb164632dab08a1eb2a4c9652a81d48249620cb, #209, labeled F7). Digit runs that fail either extra check stay in the text. A real card number that lacks the issuer prefix stays in the text too. The notes do not list the prefixes or the length window.

Long padded base64 blobs are redacted at the default confidence (f6296b4f435d92d339f98c4f3dd4550db3bec9a3, #205, labeled F3). The notes do not define the length cutoff. A blob that is only long and padded now matches with confidence left at the default.

The custom regex length gate is fixed (1e174ae9cd85396ea70a01af91c900334905824e, #206). The scanner score key half is fixed (2c264927183b5c0e6c25663bc469cdf16d9f2c0f, #215). Neither subject states the old boundary. Both are repairs to existing scoring.

A compound of words is scored by its parts, so model ids, setting keys, and User-Agent components are not hidden (3579d5d52b8cd3ea0d4e276fe51b3faaa7779ba5, #239). An abbreviated hash under a commit key stays visible, while a secret after the words password is is hidden (f8242d1d9f4ed5c2d869bddb4df369cb01e25be1, #222). Shannon entropy over ASCII bytes uses a 128 entry table instead of a 256 entry table (d66ec2bd62644f2aff692fdcc80a5ec7bf6c4b39, #240). No timing figure is attached.

When the caller passes no salt, the SDK draws a random one, matching the CLI (493656071ce9dd09eba45796692a5ab75691fbd1, #234). Two embeds that both omit the salt will not share tags. The limitations note (d9d08e16daceb6716c2f614bee13178b6c3ed0ca, #227) says salt rotation breaks tag correlation, and it describes how to trace an identifier under retired salts. Keep a retired salt if older tags still have to join.

The Python SDK stops the wasmtime Mach-port trap handler from killing Apple’s /usr/bin/python3 (a1efef0e4bd785d2d1252d7a95128aa038ce385e, #237). The same commit requires Python 3.9 and wasmtime 45. A host on the Apple system interpreter, or on an older wasmtime, will fail to load this build.

Operator grpc moves to v1.83.1 for GO-2026-6348 (d71e66b5639b9e47c2e1aa77b143c48b3da11c39, #208). The notes name the identifier and the module version. They do not describe the bug. The operator-go-dependencies group bump beside it (aed8c08f61a24857e8bf5b62ea85d9d168bf051b, #210) is dependency maintenance.

The fetch-traffic job now fails on an API error instead of committing that error (18eaca1b4bc708f4463cddb4121ea7e22a24fbed, #230). The daily run also fetches Search Console data (731a402e665d2e55bd12bc0611ce33e7adbed7a9, #233). The operator envtest suite runs in CI and under make test, and it pins objects the real API server adds (84078d65965a1895244dda51a40533dfb4ea22a7, #225). The github-actions group takes 9 updates (3dd345a41c42c2a66eb922fa8384f8e33f82b9be, #211). That bump does not change scanner output.

The remaining commits are tests. The cleaner asserts that an overflow never writes the raw line (64e2b2ada0c6e6a5f34fdf41b9da522bad07fbc6, #226). Scanner tests require the secret to be gone, not only that a marker appeared (946fa68873890fba50035b0b8ae16bcbf239d616, #223). Smoke tests freeze the corpus and fail when output is lost or shifted (#218, #207). FIFO retries, the metrics sidecar port, config restoration, and the WASM kernel mapping sit in the same harness set. They do not add a detector.

Compare a sample of redacted logs before the tag reaches a production stream.

Card shaped digit runs without an issuer prefix and a length check remain visible (#209). Long padded base64 is masked at the default confidence (#205). Model ids, setting keys, User-Agent components, and abbreviated commit hashes stay visible (#239, #222). Compact JSON should still parse, and a second scan should not alter redacted text (#217, #219).

Pass an explicit salt when tags must match across processes (#234). Salt rotation breaks correlation with older tags (#227). Python callers need Python 3.9 and wasmtime 45 (#237). Operator rollouts should include grpc v1.83.1 (#208).