had a live path from untrusted input to an external sink.
Not toy examples — smolagents, CAMEL, LlamaIndex, mem0, txtai, autogen, agno, promptflow and others, across the 8 frameworks most agents are built on. 94% of them had at least one HIGH or MEDIUM.
An exfiltration doesn't need an exploit. It needs three capabilities that are each completely fine alone:
Every agent worth shipping has all three — that's what makes it useful. The risk is whether the model can walk from one to the next without anyone having decided it should. These aren't careless repos. They're repos where somebody reviewed every tool and nobody reviewed the combinations.
114 of 156 is how common the wiring is in the world — not a claim that all of it is exploitable in production, which depends on your deployment.
Lucin reads your tool definitions, agent graph and MCP config, and only counts the edges your code actually wired.
Hugging Face, July 2026.
Not a sophisticated exploit. One bad edge, traversed roughly 17,600 times over two and a half days of logs, because nothing was watching the flow.
One edge, in a tool definition, in a file somebody had already reviewed.
Read our teardown — the incident as an information-flow graph →One finding. Severity, the exact path, the line, and the fix.
Every finding answers four questions in one read: how bad, which tool-to-tool path proves it, which single tool to restrict to close every path through it, and where in the code. Same finding, three formats.
PICK A FORMAT
Every number on this site regenerates from a committed command.
64 combinations have the shape. One is wired.
Sixty-four triples have the right shape: one tool reads untrusted text, one touches private data, one can send. That says nothing about your code.
A triple is wired when all three tools are registered to the same agent, reachable in a single run, with nothing between them that requires a human. We read that off your tool definitions, agent graph and MCP config.
The gap between those two numbers is the entire product. Sixty-four is a list nobody triages. One is a decision — and it arrives with the name of the tool that closes it.
Other scanners don't model tool composition at all. That isn't a knock — it's a different job.
Triage once. After that, only new findings can fail the build.
A scanner that starts from zero on every run teaches you to ignore it. Lucin writes today's findings to a baseline file you commit. From then on CI fails on paths that weren't there yesterday — not on the backlog you inherited.
CLICK A STEP
Accepting a finding is a decision with a name and a reason on it — and it re-opens automatically if the code underneath it moves.
A baseline is a decision you can revisit. It is not a delete key.
Writing on agent security.
All posts →05 AUG 2026 An information-flow model for AI agents, and what it provably cannot decide The lattice, the reachability query, and the min vertex cut, in 985 lines — plus the one question the static model cannot answer, which is where the runtime half has to begin. → PROOF
31 JUL 2026 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 the false-positive count, the number that argues against it, and both commands. → TEARDOWN
25 JUL 2026 The attack chain started by compromising a security scanner A credential harvester in a library with 95 million monthly downloads — and the reason this post spends a section on why you should not trust mine. →
Lucin finds the path before you merge. Guard closes it at runtime.
Lucin is free, MIT and shipping today — it reads your code. Guard is what we're building next: the same information-flow model enforced live, so a path that opens after deploy is blocked instead of logged. It isn't shipped yet.
Static analysis sees the tools you declared. Runtime sees the ones the model actually reached. We're taking a small number of design partners to build Guard against real agents in production. Tell us what you're running; we reply within a day.
Or just start local — nothing here depends on us:
pip install lucin · MIT · nothing leaves your machine