We ran 100,000 random card numbers through the Luhn check. 10% passed.
PromptScrub ships with eight rule groups. Seven are on by default. The eighth, credit card detection, is off, and people ask why a redaction tool would leave a rule for card numbers switched off.
The short answer is a number we measured: 10.17%.
What the Luhn check actually proves
Nearly every payment card number carries a checksum, calculated with an algorithm published by Hans Peter Luhn at IBM in 1954. Double every second digit from the right, subtract 9 from any result above 9, sum everything, and a valid card number comes out divisible by 10.
It is a good check for what it was designed for: catching a digit typed wrong at a checkout. It was never designed to answer "is this string a card number", and that is the question a redaction tool has to answer.
The measurement
We generated 100,000 random 16-digit strings and ran each through the same luhnOk() function the tool uses, with no other filtering:
随机 16 位数字串 100000 个, Luhn 通过: 10167 (10.17%)
That result is not surprising once you look at the algorithm. The checksum has ten possible outcomes and only one of them means "valid", so roughly one in ten arbitrary digit runs passes by coincidence. Luhn removes 90% of noise. It does not remove the last 10%.
Ten percent is small until you consider what it is ten percent of. A support transcript, an order confirmation, a shipping notification, a log file full of transaction IDs: these documents are dense with long digit runs. Flag one in ten of them as a credit card and the tool starts crying wolf, which is worse than useless. A person who learns to dismiss the warnings will dismiss the real one too.
So we tested real formats instead
Random strings are a worst case. The more useful question is whether the identifiers people actually paste get flagged. We ran seven real-world formats through the rule set, both with the default rules and with card detection switched on:
| Format | Example | Default rules | Card rule on |
|---|---|---|---|
| Amazon order number | 113-7782991-4406620 | no hit | no hit |
| UPS tracking | 1Z999AA10123456784 | no hit | no hit |
| FedEx tracking | 7712 3456 7890 | no hit | no hit |
| ISBN-13 | 9780306406157 | no hit | no hit |
| IMEI | 490154203237518 | no hit | flagged |
| Invoice number | INV-2024-0098431 | no hit | no hit |
| Unix timestamp (ms) | 1721800000000 | no hit | no hit |
Six of the seven survive even with the card rule enabled, because the rule requires either a 4-4-4-x grouping with one consistent separator, or an unbroken run of 13 to 19 digits, and then a passing Luhn check on top.
The IMEI is the instructive failure. IMEI numbers carry a Luhn checksum too, by design, and this one is 15 digits: inside the card-length window, and a valid checksum. There is no way to distinguish it from a card number by shape alone. Every phone in a device inventory is a false positive waiting to happen.
What this means for you
If you are pasting payment data, turn the rule on. It will catch what it is meant to catch.
If you are pasting a support transcript, a shipping log, or a device inventory, leave it off and rely on the seven default rules. You will not lose much: card numbers are rarer in ordinary business text than the other seven categories, and a card number that appears alongside an email address will still be surrounded by redacted context.
The general principle matters more than this one rule. Any tool that promises to find sensitive data by pattern alone is making a trade between what it misses and what it wrongly flags. A tool that hides that trade from you has made the decision on your behalf. Every rule here is a visible switch, and this article is the reasoning behind the one that ships off.
Check the arithmetic yourself
The Luhn implementation is fifteen lines and is served with the page. Open /js/scrub/rules.js, find luhnOk, and run the same experiment. If your number comes out different from ours, we would like to know.