We Ran Our Security Scanner on Its Own Code. Here Is What It Found.
A scanner that has never been pointed at its own maker is marketing. We pointed ours at our repository and our domain. This is what it found — including the finding that is still open.
Webcuris Research
Security Engineering
·4 min read

A security scanner that has never been pointed at its own maker is a marketing asset, not a tool. So we point ours at ourselves — the repository it is built from and the domain it runs on — and we act on what it finds. This article is the honest inventory: what the self-scan surfaced, what was embarrassing, what it changed in the product, and the one finding that was still open when this was first published — and which has since been closed, in public, at the bottom of this page.
The score that was wrong on purpose
The first full scan of our own repository scored 24 out of 100. Not because the product was riddled with holes, but because the repository contains what every security tool's repository contains: deliberately vulnerable test fixtures, and detection rules whose own definitions match the patterns they hunt. The scanner found our fake secrets, our intentionally broken configurations, and its own rulebook — and scored us as if they were production code.
The tempting fix was a quiet ignore-list. The honest fix was scope classification: every finding is classified by where it sits — product code, test fixture, vendored tooling — and findings outside product code are still recorded, still listed, still readable. They simply stop deciding the number. A committed credential in a fixtures directory is still a committed credential; it is just not the same fact as one in your checkout handler.
The 61-second regular expression
Our finding rules include dozens of regular expressions. Every one of them had passed code review. Then we stopped reviewing and started measuring — timing each pattern against adversarial inputs — and found one that took 61 seconds to evaluate a single crafted line. A classic ReDoS, sitting inside the security scanner itself: the tool you run against untrusted repositories contained a pattern an untrusted repository could stall.
The pattern was rewritten, and the measurement became permanent: rule patterns are now timed against hostile inputs in the test suite, with a budget they must stay inside. The lesson generalises beyond us — reasoning about regex performance failed where thirty minutes of measurement succeeded.
Three bugs only a customer could find
The most humbling class of finding did not come from the scanner at all. It came from doing what a new customer does: reading the dashboard and pasting what it says — the same passive surface anyone can read against us. Three separate instructions, printed on pages whose entire job is to be followed, could not actually be followed:
- A command that printed a localhost address to people who were not on localhost.
- A download that required a browser cookie, printed as a
curlcommand — which has no browser and no cookie, and answered 401 to everyone. - A credential printed in one command that the next command silently needed — following the page literally worked exactly once.
The common thread: we had verified the code, not the rendered page. All three now have tests that read the instructions the way a customer does — as text on a page that must be runnable — and the working rule became a house rule: verify what the customer sees, not what the source intends.
The finding we left open — and then closed
Our scanner flags domains whose root has no SPF record, because a domain that never declares who may send mail for it is easier to impersonate. It flagged webcuris.com. When this article first went out, that finding was still open, and the paragraph said it did not get to claim "fixed" until dig TXT webcuris.com said so.
It says so now. The root publishes v=spf1 include:secureserver.net ~all, and _dmarc.webcuris.com publishes a policy that reports to a mailbox we actually read. A finding you can watch us close is worth more than ten we claim we never had — so the original wording stays above rather than being quietly edited out.
Closing it taught us something we had not expected, and it is the reason the DMARC guide now carries a section about it: we added our own DMARC record while a default one from the registrar was already there, and two DMARC records is not a stronger policy — it is no policy at all. Receivers that find more than one ignore DMARC for the domain entirely. For a few hours we had managed to make our own email authentication worse by adding security to it, and we only knew because we queried the authoritative nameserver instead of trusting the control panel.
What the self-scan changed in the product
| What we hit | What now exists because of it |
|---|---|
| Fixtures decided the score | Scope classification — findings outside product code are shown but not scored |
| ReDoS in our own rules | Every rule pattern is timed against adversarial input in CI, with a hard budget |
| Unrunnable dashboard instructions | Tests that execute the rendered instructions, not the code behind them |
| Our own domain's DNS posture | The deployment monitors itself with the same checks customers get |
None of this required inventing anything. It required being willing to be a customer of our own product and reporting what that was actually like — which is the cheapest security audit available to anyone who ships software, and the least used.
Keep reading

Your Security Score Is Lying to You
Four ways a security score misleads without a single check being wrong — counted fixtures, presence theatre, severity without likelihood, and the average that hides the critical. What to demand from any number before trusting it.

Secure, HttpOnly, SameSite: Cookie Attributes Explained by What Goes Wrong Without Them
Three attributes, three specific attacks. What each one stops, what happens on the day it is missing, and the one line of Set-Cookie a session cookie should carry.

How to Find Secrets Already Committed to a Git Repository (and What to Do at 2 A.M.)
Deleting the file does nothing — the secret lives in history, clones, forks and caches. The search commands, the rotate-first order of operations, and how to prove the old credential is actually dead.