When My Own Scanner Blocked My Own Commit
I was trying to push 2,000 files at 11pm. Git said no. Twice.
The second time I was already annoyed — then I realized the thing blocking me was the thing I built.
The Setup
I was consolidating two repos into one. My career workspace lived on GitHub. My homepage lived on GitLab.
I moved everything into the homepage monorepo and ran
git push. The pre-commit hook fired. It
blocked me.
What It Caught
| Block | File | Finding | Severity |
|---|---|---|---|
| 1 | career/Personal/workspace/week1-2026-07-14-20.md |
NRIC S********G | CRITICAL |
| 2 | career/*.md (3 session transcripts) |
SG phone +65 9XXXXXXX | CRITICAL |
Both are real PII — my own. Buried in old weekly planning notes and session transcripts. I wrote them months ago and forgot they were there.
The scanner uses two stages. First, ripgrep finds anything that looks like an NRIC. Second, the Mod-11 checksum verifies it. That second stage is why it catches real numbers and ignores hex strings.
Why This Matters
A regex alone would flag every 9-character hex string in my git history. I would have disabled it on day two.
pdpa-sg-clj does not work that way. It parses ripgrep’s NDJSON output, extracts candidates, then runs each through the Singapore ICA Mod-11 algorithm. Only numbers with a valid check letter count.
That is why it stayed quiet for 200 posts and fired exactly when it should. Zero false positives in normal writing. Two true positives when real PII was about to leave the machine.
The Fix Took 30 Seconds
sed -i 's/S8602645G/S********G/g' career/Personal/workspace/*.md
sed -i 's/+6596577152/+65 9XXXXXXX/g' career/*.md
git add career/ && git commit -m "Redacted PII"
Re-scan passed. Push went through as ceed690. No PII
ever reached GitLab.
The Dogfooding Loop
| Step | What happened |
|---|---|
| Build | I wrote the scanner to protect the blog before publishing |
| Install | Added it as a pre-commit hook and a publish gate |
| Forget | Wrote old notes containing my own NRIC and phone number |
| Catch | Scanner blocked the push that would have exposed them |
| Prove | The tool protected its own author from himself |
This is the highest form of dogfooding. Not a demo. Not a test fixture. My own data, my own mistake, caught by my own tool, with the same blocking rule that protects every future commit.
What I Changed After
I added the scan to every path that can leak. The commit hook blocks staged files. The publish script scans the entire blog before building. The CI check validates again.
One scanner. Three gates. Same severity model: CRITICAL and high block, medium and low warn.
Build your tools so they can say no to you.