← All field notes
PROOF31 JUL 20267 min read

I published my false-positive rate and the command that regenerates it. Here is why almost nobody does.

Every scanner publishes what it catches. Almost none publish a command you can run to check. Here is my false-positive count, the number that argues against it, and both commands.

Every security tool tells you what it catches. Detects 200+ attack techniques. Blocks 95% of prompt injections. Full OWASP LLM Top 10 coverage.

Go looking for the command that proves any of it and you will not find one. Not on the landing page, not in the docs, not in the repository. The number is there. The way to check the number is not.

That gap is not an oversight. It is the business model, and this post is about what happens when you close it.

The wrong number sells

Ask a vendor how good their scanner is and they will answer with recall: what fraction of the bad things it finds. It is the number that fills a slide. It is also very nearly irrelevant to whether the tool is still installed a year from now.

The number that decides that is precision, and it decides it through a sequence every engineer has lived through.

A scanner flags a line that is obviously fine. You look, you shrug, you move on, and something small happens to your relationship with the tool. Third time, someone adds a # noqa. Fifth time, it goes to /dev/null in CI with a comment nobody will read. Now you have a green dashboard, a security control in the build, and no one looking at either.

A muted scanner is worse than no scanner, because no scanner is at least honest about the coverage you have.

So here is the thing worth saying plainly, and it is counterintuitive enough that the industry has organised itself around denying it: for adoption, a false positive costs more than a miss.

A miss is invisible. You never meet the vulnerability the tool failed to find, so it takes nothing from you. A false positive is loud, it is in your face, and it takes the tool. Recall failures are silent. Precision failures are the ones that get you uninstalled.

Which means the incentive is to maximise the number people can see and never publish the one that matters. That is what everybody does, and it is rational, and I think it is worth breaking.

The number, and the command

Mine is 11.

11 adjudicated false positives across 54 real repositories and 9,520 files, counted per distinct (file, detector-id) pair, outside a documented per-repo known-capability allowlist.

python benchmarks/build_benign_corpus.py

The corpus is 54 real open-source repositories, not fixtures I wrote to be found: smolagents, CAMEL, LlamaIndex, mem0, txtai, autogen, agno, promptflow and others. The script clones them, runs every detector, and counts what survives adjudication. 380 confirmed true positives are excluded under the documented methodology, so they cannot quietly flatter the result.

Eleven was not free, and I did not get there by making the detectors timid. I got there by opening every false positive and fixing what produced it. An execute keyword that over-matched. A bare substring that fired on any FastAPI or Flask server. A database verb matching inside a docstring. Each of those was a rule that was technically correct and practically noise, and cutting them is the only reason a clean scan means anything.

Precision and recall answer different questions with different denominators Precision divides by the findings the tool produced; recall divides by the vulnerabilities known to exist. Because the denominators differ, no single accuracy figure can stand in for both. PRECISION ASKSRECALL ASKSof the things we flagged,how many were real?denominator: our findingsof the things that were real,how many did we flag?denominator: known vulnsdifferent denominators — a single accuracy number hides both
Figure 1 — Two questions, two denominators. A vendor quoting one number is choosing which question you are allowed to ask.

Now the number that cuts the other way

Eleven false positives across 54 repositories sounds excellent. Print it alone and it is misleading, so here is what that corpus cannot tell you.

On a deliberately broader 81-repo population, precision is 20.5 to 31.5% (n=73 clean-holdout adjudicated, 95% CI 12.9 to 42.9%).

Those two numbers are both true and they describe different questions. The first asks whether I fire on code that is known to be fine. The second asks what fraction of everything I say is worth your time on a population I did not curate. The second is the harder question and the answer is not flattering.

There was an earlier figure of 58%. It was computed over the same adjudication labels used to build the precision filters, which is training on the test set. It is withdrawn. I am telling you about a number I deleted because the deletion is the point: if the only numbers that ever survive contact with your methodology are the good ones, you do not have a methodology.

A low false-positive count is a red flag

