PII Sanitizer
Everything runs in this tab
The text you paste, the findings, and the mapping between them never leave your browser — there is no upload and no server side to this tool. It is a way to avoid pasting a customer's card number into a public issue; it is not a compliance product, and it cannot find what has no pattern, such as names and street addresses.
Drop a file here, or
Or drop a file — it is read in this tab and never uploaded.
Samples
Each distinct value gets a numbered token, reused everywhere it appears — so the document still reads as a story.
One per line: a literal value, a domain (which also spares its subdomains), or a CIDR range. Seeded with the reserved and documentation ranges, the RFC 2606 example domains and the published test card numbers — without them every compose file loses 127.0.0.1 and every fixture loses its test card.
How PII Sanitizer works
The distinction that makes this tool usable rather than destructive is between a pattern and a detector. Sixteen digits in a row is a pattern; a payment card is sixteen digits that satisfy the Luhn checksum, where every second digit counting from the right is doubled, nine is subtracted from any doubled result above nine, and the total is divisible by ten. That single arithmetic step rejects roughly nine out of ten arbitrary digit runs, which is the difference between flagging a card and flagging every order number, request id and byte count in the file. The same principle applies everywhere there is structure to check: an address is not four numbers separated by dots, it is four numbers none of which exceeds 255.
Some classes are defined by administrative history rather than arithmetic. A US Social Security number has never been issued with an area of 000, 666, or anything from 900 upward, nor with a middle group of 00, nor with a final serial of 0000 — those combinations were reserved or set aside for other identifier programmes. Checking them removes most of what a nine-digit pattern would otherwise sweep up, and one further rule removes most of the rest: nine bare digits with no nearby cue are not treated as a Social Security number at all, because in an operational file they are overwhelmingly likely to be something else entirely.
Most secrets, though, have no shape whatsoever. There is nothing about the string `hunter2` that marks it as a password, and nothing about `abc123` that marks it as a key. What identifies them is the name of the thing they are assigned to. So one detector keys off the identifier rather than the value, recognising the assignment forms configuration is written in — colon-separated, equals-separated, and quoted inside JSON — wherever the name contains a word such as password, secret, token or credential. In practice that one rule catches more genuine leaks than all the shape-based classes put together, because it is how secrets are actually written down.
A fixed list of classes is nevertheless insufficient by construction, and not because the list is too short. Every organisation has identifiers that exist nowhere else: a badge number in a format someone chose in 2011, a customer reference with a checksum nobody documented, a naming convention for internal hosts. No shipped detector can know them, which is why patterns you supply yourself are treated as first-class citizens here rather than as an escape hatch. A rule you write becomes a detector like any other — it competes for contested spans on the same terms, it produces the same numbered tokens, and it appears in the same report — and where it overlaps a built-in, yours takes precedence, on the grounds that you wrote it precisely because the built-in was doing the wrong thing.
Overlap is not a rare corner case, and resolving it is a required pass rather than a refinement. A token in an authorization header is simultaneously a credential and a JSON Web Token; a database URL contains an address, a username and a password inside one string; a phone number written with dashes contains a tail that resembles other things. Detectors run independently and propose candidate spans, then a single sweep sorts them and keeps one winner per region of text: longest first, then by which detector is more informative about what was found. Without that step the replacements are computed against offsets that earlier replacements have already invalidated, and the output is quietly corrupted while the report beside it still looks correct.
Reference
- Luhn: double every 2nd digit from the right; subtract 9 if the result exceeds 9; the sum must be ≡ 0 (mod 10)
- Issuer ranges: 4 Visa · 51–55 and 2221–2720 Mastercard · 34/37 Amex · 6011/65 Discover · 35 JCB
- SSN rejected when area ∈ {000, 666, 900–999}, or group = 00, or serial = 0000
- Reserved for documentation: 192.0.2.0/24 · 198.51.100.0/24 · 203.0.113.0/24 (RFC 5737) · 2001:db8::/32 (RFC 3849)
- Reserved for examples: example.com · example.org · example.net · .test · .invalid (RFC 2606)
- Keyed digest: HMAC-SHA-256(key, value), first 6 hex characters
How to use this tool
Paste the text, or drop the file
A log excerpt, a stack trace, an API response, a configuration file. Nothing is uploaded and nothing is stored — the scan runs in the page you are looking at, which is the only reason it is reasonable to paste something sensitive into it.
Choose how values are replaced
Numbered tokens keep the document readable by giving each distinct value a stable label. Flat labels are the safest. Keyed digests stay consistent between separate files. Partial reveals keep a fragment visible and are the only option that can be partially undone.
Read the per-class counts before you read the output
Each class shows how many times it fired. A count that looks wrong for the file in front of you — several phone numbers in something that contains none — is a false positive, and finding it here costs nothing compared with finding it after the text has been sent.
Add anything of your own that was missed
Write a pattern for the identifiers only your organisation uses, give it a label and a token, and it joins the built-in set on equal footing. Rules can be exported as JSON so a team agrees on one set rather than each person maintaining their own.
Add exceptions for what should have been left alone
Values that must survive go in the exception list, one per line, as a literal, a domain, or an address range. It arrives seeded with the ranges and domains that specifications set aside for documentation, plus the card numbers payment providers publish for testing.
Worked examples
A checksum separating a card from an order number
- Given
- 4929 7226 5379 7141 alongside the identifier 1234 5678 9012 3456
- Result
- The first is flagged as a Visa card; the second is not flagged at all
Both are sixteen digits in four groups. Only one satisfies Luhn. Reverse the last two digits of a real card and it stops being reported, which is exactly the behaviour that keeps invoice numbers intact.
A header where two classes claim the same characters
- Given
- authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiJ9.qX8_Zt
- Result
- The credential is replaced and reported as a JSON Web Token; the word Bearer survives
The scheme word is deliberately outside the replaced span. Removing it would leave a line nobody can identify, and the more specific reading — a JWT rather than an unspecified credential — is the one worth telling you about.
A secret with no shape at all
- Given
- A configuration fragment containing DB_PASSWORD=Hs8!kQ2wPz
- Result
- The value is replaced; the variable name is untouched
Nothing about that string is recognisable as a password. It is found entirely through the name it is assigned to, which is why this detector finds more real exposures than the shape-based ones.
A configuration file that must come through unharmed
- Given
- A compose file publishing 127.0.0.1:5432 and referencing api:1.24.3-alpine
- Result
- Nothing is replaced
The loopback address is a genuine address and is genuinely detected — the exception list is what spares it. Semantic versions and image tags never reach that stage, because the surrounding guards reject a fifth dotted group.
The same value across two files a week apart
- Given
- Keyed digests under one shared key, applied to two separate exports
- Result
- An identical address produces an identical label in both
This is what makes two incidents correlatable without either file naming anybody. Change the key and every label changes, which is also how you make two sets deliberately uncorrelatable.
When to use it
- Attaching a production log to a public bug report or a vendor support ticket without handing over customer records along with it.
- Preparing a realistic sample to paste into a chat with a colleague, a forum, or a language model, where the text will be retained somewhere you do not control.
- Cleaning a configuration file or an environment fragment before it goes into a screenshot, a slide, a runbook, or documentation.
- Producing a reproducible extract for a debugging session where the shape of the data matters but the values do not.
- Correlating two incident exports under a shared key, so the same account can be followed across both without either file identifying it.
- Checking, before anything is shared at all, whether a file contains credentials nobody realised were in it.
Things to watch out for
- This is not a compliance tool and makes no claim under any regulatory framework. It reduces the chance of pasting a customer record into a public issue; it does not certify a file as safe, and it should not be the only thing between sensitive data and somewhere public.
- Replacing a value with a consistent label is pseudonymisation, not anonymisation, and European data-protection law treats pseudonymised data as still being personal data. Anything that lets you tell two records apart also lets someone else, given other information, tell who they belong to.
- Partial reveals are reversible in practice. Four visible digits plus a timestamp plus a merchant identifies a transaction; an area code plus four digits narrows a phone number to a small set. Use that mode when a person needs to recognise a record, not when the output is leaving your control.
- A digest computed without a key is not protection when the input space is small. Every ten-digit phone number can be enumerated in seconds and matched against its hash, which is why the keyed mode requires a key rather than defaulting to an unkeyed one.
- Classes with no lexical structure are out of scope entirely: personal names, street addresses, dates of birth, and free-text notes. There is no pattern that distinguishes a name from an ordinary word, and a tool claiming otherwise would be guessing.
- The replacement operates on raw text, so a value that was not quoted does not become quoted. Substituting a label for an unquoted number inside JSON produces something that no longer parses as a number, which is visible immediately but worth expecting.
- A pattern you supply is executed by the browser, and a browser cannot interrupt a regular expression once it has started. Compilation is delayed briefly after each keystroke so a half-written pattern is never run, but a pathological expression against a large document can still make the page unresponsive.
Frequently asked questions
Does any of this text reach a server?
No. There is no upload, no request, and no server component. The scanning, the replacement and the table of original values all happen in the page, and closing the tab is what disposes of them. That is the only basis on which pasting sensitive material into a web page is defensible.
What will it miss?
Anything without a recognisable shape or a recognisable name: personal names, addresses, dates of birth, free-text notes, and any internal identifier format it was never told about. Credential formats it does not yet know will also pass through untouched. Treat an empty result as an absence of matches, never as a guarantee of safety.
Is a replaced file anonymous?
No, and the distinction matters legally. Consistent labels are pseudonymisation: two records that shared a value still visibly share one, which is what makes the output useful and also what keeps it linkable. Under European data-protection law pseudonymised data remains personal data with all the same obligations attached.
Why does the keyed mode insist on a key?
Because an unkeyed digest of a small input space offers no protection at all. There are only ten billion possible ten-digit phone numbers, so anyone holding the output can hash every one of them and recover the original by lookup. A secret key makes that impossible without it, and requiring one prevents a mode that exists to avoid the mistake from making it easy.
Will it damage my configuration file?
It is built specifically not to. Every class with a checkable structure is checked rather than merely matched, the reserved documentation ranges and example domains are excluded by default, and each class reports its own count so anything unexpected is visible before you copy. Values that must survive can be listed as exceptions.
Can I undo the replacement?
Only with the table of original values, and only for the numbered-token mode, which is why that table sits behind a deliberate reveal and is never part of the copied or downloaded output. The other modes discard the originals entirely, so nothing on this page can restore them.
Related DevOps tools
All devops tools- Cron Expression Parser Read a cron line in plain English, expand every field, and see the next ten firings in local time and UTC.
- CIDR / Subnet Calculator Turn an IPv4 or IPv6 prefix into netmask, broadcast, usable range and host count — then split it, compare it, or aggregate a range.
- chmod / Unix Permissions Calculator Bind permission checkboxes, octal and symbolic modes together — including setuid, setgid, sticky and what a umask leaves behind.
- Uptime & SLA Calculator Convert an availability target into allowed downtime per day, month and year, track the error budget, and chain several services together.
- Kubernetes Resource Converter Convert CPU and memory quantities between every suffix Kubernetes accepts, and catch the ones that mean a billion times what you meant.
- YAML ↔ JSON Converter Convert either way with key order and comments-free fidelity, then see every scalar whose written form and parsed value disagree.