
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Mon, 29 Jun 2026 17:56:27 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Build your own vulnerability harness]]></title>
            <link>https://blog.cloudflare.com/build-your-own-vulnerability-harness/</link>
            <pubDate>Thu, 18 Jun 2026 17:59:40 GMT</pubDate>
            <description><![CDATA[ We break down the technical architecture behind our multi-stage vulnerability discovery harness and automated triage loop. Learn how we manage state controls, squash false positives through adversarial review, and route around LLM context limits. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>A few weeks ago, we published our initial findings from <a href="https://blog.cloudflare.com/cyber-frontier-models/"><u>Project Glasswing</u></a>, looking at what happens when you point frontier security models at an enterprise codebase. We also explored how our defensive structures adapt to protect our infrastructure and customers <a href="https://blog.cloudflare.com/frontier-model-defense/"><u>from threats posed by frontier AI</u></a>. Since then, the AI ecosystem has continued to shift rapidly — developers who've built tightly around a single model have already experienced what happens when that model is no longer available or gets superseded by a more capable one. These market shifts only reinforce our core thesis: no matter which underlying model is leading the pack on any given day, the future of agentic workflows will not be found in standalone models, prompts, or single-agent sessions. </p><p>Moving from a localized security "skill" to a continuous, fleet-wide scanning pipeline requires an architecture where models are treated as interchangeable components. Relying on a single model inherently limits defensive coverage, as the same system will tend to look at code paths through the exact same lens. To counter this, models should be frequently interchanged and cross-tested. By varying the models across the pipeline — such as using one model for initial discovery and an entirely different one for validation — we can ensure that vulnerabilities are cross-checked by distinct sets of logic. Furthermore, a true enterprise-scale harness must look beyond isolated repositories to trace vulnerabilities across cross-repo dependencies, ultimately filtering thousands of raw candidates down to a trusted, triaged queue of actionable fixes. </p><p>This post serves as a practical look at how to build that model-agnostic layer, focusing on how we manage state controls, eliminate false positives, and coordinate end-to-end triage at scale.</p>
    <div>
      <h2>Two objections, up front</h2>
      <a href="#two-objections-up-front">
        
      </a>
    </div>
    <p>The first post made the case for why generic coding agents can't do this job. The main issue is that agents only hold one hypothesis at a time, fill their context window after covering a sliver of a real repo, and then lose information during context compaction. For more details, <a href="https://blog.cloudflare.com/cyber-frontier-models/"><u>read that post</u></a>.</p><p>Before we move forward, we would like to answer two likely questions.</p><p><b>"Why not use subagents instead of a harness?" </b>Subagents are useful, and they are a good starting point. But security analysis needs hundreds of separate investigations that survive across runs, don't share a context window, and can be re-scoped and cross-referenced later. It needs persistence, deduplication, resumability, and eventually fleet-wide dependency tracing. That's an orchestration problem, and a prompt can't get you there.</p><p><b>"Is this blog post just an ad for frontier models?" </b>No. Our approach centers on the harness, not the model. When it comes to vulnerability discovery, we run it with whatever frontier model is currently best at what we need. When we point different models at the same target, they each turn up a different share of the bugs. The harness is the bit that lasts. If you build your own system, design it to be model-agnostic from day one. This will allow you the freedom to use any model of choice without constraints. </p>
    <div>
      <h2>It all starts with a skill</h2>
      <a href="#it-all-starts-with-a-skill">
        
      </a>
    </div>
    <p>We started with a ~450-line <code>security-audit</code> skill that we ran on a single repository, and adjusted the prompts until we surfaced real bugs. Later, we added the orchestration that became the plumbing of the entire system. The real value lives in the prompts themselves, and our prompts continue to carry the initial skill's attacker scenarios, bug classes, and anti-pattern detections nearly unchanged.</p><p>The skill was written to run a 7-phase audit in one session:</p><ul><li><p>Three parallel research agents do recon and write an <code>architecture.md</code>.</p></li><li><p>One <b>Hunter </b>agent runs per class attack, trying to break the code rather than review it.</p></li><li><p>Adversarial validators try to disprove each finding.</p></li><li><p>The survivors are written up as a human-readable vulnerability report.</p></li><li><p>They're also emitted as <code>findings.json</code> against a schema, and a mechanical check validates that file.</p></li><li><p>Finally, a fresh agent independently re-verifies every finding against the source.</p></li><li><p>The surviving, re-verified findings are submitted to the ingest API.</p></li></ul><p>That first skill maps almost directly onto the later harness:</p><table><tr><th><p><b>Skill phase</b></p></th><th><p><b>Harness stage</b></p></th></tr><tr><td><p>Recon agents write <code>architecture.md</code></p></td><td><p>Recon</p></td></tr><tr><td><p>Hunters run per attack class</p></td><td><p>Hunt</p></td></tr><tr><td><p>Validators disprove findings</p></td><td><p>Validate</p></td></tr><tr><td><p>Surviving findings become a report</p></td><td><p>Report</p></td></tr><tr><td><p><code>findings.json</code> is checked mechanically for schema adherence, not correctness</p></td><td><p>Mechanical validation of line numbers and functions in findings</p></td></tr><tr><td><p>Fresh agent re-verifies findings</p></td><td><p>Independent validation</p></td></tr></table><p>The skill worked, but it quickly revealed its limits. Looking at the coverage metrics, a single run finds only about half the bugs you'd catch across multiple runs. In our experience the ones it did find skewed toward the simpler and less subtle. Once your process is basically "run it ten times and diff by hand," you probably need to start looking at a real harness.</p><p>While running and fine-tuning the skill, we ran into three walls: </p><ul><li><p><b>Context exhaustion</b>: An hour in, the context window fills up and the model will cannibalize its own memory, instantly forgetting the bugs it spent all morning tracking down. We broke this bottleneck by externalizing the state entirely, treating the LLM as a stateless compute engine. </p></li></ul><ul><li><p><b>Persistence</b>: A crash mid-run means starting over. Losing hours of work to one AI rate-limit error or connection flakiness is an incredibly expensive way to realize you need a better architecture. </p></li></ul><ul><li><p><b>Cross-repo reasoning</b>: A single repo session is completely blind to the relationships between applications that consume it, and the number of bugs that surface when you inspect the interface between components is probably more than one might expect.</p></li></ul><div>
    <p><strong>ADVICE:</strong> A real but minimal harness consists of just Recon, Hunt, and Validate stages kept in a database, alongside a separate <strong>Validator</strong> that can't file its own findings. You should skip cross-repo tracing entirely until you have more than one repository that matters. Skip a dedicated Deduplication agent until you are actively drowning in noise. Start with a skill in your development environment, get your prompts working well, and only build the next architectural stage when not having it is the specific thing slowing you down.</p>
</div>
    <div>
      <h2>Codifying the skill into a pipeline</h2>
      <a href="#codifying-the-skill-into-a-pipeline">
        
      </a>
    </div>
    <p>Most AI security write-ups in this space are about a single repo or a curated benchmark; running a whole fleet this way, with cross-repo tracing, isn't something we've seen written up elsewhere. Our codebase spans a massive mix of languages — Rust, Go, C, Lua, TypeScript and Python, alongside various configuration management systems, static configs, and all sorts of additional context. So we had to come up with something new that worked for us. Going from that first slash-command run to a fleet scanner that could cover 128 distinct repos, automatically finding and interrogating relevant dependencies, took about six weeks. Codification was mostly mechanical: we lifted each phase of the skill into its own agent, put a database behind it and an orchestrator in front. The mapping was almost one-to-one.</p><p>The entire fleet runs on one unified harness with no per-language tuning and traces the dependencies between repos. While offloading syntax to a model makes the system language-agnostic, the differentiator is its ability to trace dependencies <i>between</i> repos. The harness itself doesn’t care if it’s looking at C pointers or a TypeScript file; it focuses on the higher-level logic of security orchestration. This allows us to scale across hundreds of different codebases, without having to write custom language parsing. </p>
    <div>
      <h2>A two-stage vulnerability research workflow</h2>
      <a href="#a-two-stage-vulnerability-research-workflow">
        
      </a>
    </div>
    <p>Our entire vulnerability research workflow is built on a two-stage operational framework: the <b>Vulnerability Discovery Harness (VDH) </b>and the <b>Vulnerability Validation System (VVS).</b></p><p>The VDH functions as our discovery engine, proactively scanning codebases to surface potential security issues. Once bugs enter the VVS, which allows multiple harnesses to feed into it, they go through stages of Deduplication, Judgment, and finally Fixing, as we’ll talk about later.</p><p>We use one model for VDH, but we use a completely different model for VVS, so the models are effectively double-checking each other. There is an obvious security benefit to this: by forcing Model B (VVS) to judge the output of Model A (VDH), you ensure that the finding is evaluated by an entirely different set of logical weights and training data — one that acts as an unbiased, adversarial third party whose sole job is to ruthlessly stress-test Model A's assumptions.  And operationally, we benefit from treating model providers like interchangeable commodities. Model providers can change temperature, caching, and inference effort budgets over time, even within one model version. Instead of building a system that depends on a model behaving predictably over time, our harness is built to absorb downstream volatility without breaking.</p>
    <div>
      <h2>Stage 1: Vulnerability Discovery Harness (VDH)</h2>
      <a href="#stage-1-vulnerability-discovery-harness-vdh">
        
      </a>
    </div>
    <p>The first post covered what each agent/stage is for, so we'll talk about the parts it didn't: the glue between stages, and the handful of details that decide whether any of it works.</p><table><tr><th><p><b>Agent/stage</b></p></th><th><p><b>Primary Role</b></p></th><th><p><b>Sub-agents / Tooling</b></p></th></tr><tr><td><p><b>Recon</b></p></td><td><p>Maps out the target architecture and maps potential threat vectors</p></td><td><p>3 parallel Recon sub-agents write <code>architecture.md</code></p></td></tr><tr><td><p><b>Hunt</b></p></td><td><p>Runs per-class attacks, compiles fragments, probes binaries</p></td><td><p>It spawns siblings (these handle between 9% and 20% of fleet-wide tasks depending on the model). It reaches out to and writes to the Wishlist tool. </p></td></tr><tr><td><p><b>Validate</b></p></td><td><p>Mechanically checks the finding, then adversarially disproves it</p></td><td><p>Runs in two passes: plain code handles the initial schema/path checks, then a single isolated agent tries to disprove the finding before it can be filed. </p></td></tr><tr><td><p><b>Gapfill</b></p></td><td><p>Generates new hunt tasks for empty coverage cells</p></td><td><p>Enqueues fresh hunt tasks for any under-tested (area × attack-class) cells that still look thin</p></td></tr><tr><td><p><b>Dedup</b></p></td><td><p>Identifies and consolidates overlapping findings</p></td><td><p>Combines deterministic code and agents to cluster findings by root cause, folding them together in real time</p></td></tr><tr><td><p><b>Trace</b></p></td><td><p>Walks dependency graph; spawns consumer-repo tasks</p></td><td><p>Walks the graph to add hunt tasks inside every identified consumer repo to make sure cross-repo bugs are caught</p></td></tr><tr><td><p><b>Feedback</b></p></td><td><p>Learns from pre-existing reports and optimizes future runs</p></td><td><p>Takes validation failures, shallow runs, and repeated misses, and instantly rewrites queued prompts to make future tasks sharper.</p></td></tr><tr><td><p><b>Report</b></p></td><td><p>Renders human-readable report</p></td><td><p>Just a script, no model required</p></td></tr></table><p><sup><i>Table 1: Vulnerability Discovery Harness (VDH)</i></sup></p><p>Stages four through eight run as a continuous producer-consumer loop. As the initial hunt progresses, the <b>Gapfill, Feedback </b>and <b>Trace agents</b> generate new tasks; <b>Dedup</b> folds overlapping findings back together and the rest of the loop keeps consuming the queue. This ensures a vulnerability discovered late in the cycle is still validated, reported and checked against other code to make sure it doesn't contain the same bug, all within the same run.</p><p>Splitting the pipeline this way guarantees strict context controls. If you fill the context window, the model starts hallucinating. We keep each agent’s job hyper-focused, keeping context usage below 25% of the total window. A naive “<i>read all files</i>” approach will blow past this limit every single time.</p><p>One thing that caught us out was that persistence needs to be factored in before parallelism. You do not want to throw away a five-hour run because of an unforeseen error. Every stage writes to one SQLite database keyed by (<code>run_id</code>, <code>repo</code>, <code>stage</code>). Any stage can resume, retry, or get pulled into a later run without redoing work. Findings are streamed and saved as they happen, so a crash costs you the task in flight and nothing else.</p><div>
    <p><strong>ADVICE: </strong>Sometimes a transient API error comes back as text in the (<code>200 OK</code>) response stream instead of throwing a code exception. To the orchestrator, this looks exactly like a task that finished cleanly. You must explicitly classify the response text, not just trust the exception type, or you end up logging empty runs as successes.</p>
</div>
    <div>
      <h3>Dynamic threat modeling</h3>
      <a href="#dynamic-threat-modeling">
        
      </a>
    </div>
    <p>During the <b>Recon</b> stage, the agent writes the threat model instead of being handed one. Beyond about ten built-in attack classes (many forms of injection, memory corruption, protocol parsing, timing side channels, and others), the <b>Recon </b>agent can invent repo-specific classes on the spot, each with its own methodology. It writes a custom taxonomy tailored specifically to that codebase, which is used to more tightly scope the <b>Hunter </b>agents.</p><p>Reading source code isn’t enough to understand how it behaves under stress, especially for subtle undefined-behavior bugs in C and other lower-level languages. The <b>Hunter </b>agents move past code reading and transition into active execution. They compile fragments, build small versions, and attack them. The biggest jump in quality came from giving <b>Hunters</b> a sandbox (built on <code>unshare</code>) to crash binaries.</p><div>
    <p><strong>ADVICE: </strong>If the harness itself runs inside Docker, that sandbox needs <code>seccomp=unconfined</code> and <code>apparmor=unconfined</code> or it will silently fail to start. It’s a one-line fix that saves you a day of head-scratching if you aren't an expert in nested containerization, like us.
</p>
</div>

    <div>
      <h3>Micro-forks and the wishlist</h3>
      <a href="#micro-forks-and-the-wishlist">
        
      </a>
    </div>
    <p>Beyond the core pipeline stages, we added two specialized mechanisms that grant the <b>Hunters</b> significant autonomy to adapt their focus and request external resources without derailing an ongoing analysis:</p><p><b>Sibling Forking</b>: This helps ensure that if a <b>Hunter</b> agent trips over an interesting code path that is outside the current scope, it doesn’t wander off track. It uses a tool call to fork a sibling agent with a precise structural seed. Fleet-wide, this accounts for roughly 9% of tasks, though the rate is highly model-dependent — from near-zero to about a fifth, depending on which model is hunting.</p><p><b>The Wishlist</b>: When an agent needs a tool it doesn't have, often a <b>Validator</b> confirming a Proof of Concept (PoC) or a <b>Hunter </b>wanting to build something (like a specific build environment, a VM, or some prod config files), it writes to a central wishlist. It provides enough context for the system to automatically re-run that exact task once a human provides the dependency. Some of these can be partly self-healing: if the container needs to be rebuilt with some changes, this can autonomously happen after the run by having a generic coding harness monitor the logs.</p><p>The wishlist has been written to 25,472 times across 128 repos since the wishlist was added, and it's the main way the agents talk back to us. One that landed while we were writing this: "<i>I need a FreeBSD VM to confirm this PoC end-to-end.</i>"</p>
    <div>
      <h3>Fleet-wide cross-repo tracing</h3>
      <a href="#fleet-wide-cross-repo-tracing">
        
      </a>
    </div>
    <p>After the initial cleanup, a <b>Tracer</b> agent checks how different software components are connected. It looks for a specific path: can a potential attacker send harmful input from the outside to a vulnerable part of the system? If the answer is yes, the <b>Tracer</b> agent automatically spawns fresh hunt tasks inside the consumer repository. To make this work, you need a unified, cross-repo symbol index and an accurate dependency graph. This allows you to uncover deep, systemic flaws that a standard single-repo scan would miss.</p><p>Running our harness across an entire fleet of repos revealed two lessons that only surfaced when this was done at scale. </p><p>First, deduplication is its own problem, big enough to need its own agents. When you are scanning a handful of repositories, you can manually eyeball overlapping bugs. Simple string matching or file-path checks won't save you here. Determining whether two complex logic flaws are actually the exact same root bug sounds trivial, but it isn't. It requires so much cognitive reasoning that we had to deploy dedicated <b>Dedup</b> agents just to clean up the noise, along with their own heuristics and ways of reducing the work.</p><p>The second is to not wire in static analysis early. We plumbed Semgrep all the way through, and the <b>Hunters</b> invoked it zero times in a month of runs. They would rather read and run the code. The wishlist, by contrast, was the single most-used tool in the system. It's worth paying attention to what the agents actually reach for, rather than what you think they'll want.</p>
    <div>
      <h3>Making findings you can trust</h3>
      <a href="#making-findings-you-can-trust">
        
      </a>
    </div>
    <p>The agent will edit the source code so its own exploit works, then triumphantly report the bug it just created. It will write a test that proves something entirely tautological like “<i>exec() executes things, therefore critical vulnerability</i>”. Or it builds an exploit that runs fine but proves nothing, because the threat model behind it is nonsense. If your harness doesn't actively fight this, all you've built is a faster way to produce junk.</p><p>A <b>Hunter </b>has to state the threat model before it's allowed to file anything. It has to define exactly who the attacker is, and what boundary the vulnerability crosses or what assumption it breaks. The output schema ordering enforces it. This requirement eliminates the vacuous findings, the "<i>if a user has database write access, they can write to the database</i>" kind.</p><p>Every confirmed finding ships with a PoC written as a test that runs against the original, untouched codebase. This prevents the agent from editing the source files to force an exploit to land. If there is no working PoC, we treat the finding as fake. In practice, that's a <b>Hunter</b> compiling a thirty-line parsing loop, running it with memory protection enabled, and demonstrating that the incorrect read stride is originating from a stack address rather than the expected message body. You can re-run it yourself. Furthermore, every confirmed finding must also ship a proposed patch. What actually reaches our review queue is a verified bug, a working test, and a functional git diff, not just a vague text description of a problem.</p><p>Before an exploit path survives, deterministic code (written in plain code, not another model) mechanically verifies that the cited files and paths actually exist, and confirms that both the patch and the test parse correctly. This <b>Validator </b>cannot log findings of its own; its sole job is to aggressively disprove the <b>Hunter</b>'s theory. If a <b>Hunter </b>is allowed to grade its own homework, it will confidently validate everything it outputs.</p><p>We don't claim a false-negative rate for our system. There's no labeled set of every real bug in a codebase, so any claimed recall number is entirely speculative. What we can watch is whether re-runs keep turning up new bugs (they do) and whether coverage is still growing across runs. It’s all a proxy, as you don’t know for sure how many bugs exist in a single codebase, but it’s a good-enough way of measuring effectiveness.</p>
    <div>
      <h2>Stage 2: Vulnerability Validation System (VVS)</h2>
      <a href="#stage-2-vulnerability-validation-system-vvs">
        
      </a>
    </div>
    <p>A finding coming out of the harness is just the start of the triage process, with all discoveries landing in a single, shared VVS that currently holds 13,841 findings across 145 repos in total. Triaging that volume is its own massive engineering problem, and it matters just as much as the hunting. That triage engine runs on a different model from the harness, broken down into three distinct jobs.</p><table><tr><th><p><b>Agent/stage</b></p></th><th><p><b>Primary role</b></p></th><th><p><b>Spawns/ sub-agents/tooling</b></p></th></tr><tr><td><p><b>Dedup</b></p></td><td><p>Identifies if a vulnerability is already in the system, or raised as internal Jira ticket already</p></td><td><p><b>Deterministic:</b> plain code builds inverted indexes over files, functions, trust boundaries, and rare tokens, then hands each finding a short candidate list 

<b>Probabilistic:</b> <b>Dedup</b> agent reasons over that short list, Stable cross-run key reopens existing records</p></td></tr><tr><td><p><b>Judgment</b></p></td><td><p>Production reachability and validation</p></td><td><p>Single agent — builds context about the bug from MCP servers, to get the shape of what the service looks like in production. Searches the wiki, Jira, git, config, and all available other sources to try and understand whether a bug is truly applicable to our production environment, and then score the vulnerability against this. It also validates the bug against source code to understand if the bug still exists on the latest main branch.</p></td></tr><tr><td><p><b>Fixing</b></p></td><td><p>Generates patches, runs regression tests</p></td><td><p>Runs the regression test before and after (filtered to the affected test; full suite only when per-test filtering isn't available). It requires a clean fail→pass flip on the target test to clear the gate. If the post-patch test fails, or if a global run detects downstream regressions, the commit is automatically blocked and flagged for human intervention.</p></td></tr></table><p><sup><i>Table 2: Vulnerability Validation System (VVS)</i></sup></p>
    <div>
      <h3>Deduping</h3>
      <a href="#deduping">
        
      </a>
    </div>
    <p>Comparing every single finding against every other finding using an LLM scales at O(N^2), which falls apart completely at scale. To keep the model off the critical path, deterministic code builds inverted indexes over the structured data (touched files/functions, trust boundary, rare tokens) to generate a short list of real candidates. Only then does an agent look at that short list to see if a single fix would close several of them. Stable cross-run keys ensure re-found bugs reopen existing records rather than spawning new ones.</p>
    <div>
      <h3>Contextual judgment</h3>
      <a href="#contextual-judgment">
        
      </a>
    </div>
    <p>Judgment is a second, independent pass over what survived. The agent rechecks the latest information, pulling from deployment, environment, and config context to determine if the code path is reachable in prod, and identify the repo owner. This process filters "exploitable now" from "real but latent" and from "real but filed against the wrong component." It's moving a pile of chaotic findings into a risk-driven orchestration workflow.</p>
    <div>
      <h3>Automated fixing</h3>
      <a href="#automated-fixing">
        
      </a>
    </div>
    <p>The <b>Fixer </b>takes the proposed patch and unit tests, rewrites them to match the repo’s style, applies the diff, and runs targeted tests. A clean fail→pass flip is the ideal and the only auto-cleanup case; a failing post-patch test blocks the commit. The <b>Fixer</b> never merges code on its own; a human must review the branch. This gate is the non-negotiable, human-in-the-loop safeguard that enables a clean, unbreakable cryptographic trail for change management compliance. Left to patch freely, a model will happily fix a security bug while quietly breaking an unrelated feature or adding dozens of new bugs.</p><p>Across all three triage jobs, each agent is confined to one narrow task wrapped in deterministic bookkeeping code, and nothing writes to production without a human signing off on a dry run. While this pipeline moves the engineering bottleneck from finding bugs to reviewing and landing fixes, the <b>Fixer </b>remains the youngest and slowest part of the system. </p>
    <div>
      <h2>What it costs</h2>
      <a href="#what-it-costs">
        
      </a>
    </div>
    <p>Running hundreds of agents over a fleet of repos is not cheap, but at least the shape of the spend is predictable. Almost all of the compute budget goes directly into the hunt stage. This makes <b>Gapfill</b> our cost-to-coverage lever, as each additional pass costs roughly half as much as the initial hunt.</p><p>Because the cost per repository varies wildly, we budget per repo rather than per run. We enforce a strict task cap per repository and spin up a worker pool of anywhere from 50 to 200 workers. That way you can spend money on the repos that are actually finding things, and not waste it on the ones that aren't.</p><p>It's also why, for us, the big scans are a periodic backlog sweep and not a per-PR check. A full scan of a complex repo can take hours; the worst run took just over 14 hours. Cheaper, smaller harnesses are the right tool for that job.</p>
    <div>
      <h2>How we tell it's working</h2>
      <a href="#how-we-tell-its-working">
        
      </a>
    </div>
    <p>We measure our system’s effectiveness by tracking how efficiently our automated pipeline filters deliberate engineering noise into high-quality, actionable findings. Because we intentionally tune our <b>Hunters</b> to over-report subtle primitives that could be chained into larger attacks, our true indicator of success is how sharply we can refine that initial mountain of raw data, before it ever reaches a human.</p><p>To gauge this, we track exactly how many raw findings survive each validation stage over time. Thanks to better context injection from our Recon phase, our initial validation rejection rate dropped from 40% down to 11%, while the share of high-integrity findings climbed from 35% to 58% (representing ~12,057 lifetime findings).</p><p>Here's the lifetime breakdown from raw candidates to actionable findings, at the point in time this blog post was written.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/65jRLGhBLnc1IvleCfC7rh/7246ae5bb6b63d7e7ee655704f224200/BLOG-3344_2.png" />
          </figure><div>

<p><strong>Vulnerability Discovery Harness (VDH)</strong></p>
<ul>
    <li><strong>Raw candidates:</strong> Everything the discovery harness emitted before independent validation.</li>
    <li><strong>Needs repro:</strong> Findings that appeared plausible but required manual reproduction before being trusted.</li>
    <li><strong>Rejected at validation:</strong> The validator disproved the threat model, exploit path, affected code, or evidence.</li>
    <li><strong>Duplicates:</strong> Candidates collapsed onto another finding from the same harness.</li>
    <li><strong>Survived validation:</strong> Findings that passed the independent validation gate and moved into the VVS.</li>
    <li><strong>Bugs that went elsewhere:</strong> Findings deliberately routed outside this flow.</li>
</ul>


<p><strong>Vulnerability Validation System (VVS)</strong></p>
<ul>
    <li><strong>Another vulnerability harness:</strong> Other automated sources feeding the same validation system.</li>
    <li><strong>Total bugs in system:</strong> The combined pool after ingest.</li>
    <li><strong>Duplicates:</strong> Findings the dedup pass identified as already covered by another canonical finding or ticket.</li>
    <li><strong>Wrong repo / other / not a risk:</strong> The noise bucket: misattributed findings, defense-in-depth, or latent risks.</li>
    <li><strong>Bugs sent to teams:</strong> Finalized, clean findings ready for remediation.</li>
    <li><strong>Judged Internet-exploitable:</strong> High-urgency findings a realistic attacker could trigger in production.</li>
    <li><strong>Not judged Internet-exploitable:</strong> Lower-urgency, actionable bugs (production issues, dependency risks, or config errors).</li>
    <li><strong>Final severity split:</strong> The categorization used to assign priority for the engineering teams.</li>
</ul>
</div><p>The core metric of the harness isn’t a speculative recall score — it’s keeping the number of unconfirmed findings in front of real humans as close to zero as possible. The architecture needs to be a relentless filtering funnel.  </p><ul><li><p>Out of <b>20,799</b> raw candidates generated by VDH, only about 12,057 survived validation. </p></li><li><p>When these were pushed into the VVS, joining findings from another harness, the central pool was brought to <b>13,841</b>. </p></li><li><p>The <b>Dedup</b> agent folded away <b>5,442</b> findings as duplicates. </p></li><li><p><b>1,154</b> were routed to the queue as ‘wrong-repo’ or ‘low-risk’ and were recycled back into the system where appropriate. </p></li><li><p>Ultimately this left <b>7,245</b> actionable findings for engineering teams to act on.</p></li></ul><p>Traditional compliance rules dictate arbitrary remediation windows based entirely on a static CVSS score (e.g., "Fix all Highs in 30 days"). Our contextual judgment layer turns this compliance checkbox into actual risk management. </p><p>The architecture is capable of tracking findings back to their origin, meaning that fixing a single root cause resolves an entire cluster of findings rather than just patching individual issues. VDH system performance is also measured by dividing repos into (area x attack-class) cells and running the <b>Gapfill</b> agent iteratively until it stops producing findings. Whenever we update an underlying prompt, we test it against a held-out repository to see if that total coverage cell number actually moves.</p><p>The harness wires automated health signals to catch system failures early in the pipeline. If a hunt finished suspiciously fast and fails to spawn sub-hunts or gap tasks, it usually indicates a crashed dependency rather than a clean codebase. To remedy this, the system flags any <b>Hunter</b> agent that finishes with zero findings as “shallow” and immediately requeues it for a new run.  </p><p>Finally, our system’s robustness is reinforced by the independent triage pass described earlier. By re-judging all submissions with a different model and separate logical weights, we ensure an unbiased, adversarial verification that is decoupled from the specific model used for discovery, providing a trust layer that persists regardless of which model is in use.</p><p>None of this is finished. We change our system constantly, and it is nowhere near a perfect science. But raw candidate findings are cheap now, and the only work worth doing is turning them into sound, verifiable code fixes.</p><p>Building your own harness means accepting that AI models are volatile, but your orchestration layer doesn't have to be. By decoupling your security logic from any single provider, forcing adversarial verification, and automating your triage pipeline, you can turn a mountain of LLM noise into a reliable, fleet-wide defense engine.</p>
    <div>
      <h3>Our “North Star” metrics: measuring real-world velocity</h3>
      <a href="#our-north-star-metrics-measuring-real-world-velocity">
        
      </a>
    </div>
    <p>Every codebase is a little different, so to show you how this actually works in the real world, we mapped out a realistic benchmark based on a standard repo run. Keep in mind that this represents a single pass on one repo; over time, as the continuous fleet-wide loop deduplicates, filters, and recycles findings, it reduces the volume of lifetime candidates by roughly 65%. </p><p><b>Engineering hours saved via automated patching: </b>Rather than focusing on static baselines, we measure the health of our pipeline by its technical throughput, processing velocity, and its ability to eliminate the manual triage bottleneck:</p><ul><li><p><b>Initial Validation Cut:</b> For a standard repository (~30k lines of code), this yields 100 initial findings, with a full run taking 3-4 hours, maintaining a hyperfocused context window throughout. </p></li><li><p><b>Compression:</b> The Deduplication and Contextual Judgment Layers process these candidates in parallel. Within 3 hours, the system compresses and refines the batch of findings from ~100 raw candidates to 80 distinct, high-fidelity bugs.</p></li><li><p><b>Remediation: </b>The automated Fixer processes these 80 distinct bugs at an average rate of 5 minutes per bug. In total, the system can discover, validate, deduplicate, and open functional pull requests in approximately 14 hours. </p></li></ul><p><b>Shrinking mean-time-to-resolve for critical flaws: </b>Of course, you can’t dump 80 patches into production all at once without breaking things. To keep deployments safe, our system uses a tiered rollout:</p><ul><li><p><b>Critical Exposure Containment</b>: The system isolates the critical, high, and exploitable bugs (avg. 10 out of 80). We fast-track these for a human review and introduce them into release cycles, getting them fully patched in production in 5 days.</p></li><li><p><b>Incremental Hardening: </b>The remaining latent risks, minor config anomalies, and lower-urgency bugs are incrementally rolled into prod over a 15-20 day window to guarantee platform stability.</p></li></ul>
    <div>
      <h3>How we’re handling all of this patching</h3>
      <a href="#how-were-handling-all-of-this-patching">
        
      </a>
    </div>
    <p>These findings are the result of an isolated, ring-fenced research experiment designed to stress-test our code. They do not represent active, unpatched vulnerabilities in our live production environment.</p><p>Because the harness runs constantly in our test environments, these specific numbers are completely out of date by the time you're reading this. Every single bug surfaced by the pipeline came attached to a working test case to demonstrate the bug and a draft patch. Our security teams are systematically processing the reports and applying the necessary fixes, meaning the Cloudflare products you use every day are already actively hardened against these vectors.</p><p>Along with this blog post, we’re releasing the initial skill we used to develop the harness, it’s been slightly cleaned up before release so it’s easier to understand and integrate, but the skill itself remains substantially the same. Hopefully the harness itself will follow shortly. This could be a starting point for your own vulnerability harness, your own skill, or whatever suits your needs best:
<a href="https://github.com/cloudflare/security-audit-skill"><u>github.com/cloudflare/security-audit-skill</u></a></p><p>If your team is working on the same problems and would like to compare notes, reach out to us at <a href="#"><u>security-ai-research@cloudflare.com</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[AI]]></category>
            <category><![CDATA[Engineering]]></category>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[Vulnerabilities]]></category>
            <guid isPermaLink="false">5Ad3Svz0xFxJQNx6eQZjJP</guid>
            <dc:creator>Dan Jones</dc:creator>
            <dc:creator>Alexandra Godoi</dc:creator>
            <dc:creator>Grant Bourzikas</dc:creator>
        </item>
        <item>
            <title><![CDATA[Defend against frontier cyber models: Cloudflare's architecture as customer zero]]></title>
            <link>https://blog.cloudflare.com/frontier-model-defense/</link>
            <pubDate>Tue, 09 Jun 2026 06:00:00 GMT</pubDate>
            <description><![CDATA[ In our post about Project Glasswing, we made the argument that the architecture around a vulnerability matters more than the speed of the patch. Here we walk through what that architecture looks like, the threats it defends against, and how we run it ourselves as Cloudflare's customer zero. ]]></description>
            <content:encoded><![CDATA[ <p></p><p></p><p>A few weeks ago, we <a href="https://blog.cloudflare.com/cyber-frontier-models/"><u>wrote about Project Glasswing</u></a> and what we observed when we pointed cyber frontier models at our own code. Since then, we’ve seen that the part of the post that has resonated most deeply is the argument that the architecture around the vulnerability matters more than the speed of the patch.</p><p>In the conversations we've had with CISOs and security teams since, the questions have been consistent: what does our architecture actually look like, what should we monitor for, where do we start, and how can Cloudflare help?</p><p>Before getting into the details: the architecture below is built almost entirely from Cloudflare's own products, because Cloudflare security is <a href="https://blog.cloudflare.com/tag/customer-zero/"><u>customer zero</u></a> for the security products we build. The Cloudflare stack already exists in front of our code, employees, and customer-facing applications. If you're a Cloudflare customer, every layer below is available to you today. If you're not, the principles still apply to whatever stack you've built.</p>
    <div>
      <h2>What a cyber frontier model actually changes</h2>
      <a href="#what-a-cyber-frontier-model-actually-changes">
        
      </a>
    </div>
    <p>In the <a href="https://blog.cloudflare.com/cyber-frontier-models/"><u>previous post</u></a>, we showed how a cyber frontier model like Mythos changes the attacker’s timeline. It can find vulnerabilities, reason through exploit chains, and generate working proofs faster than earlier models. While models like Mythos do not change the shape of an intrusion — reconnaissance, initial access, lateral movement, persistence, and exfiltration still have to happen — the difference is in the speed and scale. When pointed at the open web, a model can find and hit low-hanging fruit quickly. Against a hardened target, it still has to probe, and adapt, and it often produces more noise than a careful human operator would.</p><p>Discovery, exploit chain construction, and proof-of-concept generation used to be the gating constraints on producing a working attack. A frontier model handles all three in a fraction of the time. Work that used to be slow and methodical is now fast and indiscriminate.</p><p>While AI is accelerating how fast developer teams at Cloudflare and many other companies can ship code, the security team’s work has not compressed the same way. An attacker only needs one opening to get in, while security teams need to find and close them all. Writing a fix, regressing it, and shipping it without breaking the code around it has constraints that AI doesn't remove. We learned this the hard way when we let an AI coding assistant write its own patches against our own bugs, as we described at the end of the previous <a href="https://blog.cloudflare.com/cyber-frontier-models/"><u>post</u></a>. Some of those patches fixed the original bug while quietly breaking something else the code depended on.</p><p>As these models become more competent and capable, our main focus from a threat standpoint comes down to three things. Each one shapes the architecture we walk through in the rest of this post.</p><ul><li><p>The first is the <b>speed of discovery.</b> Frontier models make it easier to search large bodies of public code, including the open-source libraries that many companies depend on. That does not mean every bug in a library is exploitable, or that library bugs are where most vulnerabilities live. Exploitability still depends on how the code is used, whether attacker-controlled input can reach the vulnerable path, and the protections that sit around it. But widely used open-source libraries and frameworks give attackers a shared surface to study at scale. When a real, reachable vulnerability exists there, a model can help find it, reason about possible exploit paths, and generate proof-of-concept variants faster than maintainers and defenders can review every downstream use. The gap between when an attacker discovers a vulnerability and when defenders learn it exists is what worries us most. If you are not running these models against your own code, it is safe to assume someone else is.</p></li><li><p>The second is <b>exploit</b> <b>volume and adaptation.</b> A model can produce thousands of variations of a single exploit and run reconnaissance at the same scale. All that volume gives an attacker an advantage, but it won’t necessarily get them past signature-based detections. Many of those iterations will have the same underlying signature, so a rule that catches the first one will catch the rest. Adaptation is how they will get past signature-based detections. Ask a model to show you a SQL injection, and it will return a textbook example. Tell it there is a WAF in the way, and it will start probing, learning what gets blocked, and rewriting the payload until it can slip past the rule blocking it. </p></li><li><p>The third is <b>the impact when a vulnerability is inevitably exploited. </b>No architecture catches everything. After the vulnerability is exploited, the question we ask ourselves is: where can the attacker get to with one identity, one path, or one credential, before something else stops them? If the answer is "anywhere they want," the vulnerability was never the problem. The architecture around the vulnerability was.</p></li></ul>
    <div>
      <h2>Cloudflare’s superpower: visibility</h2>
      <a href="#cloudflares-superpower-visibility">
        
      </a>
    </div>
    <p>We see roughly a fifth of the web and that tells us, in real time, which payloads are mutating, which patterns are picking up, and where attacker tooling is moving next. Two teams turn that visibility into defense.</p><p>First is <a href="https://www.cloudflare.com/cloudforce-one/"><b><u>Cloudforce One</u></b></a>, our threat intelligence, research, and operations team, which sits within the Cloudflare security organization. They turn what we see across the network into insights the rest of the stack can act on: tracked adversaries, emerging campaigns, and indicators of compromise (IOCs). The hard part of this work was never knowing what is malicious — it was the delay in mitigation. Knowledge of a new threat normally has to travel from a threat report, into a feed, and then into a company’s defense before it can be used to block anything. Attackers have learned to move faster than that. Our network closes that gap: Cloudflare customers can now use Cloudforce One threat intelligence <a href="https://blog.cloudflare.com/realtime-threat-intel-waf-rules/"><u>directly within the WAF</u></a> to block high-risk traffic.</p><p>Second is the team that owns the WAF engine that does the actual detecting: the managed rulesets that run in front of our own properties and are available to every Cloudflare customer, the machine learning behind <a href="https://developers.cloudflare.com/waf/about/waf-attack-score/"><u>WAF Attack Score</u></a>, and the relationships that sometimes let us ship a rule before a CVE is publicly disclosed. The team is globally distributed and moves fast, releasing rules within hours of a proof-of-concept of an attack becoming known. Once a detection is deployed, it reaches our entire network, along with every Cloudflare customer, in under 30 seconds. <a href="https://blog.cloudflare.com/react2shell-rsc-vulnerabilities-exploitation-threat-brief/"><u>React2Shell</u></a> is a recent example: a managed WAF rule <a href="https://blog.cloudflare.com/waf-rules-react-vulnerability/"><u>was protecting our own properties, and everyone else's on Cloudflare</u></a>, hours before the official advisory was published.</p><p>The scoring layer, the defenses we put in front of the application, and the containment around the vulnerability all build on what these two teams see. </p>
    <div>
      <h2>Scores over signatures</h2>
      <a href="#scores-over-signatures">
        
      </a>
    </div>
    <p>Signature-based defenses were built for a world where novel exploits were scarce and variations took weeks. Cloudflare's traditional SLA from a fresh proof-of-concept to a live, deployed rule has been 12 hours. With the advent of frontier models, this is not good enough anymore. Detections need to be in place <a href="https://blog.himanshuanand.com/2026/05/the-90-day-disclosure-policy-is-dead/"><u>before a CVE is discovered</u></a>. This is why we layer ML-based detection in front of the traditional signature-based WAF.</p><p>The model is trained on a large body of past attack traffic, and it catches new variants of vulnerabilities before they're publicly known. A novel <a href="https://www.cloudflare.com/learning/security/threats/sql-injection/"><u>SQL injection</u></a> or remote code execution chain is almost always a rearrangement of attack shapes the model has seen before, even when the specific exploit is brand new. We run the model on every request and assign a WAF Attack Score between 1 and 99, based on how closely the request resembles those underlying shapes, not against a list of known-bad signatures. The lower the score, the more aggressively we treat the request. That score determines whether we let the request through. We apply a similar scoring methodology to AI prompts with <a href="https://developers.cloudflare.com/waf/detections/ai-security-for-apps/"><u>AI Security for Apps</u></a>: rather than check each prompt against a list of known malicious prompts, we score how closely a prompt resembles an actual attack. </p>
    <div>
      <h2>The architecture around the vulnerability</h2>
      <a href="#the-architecture-around-the-vulnerability">
        
      </a>
    </div>
    <p>Those capabilities only matter once they're stacked in front of an application, and the first layer in our defense-in-depth approach is the <a href="https://www.cloudflare.com/en-gb/application-services/products/waf/"><b><u>WAF</u></b></a>. Anything that matches a known-bad pattern gets dropped before it reaches the application, which clears the bulk of the obvious traffic and lets the more specialized layers below focus on what's left.

On the API surface, we run a positive security model through <a href="https://www.cloudflare.com/en-gb/application-services/products/api-shield/"><b><u>API Shield</u></b></a>. Instead of trying to anticipate every bad request, we describe what a valid request to each API looks like, either from the API's own definition or learned from our real traffic, and anything that doesn't fit doesn't get through. This neutralizes the advantage of frontier AI models: because we only permit validated traffic, generating thousands of new attack variations fails to bypass the system.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5KSSyocxKzsJsOfG4pxOM7/059a597af1b67a3d5a5ccdeda5ab7ca1/image2.png" />
          </figure><p><sup><i>Cloudflare’s layered architecture</i></sup></p><p><a href="https://www.cloudflare.com/learning/bots/what-is-bot-management/"><b><u>Bot Management</u></b></a> catches probing traffic on our network before frontier models can build a map. It scores every request on how likely it is to be automated, using the same signals across our whole network: how the client behaves, whether it looks like a real browser, and whether the connection matches a known-bad pattern. An attack only lands if it can find a soft spot. </p><p><a href="https://www.cloudflare.com/sase/products/access/"><b><u>Zero Trust Network Access</u></b></a> is used for every internal application. The implicit trust of being inside the network is replaced with explicit per-request identity and policy for every employee accessing every tool. The value of this was clear when one of our engineers shipped a misconfigured tool. A flat network would have exposed everything on the same segment, but in our deployment, the exposure stopped at the tool itself. We built <a href="https://developers.cloudflare.com/cloudflare-one/access-controls/access-settings/require-access-protection/"><b><u>Require Access Protection</u></b></a> afterwards so newly deployed or misconfigured applications can't be reachable before an access policy is in place.</p><p><a href="https://developers.cloudflare.com/cloudflare-one/integrations/identity-providers/idp-federation/"><b><u>IdP Federation</u></b></a> makes that secure by default posture easier to keep consistent across every Cloudflare account — which becomes even more necessary when more people are shipping internal tools quickly. Instead of asking each team to wire up SSO separately, we configure our identity provider (IdP) once and share it across the organization. New accounts get SSO automatically, recipient-side IdP connections are read-only, and Access policies in each account still evaluate the resulting identity as part of the normal request flow. </p><p><a href="https://developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/mcp-portals/"><b><u>MCP Server Portal</u></b></a> gives teams a controlled way to connect AI agents to enterprise systems. Agents access MCP servers that are centrally managed through a single portal, with every action logged. That way when an agent acts on someone's behalf, we know what it did, what it touched, and whether it should have been allowed to. The full picture of how we built it is in our post on <a href="https://blog.cloudflare.com/enterprise-mcp/"><u>enterprise MCP</u></a>.</p><p><a href="https://www.cloudflare.com/developer-platform/products/ai-gateway/"><b><u>AI Gateway</u></b></a> runs in front of our internal AI tools the same way <a href="https://developers.cloudflare.com/waf/detections/ai-security-for-apps/"><u>AI Security for Apps</u></a> runs in front of customer-facing AI features, with the same scoring and the same visibility. Inside the company, the visibility piece is more useful than the blocking, because we needed to see what engineers were actually building before we could write meaningful policy on it.</p>
    <div>
      <h2>Where your teams can start </h2>
      <a href="#where-your-teams-can-start">
        
      </a>
    </div>
    <p>Frontier models can help attackers find vulnerabilities, adapt payloads, and move faster, but they still have to pass through the layered defense you deploy in front of your application. That is where teams should start:</p><ul><li><p>Put inspection in front of public applications.</p></li><li><p>Define what valid API traffic looks like.</p></li><li><p>Use bot detection to limit automated probing.</p></li><li><p>Require identity and access policy before any internal tool is reachable.</p></li></ul><p>For AI and agentic systems:</p><ul><li><p>Route model traffic through a gateway.</p></li><li><p>Keep agents connected through approved MCP servers.</p></li><li><p>Log what they do. </p></li></ul><p>The goal is to make sure that when one layer misses, the next layer limits what the attacker can see, reach, or change.</p><p>That is the point of the architecture around the vulnerability: to limit the scope of an attack. The vulnerability may be what starts the attack, but the architecture determines how far it can go.</p>
    <div>
      <h2>How do we know this approach works?</h2>
      <a href="#how-do-we-know-this-approach-works">
        
      </a>
    </div>
    <p>Plenty of security stacks look impenetrable on a whiteboard but fall over in practice. That is why we test ours continuously, both at the perimeter and inside our environment, with our red team involved across both.</p><p><b>At the perimeter</b>, frontier models are one tool we use to test our application security stack as an adaptive attacker. These models sit alongside the rest of our red team and detection workflows including: manual testing, threat intelligence, observed traffic patterns, proof-of-concept analysis, and signals from our own network. Together, those inputs help us decide where to aim testing: newly launched products, recently changed surfaces, and the paths an attacker is most likely to probe first. The most important part is the process that follows. When something gets through, we identify the gap, use the right mix of tools to understand it, write the rule or mitigation, ship the update, and test again to make sure the gap is closed.</p><p><b>Inside the environment,</b> our red team starts from the assumption that the perimeter has already failed. They look at what has changed, where sensitive systems carry risk, and whether one compromised identity, path, or credential can reach farther than it should. When we change the architecture based on what they find, they run the scenario again against the new version to confirm the gap is actually closed.</p><p>We confirm that this architecture is working by continuously testing its behavior during failures, rather than relying on the perfection of individual layers.</p><p>If your team is working on the same problems and would like to compare notes, reach out to us at <a href="#"><u>security-ai-research@cloudflare.com</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[AI]]></category>
            <category><![CDATA[Threat Intelligence]]></category>
            <category><![CDATA[Risk Management]]></category>
            <category><![CDATA[Customer Zero]]></category>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[Zero Trust]]></category>
            <category><![CDATA[Cloudforce One]]></category>
            <category><![CDATA[Bot Management]]></category>
            <guid isPermaLink="false">5Jw5AkmauDqCVgDeCs0mBa</guid>
            <dc:creator>Rohit Chenna Reddy</dc:creator>
            <dc:creator>Chase Catelli</dc:creator>
            <dc:creator>Dan Jones</dc:creator>
        </item>
    </channel>
</rss>