You should treat mine as one. cat /dev/null has never produced a false positive either.

The only thing that makes a precision claim mean anything is recall printed beside it, so:

76% recall. 38 of 50 distinct vulnerabilities across 10 classes. A 24% false-negative rate. 86%, or 19 of 22, on the real third-party cases.

python benchmarks/recall_corpus.py

The recall corpus is 50 distinct vulnerable agents: 22 real cases with provenance and CVEs in a manifest, plus 28 labelled constructed ones. Per class, including where I am weak and where I am blind:

Vuln class Recall Note
SQL / CQL injection 100%
Command injection 100%
eval / exec RCE 100%
CORS / no-auth 100%
Lethal trifecta (exfil edge) 100% on labelled cases
Insecure deserialization 100% via cross-function/intra-class taint
Container escape ~80% resolves docker commands built through a variable
SSRF 17% deliberately conservative: fires only when tainted data forms the URL host
Path traversal 0% detector built, sound, unit-tested, switched off on purpose
False positives across the 54-repository benign corpus Each square is one repository. Forty-five produce no findings at all. Nine produce a total of eleven adjudicated false positives. The corpus was curated to be clean, which bounds what the result can be used to claim. BENIGN CORPUS — 54 REAL REPOSITORIES, 9,520 FILES45 of 54 scan completely clean9 repos produced 11 adjudicated false positivesThis answers one question — how noisy is Lucin on code chosen to be clean — and no others.
Figure 2 — A good number, on a corpus chosen to be clean. Regenerate: python benchmarks/build_benign_corpus.py

The zero I am proudest of

Look at the last row again. Zero percent. Not because I could not build it.

I did build it. It works. It has unit tests. It catches real bugs.

It is not registered, and it will not be, because the benign corpus is full of legitimate file-handling tools that are byte-identical to the vulnerable ones without runtime context. Turning it on raises recall on the slide and breaks the precision result in your repository. So it sits in the tree, switched off, and the class it covers reads 0%.

That is the whole argument for precision over recall, made once, with something that cost me. It is easy to say you value precision. It is different to publish a zero you could have made a number.

SSRF at 17% is the same trade, smaller. That detector fires only when tainted data actually forms the URL host, so it stays quiet instead of flagging every outbound request in your codebase.

Why almost nobody does this

Not dishonesty. Two much more ordinary reasons.

It is expensive. A reproducible benchmark is infrastructure: a corpus of real repositories, a labelling methodology, a script that regenerates on every commit, and all of it staying green while the detectors keep moving underneath. That is permanent work with no demo payoff.

And it is dangerous. The moment you publish the command, the number stops being yours. A hostile reader runs it. If your real false-positive rate is 12% and your marketing says "low false positives", you have handed every skeptic a thirty-second disproof. Most tools cannot survive that, so they do not offer it, and the absence of a rerun command is itself information about the number.

There is a third reason and it is the one that actually bites. Publishing the command forces you to publish the methodology: what counts as a false positive, what is excluded, which corpus, how adjudication works. Once that is public you can no longer improve the number quietly by moving a definition. The discipline is the product. The number is just its receipt.

What this does not buy

It does not make me right.

54 repositories are a proxy for the population of real agent codebases. They are not that population. Precision at real user scale is something I can only earn from real users, and I have not earned it. The recall figure will move as detectors change, and when it moves the command will show which direction it went, including the wrong one.

What it buys is narrower and it is the only thing security tooling genuinely runs on: a claim you can check without trusting the person making it. You do not have to believe eleven. You can run build_benign_corpus.py and get eleven, or you can run it and find the twelfth one I missed.

The second outcome is worth more to me than the first, and it is the ask.

pip install lucin && lucin scan ./your-agent/

MIT licensed. Both commands are committed. The 24% I miss is written down by name.

Sources: OWASP Top 10 for LLM Applications 2025. Benchmark methodology and both commands are in the repository. All figures verified against DEFINITION_OF_DONE.md, 2026-07-29.

Check your own agent for this shape.
Free, MIT, 30 seconds.