Heads up: the Gumroad link at the bottom goes to my own paid extension. No affiliate, no tracker, no third-party referral. I'm the developer.
Every Korean developer has done this dance: about to share a screen on Zoom, then panic-scrolls because a 주민등록번호 (RRN, 13-digit national ID) is sitting in a spreadsheet preview, or a phone number is auto-filled in a form.
I got tired of squinting and built a tiny Chrome extension that does the squinting for me. Shipping notes below in case it helps anyone else.
What it catches
-
Korean RRN — 13 digits, validates the checksum from the Resident Registration Act formula (multiplier 2,3,4,5,6,7,8,9,2,3,4,5 + mod 11). No false positives from random
940101-1234567-shaped strings. -
Korean mobile —
010-XXXX-XXXXplus the legacy011/016/017/018/019prefixes. - Email — RFC-ish, but biased toward catching the obvious slip cases (gmail.com, naver.com, etc.).
- 사업자등록번호 — 10-digit business number with the 국세청 (National Tax Service) checksum.
It highlights detected matches inline (no upload, no network call) and surfaces a small badge in the toolbar with a count. Optional Pro toggle auto-masks the DOM nodes — useful when you forgot to clear a tab before opening it on a projector.
Why this isn't a regex one-liner
The naive version is \d{6}-\d{7} and you're done. Three problems:
- False positives. Order numbers, tracking codes, invoice IDs all hit that shape.
- Locale ambiguity. A 13-digit string in a Japanese page is not an RRN, but a single regex doesn't care.
-
Performance. Scanning every DOM mutation on a busy SPA tanks Chrome if you aren't careful with
MutationObserverdebouncing.
The fix is layered: regex shape → checksum verify → page-language heuristic → debounced observer. All four together is ~150 lines of plain JS in the content script. No framework, no bundler, no telemetry.
What it looks like
Two modes:
- Detect mode (default) — yellow underline on matches, badge count in toolbar.
-
Mask mode (Pro) — replaces the visible characters with
••••••while keeping the underlying DOM intact, so copy-paste still gives you the original if you actually need it.
No dashboard, no account, no "sign up to continue." The whole thing is local: zero network requests, zero analytics. You can verify by opening DevTools → Network and watching nothing happen.
Stack
- Manifest V3, content script only.
-
chrome.storage.localfor the on/off + Pro flag. - One
MutationObserverper frame, 250ms debounce. - Checksums in pure JS — no WASM, no external lib.
- Total size of the extension: under 40 KB packed.
What I learned shipping it
Three things I didn't expect:
"PII detection" is mostly a checksum problem, not a regex problem. Once I added RRN/사업자번호 checksums, the false-positive rate dropped to roughly zero on the pages I tested (mostly Korean SaaS dashboards, naver mail, government forms).
Manifest V3 service workers die fast. Anything that needs to persist between tab switches has to live in
chrome.storageor you'll wonder why your settings reset every 30 seconds. The first version had this bug; I noticed after I tried demoing to a friend.Pricing a niche utility is weird. I parked it at $19 because pricing it lower felt insulting to the buyer profile (someone who handles Korean PII on the daily) and pricing it higher felt greedy for ~150 lines of JS. Verdict pending — sales are still single digits.
Released the algorithm as a standalone npm package
The checksum logic above is now also published as a zero-dependency npm package — same RRN + BRN + Korean phone validators, plus a masked-RRN validator for the 901101-1****** display form Korean privacy law (개인정보보호법 §29) mandates.
npm install @lazymac/korean-pii-validate
import { validateRRN, validateBRN, validateMaskedRRN, detectAll, redactValid } from '@lazymac/korean-pii-validate';
validateRRN('901101-1234564').valid; // true
validateBRN('220-81-62517').valid; // true (official NTS checksum)
validateMaskedRRN('901101-1******').valid; // true — masked display form
41 tests, TypeScript types, ESM + CJS interop on Node 22+, MIT. Same zero-network-call promise as the extension. Use it in a pre-commit hook, an LLM-prompt sanitizer, or a CSV/log scanner.
📦 npm: @lazymac/korean-pii-validate
Links
- Get it: https://coindany.gumroad.com/l/hwwsq
- Privacy policy (it's short — "nothing leaves your browser"): https://api.lazy-mac.com/privacy/k-privacy-scanner
If you've solved a similar problem in another locale, I'd genuinely like to hear how you handled the checksum-vs-regex tradeoff — drop a comment.
— Daniel (@lazymac2x)
Top comments (0)