Problem: an egress privacy filter scans outgoing JSON for filesystem-path-like strings using regular expressions applied to the serialized payload. Escaping defeats this. A backslash separator is doubled by JSON string encoding, and can also arrive as a unicode escape, so a pattern authored against ordinary decoded text does not match the wire bytes. Percent encoding, double encoded JSON carried inside a string value, and base64 blobs create the same layer mismatch.
Known evidence from reasoning, not from executed tests: the defect is a layer error rather than a weak pattern, because the scanner inspects a different representation than the one a human reads. Deny listing concrete shapes is unbounded, so a schema driven positive allowlist per field, applied to decoded values after bounded canonical decoding, appears more sound. Structural signals such as a drive letter prefix, a doubled separator network prefix, a leading separator, a home shorthand, separator density and segment counts generalize better than literal patterns. The validated value must be the exact value later emitted, so canonical serialization after validation matters.
Second question: how to record rejections usefully without retaining the rejected content. Candidate approach is to log only a field location derived from the schema, a rule identifier, a coarse length bucket, a character class summary, and a keyed hash under a rotating secret rather than a plain digest, since low entropy strings are brute forceable.
Unknowns: how deep recursive decoding should go before a payload is simply refused, whether normalization itself introduces false negatives, and how to test encodings without ever printing a candidate value.
Changed question. The filter is now an availability surface as well as a privacy control. Reasoned design, not measured; no tests executed.
One. Route every cost through a single budget ledger with one spend entry point, rather than scattering separate cap checks through each layer. Three benefits: every spend site is enumerable for audit, the identity of the budget that aborted first falls out for free, and consumed units become an observable a test can assert on. Scattered checks tend to leave one path unmetered, and the unmetered path is the whole vulnerability.
Two. Byte count and node count must both be capped because neither bounds the other. Deeply nested empty containers cost about two bytes per level, so a generous byte cap still admits enormous node counts. One very large string is the opposite shape, few nodes and many bytes. A cap on decoded length per string also needs an aggregate companion, since many medium strings evade a per item limit.
Three. New failure mode identified beyond the earlier list: warning cardinality. If a rejection record is keyed by anything attacker controlled, such as an unexpected object key name, the metrics backend suffers a cardinality explosion even though no value was echoed. The fix is a fixed size counter vector indexed by a compile time enumerated category, so cardinality is statically bounded and no data derived string can become a label. Never echoing the value is necessary but not sufficient.
Four. Separate two things that both get called failing closed. The verdict defaults to deny and only a completed clean walk assigns allow. Independently, the filter must be total with respect to its caller: a catch all at the boundary maps any escape, including an unexpected runtime error, to deny rather than propagating into the primary task. Sharing is optional and the developer work is not, so an abort suppresses sharing while the primary task proceeds untouched. This also means the primary task must not block on the filter past its deadline, which is the one place a wall clock backstop earns its keep even though counted units remain the real gate.
Five. Parser hardening details that are easy to omit. Cap numeric literal length, because arbitrary precision parsers do superlinear work on long exponents. Cap keys per object, because building a map from attacker chosen keys invites hash collision degradation. Reject duplicate keys outright rather than resolving last wins, since a differential between the validating parse and any later parse is an evasion.
Six. Budget reachability is testable and worth testing. For each enumerated budget there should exist an input that trips that budget first. A budget that no input can trip first is dominated by another cap, is dead code, and offers false assurance in review. Also assert determinism directly: the same input yields the same verdict and the same consumed unit count across runs, which is exactly the property a wall clock gate destroys.
Unknowns unchanged and unresolved by reasoning: a good default for the normalization expansion ratio cap, whether naming the first budget that fired discloses useful structure to someone probing the filter, and whether isolating the matcher in a separate process is worth its cost when the engine cannot be replaced.