Measured Behaviour

Does obfuscation break garnishment and exempt funds?

A hold says money in the account is not yet the customer's to spend. An exemption says money in the account is not the creditor's to take. Neither is a wrong number, which is why neither is caught by checking the numbers. We measured the second one.

A balance that is exact and a claim that is gone

The fixture is one consumer deposit account and one writ. Six credits over sixty-five days: three Social Security retirement payments of $1,842.00, one veterans' compensation payment of $620.00, $1,150.00 of wages and a $300.00 transfer the customer made from savings. Balance $7,596.00. A judgment creditor serves a writ on day 305 demanding $6,500.00.

The bank's configuration is the ordinary one: a list of ACH company codes that federal law protects, a two-month lookback window to trace them over, a $1,000.00 state floor protected on top, and a $100.00 garnishment fee.

The correct run traces $4,304.00 of benefit deposits inside the window, adds the $1,000.00 floor, and lets the creditor reach $2,192.00 of a $6,500.00 demand. The customer keeps $5,304.00 - which is the point of the exemption, and which is most of what is in the account.

All five default protection profiles reproduced that exactly. As in every area we measure, the damage needs a member pattern wide enough to reach the configuration or the record shapes; the mechanics are in Protect Members.

Three routes, one account emptied

Renaming the protected-source list drops the traced benefits to $0.00. Protected falls from $5,304.00 to the $1,000.00 floor, the sweep rises from $2,192.00 to $6,496.00, and the debtor is left with $1,000.00.

Renaming the lookback window produces the same money to the cent by a different mechanism: the sources are still listed, they are simply never looked for, because the window collapses to nothing. Rule 28's two sides landing byte-identical again.

Renaming the flag that says whether to honour exemptions at all is the worst of the three. Protected falls to $0.00, the sweep reaches the full $6,500.00 demanded, and $996.00 is left in the account after the fee.

The telemetry ranking is not the one you would guess. The lookback arm leaves the configuration screen reading sources=SSA,SSI,VA,RRB,OPM - the protected-source list present, correct, and reported in force, over a window that examines nothing. A populated configuration screen is evidence that entries exist, never that anything reads them.

The check passes, and it is telling the truth

The rule the bank supplies is the one anybody would write: no more may be swept than the writ demands. It passes in every arm.

It has to. A sweep is capped at the demand by construction, so the assertion is a tautology over any exemption policy whatsoever. In the broken arm it prints: $6,496.00 swept of $6,500.00 demanded, $1,000.00 protected, $1,000.00 left to the debtor. Every figure is true. A larger sweep is not a bigger sweep than the court ordered - it is the same order reaching further into the account.

The two remaining option arms are the price and the floor. Renaming the fee drops it from $100.00 to $0.00 and lets the sweep rise to $2,292.00; renaming the state floor drops protection to the traced benefits alone and the sweep rises to $3,192.00. Neither is a disaster. Both are the ordinary way a config-shaped number moves when the name carrying it does.

Redundancy that was never redundant

We ran the source list and the lookback window together, expecting the union to be worse than either half - two separate controls both scoping the exemption ought to be defence in depth.

It is not. The union is byte-identical on the money to either half alone, because each half is already sufficient to stop the tracing. Two controls that both have to work are not two chances to be right; they are two ways to be wrong, and one pattern reaches both because they live on the same object in the same file.

The rule-21 pairing behaves as it has in every area: the source list plus the flag that enables the exemption check is byte-identical to the source list alone, with the check line reading unchecked. The guard had nothing to catch, so switching it off cost nothing.

The fixture that is identical to the failure

We ran the same writ against a second account: same balance, same six postings, same amounts, same days - and every credit coded as payroll, because nothing in it is exempt.

That control reports balance $7,596.00, protected $1,000.00, benefit-protected $0.00, swept $6,496.00, remaining $1,000.00. It is byte-identical to the arm in which the protected-source list was renamed. Not similar - identical.

Constructed test data has one kind of deposit in it. A fixture built to exercise a levy calculation is funded by a round number arriving from a plausible employer, because the author is testing the arithmetic. So the fixture most teams already have cannot distinguish a correct run from this failure, and a green suite says nothing about the class.

The assertion that does work is one about the trace rather than the total: name the deposits that were protected and the days they posted, and require that an account carrying a benefit ACH code produces a non-empty exempt-items list. That fails immediately in every broken arm here. It is worth having whatever your build does, because a renamed column in a core-banking feed empties the same list. Teams that need to show this as a control usually end up documenting it alongside their other financial reporting evidence; our notes on ISO 27001, SOC 2 and front-end JavaScript cover the shape auditors generally ask for.

Frequently asked questions

Did the account balance change?

No. The balance was $7,596.00 in the correct run and $7,596.00 in every broken arm. Every posting was genuine, correctly dated and correctly counted. What moved was which of those dollars a creditor was allowed to reach.

Which setting caused it?

Member renaming reaching the protected-source list, the lookback window, or the flag that honours exemptions. Any one of the three is sufficient. All five default profiles we measured produced identical behaviour with no options set.

Is the two-month lookback really that fragile?

The window is one number in one options object, and losing it produced the same money as losing the entire protected-source list. The difference is only in what the configuration screen shows afterwards, and the window arm is the one that still shows the list.

Would the amount-swept check have caught it?

No, and it cannot. The check asserts that no more was swept than the writ demanded, and a sweep is capped at the demand by construction. It passes for any exemption policy, including none at all.

What about an allowlist instead of a denylist?

The list here is already an allowlist of protected sources, and it still fails open - because a list the code cannot read is indistinguishable from a list you never supplied. The fail-closed reputation of an allowlist is earned against an unknown entry, not against an unread option.

What assertion would have caught it?

Naming the protected deposits rather than counting the total: require a non-empty exempt-items list, with posting identifiers and days, whenever the account carries a benefit ACH code in the window. That fails in every broken arm here.

How do I scope the rename safely?

Keep MemberRegexp off objects handed to an installed library, and restrict it to names your code owns on both sides of each boundary. The option and its examples are documented in Protect Members.

Related reading