The Security Gate Caught Its Own Announcement
My content security gate auto-rolled back the very article announcing it. False positive, cheap fix, and the best proof the fail-closed design works.
I'd just watched the announcement post go live. Then I ran the verify command again to fix a typo, and the terminal spat exit 1. The article, already public, got yanked back to draft on its own. The gate I'd built to protect published content had fired on its own announcement.
The trigger was dumb. The check included a regex for internal\. meant to catch internal hostnames. My draft ended a sentence with the word internal and a period. That matched, got flagged as internal topology, and rolled back.
What the gate actually checks
That commit (e262ee2) added 48 lines to the article tool's verify command. The new hard gate rejects any published article containing two classes of stuff: secrets and internal repo topology. Secrets covers API keys, tokens, passwords, JWTs, private keys, anything credential-shaped. Internal topology covers private IP ranges reserved by RFC 1918 (we mask them as 10.x.x.x, 172.16.x.x, 192.168.x.x), loopback, internal hostnames, absolute local paths, MAC addresses. We name the types, never print real ones.
Concretely, two categories get checked before anything goes live:
- Credentials and secrets: provider token shapes, JWTs, private keys, cloud key patterns, and any variable assignment with a suspiciously long literal value.
- Internal topology: addresses inside the three private ranges (always written masked, like the 10.x.x.x shape you saw above), loopback, internal hostnames and zones, absolute user home paths on any desktop OS, and MAC addresses.
The check sits as one function at the end of verify. Match found? It doesn't shrug and warn. It fails hard: exit 1, and any article already live gets **auto-rollback** to draft. That's **fail-closed**.
Our posts are written by an LLM agent that reads real repo context: commits, config, logs. Old gates only checked SEO structure, not content. A token copied from a log could have shipped to readers. OWASP says detect secrets as early as possible and rotate if exposed. GitHub push protection blocks secret pushes before they hit the repo; that's the mindset I stole. Their secret scanning covers hundreds of provider patterns.
The entry-point framing matters more than it sounds. Secrets that never reach a published page never need rotating, never need takedown requests, and never live in third-party caches. OWASP puts it the same way for source code: catch secrets at the earliest point you can, ideally before commit. A blog pipeline is just a smaller repo with a printing press attached.
Fail-closed by design
Some say a report-only check is enough, that false positives waste publishes. I don't buy it. For machine-written content pulled from real repo data, a false positive costs me minutes to rewrite a sentence, like I did with that internal period. A leaked credential costs a key rotation and leaves a permanent trace in search engines. I'd rather the gate embarrass me than leak something.
So the verify command acts, not just reports. When it rolled back its own announcement, that was the best proof the design works. I'm keeping the auto-rollback even when the author is the one who gets caught.
This is not the first gap the verify command has closed. Last week it caught rendering bugs that managed to ship despite a clean run, which I wrote up in verify said 0 fail and bugs still shipped. The difference is scope: those bugs broke how articles looked, this one guards what articles say. Same lesson either way: a gate is only as good as what it inspects.
The gate also fits into a bigger shift. The whole pipeline is event-driven now, waking only for new commits instead of chasing a daily quota, which I covered in deleting the RAMP table that haunted our scheduler. Fewer runs, but every run now ends with a check that can actually stop the presses.