<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Corefix Blog</title>
        <link>https://blogs.corefix.dev/</link>
        <description>Security insights, product updates, and engineering deep-dives from the Corefix team.</description>
        <lastBuildDate>Mon, 22 Jun 2026 11:14:19 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Corefix Blog</title>
            <url>https://blogs.corefix.dev/light/corefix-logo-light.svg</url>
            <link>https://blogs.corefix.dev/</link>
        </image>
        <copyright>© 2026 Corefix. All rights reserved.</copyright>
        <item>
            <title><![CDATA[Mastering ZAP Authenticated Scanning: Session Management, Scan Optimization, and Framework-Aware Configuration]]></title>
            <link>https://blogs.corefix.dev/zap-authenticated-scanning-guide</link>
            <guid isPermaLink="false">https://blogs.corefix.dev/zap-authenticated-scanning-guide</guid>
            <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How we went from broken session tokens and missed vulnerabilities to a fully authenticated, optimized DAST pipeline — and every lesson learned along the way.]]></description>
            <content:encoded><![CDATA[<p><em>How we went from broken session tokens and missed vulnerabilities to a fully authenticated, optimized DAST pipeline — and every lesson learned along the way.</em></p>
<hr>
<h2 id="introduction" tabindex="-1">Introduction <a class="header-anchor" href="#introduction" aria-label="Permalink to &quot;Introduction&quot;">&ZeroWidthSpace;</a></h2>
<p>OWASP ZAP is a powerful open-source DAST (Dynamic Application Security Testing) tool, but getting authenticated scanning right — especially against modern JavaScript-heavy applications built with Next.js, React, Vue, or similar frameworks — is notoriously difficult. Authentication might <em>appear</em> to work while the session silently dies mid-scan, producing results that look comprehensive but only tested unauthenticated surfaces.</p>
<p>This post documents a real-world journey of configuring ZAP's automation framework for authenticated scanning across two structurally different applications: a <strong>Next.js + NextAuth cookie-session app</strong> and a <strong>JWT-based SPA (OWASP Juice Shop)</strong>. We cover authentication methods, session management strategies, verification tuning, framework header handling, scan policy optimization, and job ordering — with before/after data from actual scan reports.</p>
<hr>
<h2 id="the-authentication-problem" tabindex="-1">The Authentication Problem <a class="header-anchor" href="#the-authentication-problem" aria-label="Permalink to &quot;The Authentication Problem&quot;">&ZeroWidthSpace;</a></h2>
<p>ZAP offers several authentication methods — form-based, JSON-based, browser-based — and multiple session management strategies. Picking the wrong combination, or misconfiguring any piece, leads to a scan that <em>thinks</em> it's authenticated but is actually testing as an anonymous user.</p>
<h3 id="how-to-tell-if-authentication-is-really-working" tabindex="-1">How to Tell If Authentication Is Really Working <a class="header-anchor" href="#how-to-tell-if-authentication-is-really-working" aria-label="Permalink to &quot;How to Tell If Authentication Is Really Working&quot;">&ZeroWidthSpace;</a></h3>
<p>ZAP's auth report statistics are the ground truth. These are the key fields to check:</p>
<p><strong><code>stats.auth.state.loggedin</code></strong> — The number of times ZAP's poll verification <em>confirmed</em> the session was alive by matching the <code>loggedInRegex</code> against the poll URL's response. This is the gold standard. If this number is zero or absent, your scan is not reliably authenticated, regardless of what the summary says.</p>
<p><strong><code>stats.auth.state.assumedin</code></strong> — Requests where ZAP <em>assumed</em> the session was valid without re-checking. A high ratio of <code>assumedin</code> to <code>loggedin</code> is normal (ZAP can't poll on every request), but <code>loggedin</code> should still be a meaningful number — at least dozens for a multi-minute scan.</p>
<p><strong><code>stats.auth.state.unknown</code></strong> — Requests where ZAP's poll check matched neither the logged-in nor the logged-out regex. Any nonzero value here means your verification regexes don't fully cover the poll endpoint's possible responses. Investigate immediately.</p>
<p><strong><code>stats.auth.failure</code></strong> and <strong><code>stats.auth.browser.failed</code></strong> — These should be absent. If present, authentication attempts are failing, likely due to timing issues or form field detection problems.</p>
<p>Here's what a healthy vs unhealthy auth report looks like:</p>
<table tabindex="0">
<thead>
<tr>
<th>Stat</th>
<th>Broken Run</th>
<th>Healthy Run</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>auth.state.loggedin</code></td>
<td><em>absent</em></td>
<td>2,112</td>
</tr>
<tr>
<td><code>auth.state.unknown</code></td>
<td>25</td>
<td><em>absent</em></td>
</tr>
<tr>
<td><code>auth.state.assumedin</code></td>
<td>1,012</td>
<td>72,647</td>
</tr>
<tr>
<td><code>auth.failure</code></td>
<td>✗</td>
<td><em>absent</em></td>
</tr>
<tr>
<td>Session token tracked</td>
<td><em>absent</em></td>
<td>4,273</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="browser-auth-autodetect-the-winning-combination" tabindex="-1">Browser Auth + Autodetect: The Winning Combination <a class="header-anchor" href="#browser-auth-autodetect-the-winning-combination" aria-label="Permalink to &quot;Browser Auth + Autodetect: The Winning Combination&quot;">&ZeroWidthSpace;</a></h2>
<p>After testing multiple configurations, we settled on a combination that works reliably across both cookie-session and JWT-based applications.</p>
<h3 id="authentication-method-browser" tabindex="-1">Authentication Method: Browser <a class="header-anchor" href="#authentication-method-browser" aria-label="Permalink to &quot;Authentication Method: Browser&quot;">&ZeroWidthSpace;</a></h3>
<p>The <code>browser</code> method launches a real headless Firefox instance, navigates to the login page, and lets ZAP's form detection fill in credentials. This handles JavaScript-rendered login forms, CSRF tokens, multi-step flows, and cookie-setting redirects automatically — things that the <code>form</code> and <code>json</code> methods struggle with.</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">authentication</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">browser</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    browserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">firefox-headless</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    loginPageWait</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # Give SPAs time to fully render</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    loginPageUrl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://example.com/auth/signin</span></span></code></pre>
</div><p>The <code>loginPageWait: 30</code> is important for SPAs — the login form might not be in the DOM immediately. We tested with 5 seconds and saw intermittent <code>browser.nopasswordfield</code> / <code>browser.nouserfield</code> failures. At 30 seconds, ZAP reliably finds the form fields every time.</p>
<h3 id="session-management-autodetect" tabindex="-1">Session Management: Autodetect <a class="header-anchor" href="#session-management-autodetect" aria-label="Permalink to &quot;Session Management: Autodetect&quot;">&ZeroWidthSpace;</a></h3>
<p>Rather than manually templating session headers (which is fragile and error-prone), let ZAP figure out which cookies or tokens carry the session.</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">sessionManagement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">autodetect</span></span></code></pre>
</div><p>In our first attempt, we hand-crafted the session header:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">sessionManagement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">headers</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    Cookie</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"__Secure-next-auth.callback-url=...; __Secure-next-auth.session-token={%cookie:__Secure-next-auth.session-token%}"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    next-url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/auth/signin</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    rsc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    next-router-state-tree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#x3C;VALUE></span></span></code></pre>
</div><p>This caused two problems. First, <code>next-router-state-tree: &lt;VALUE&gt;</code> was a literal string placeholder, not a real value — Next.js either ignored it or returned malformed responses. Second, ZAP's own session token bookkeeping never tracked the session cookie because we'd bypassed its detection mechanism. The result: <code>auth.state.loggedin</code> was absent (ZAP never confirmed the session was alive), and the <code>__Secure-next-auth.session-token</code> cookie didn't appear in the session token stats at all.</p>
<p>Switching to <code>autodetect</code> resolved both issues immediately. ZAP discovered the session cookie on its own, tracked it across the scan, and <code>auth.state.loggedin</code> jumped from zero to 636 on the first clean run.</p>
<h3 id="verification-explicit-poll-configuration" tabindex="-1">Verification: Explicit Poll Configuration <a class="header-anchor" href="#verification-explicit-poll-configuration" aria-label="Permalink to &quot;Verification: Explicit Poll Configuration&quot;">&ZeroWidthSpace;</a></h3>
<p>Verification is how ZAP confirms the session is still alive during a scan. The <code>poll</code> method hits a URL at regular intervals and checks the response against regexes.</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">verification</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">poll</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  loggedInRegex</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'"email":"user@example\.com"'</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  loggedOutRegex</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Please check your password or email\.</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  pollFrequency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  pollUnits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">seconds</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  pollUrl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://example.com/api/auth/session</span></span></code></pre>
</div><p><strong>Never use <code>method: autodetect</code> for verification.</strong> When we did, ZAP picked a random API endpoint (<code>/api/proxy/agent-approve/...</code>) and matched on HTTP status codes (<code>200 OK</code> / <code>401 Unauthorized</code>). Any endpoint returns 200 when the server is up — this doesn't prove authentication. The poll frequency also defaulted to 60 seconds instead of 10, reducing verification checks from ~180 to just 5 across a scan.</p>
<p><strong>Always use a JSON API endpoint</strong> (session endpoint, whoami endpoint) that returns user-specific data in the response body, and match on a stable identifying field like the user's email or role.</p>
<p><strong>Always have a fallback</strong> if your LLM-based config generator fails to return a <code>pollUrl</code>:</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> verification</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> llmResult.pollUrl</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      method: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"poll"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      loggedInRegex: llmResult.loginRegex,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      loggedOutRegex: llmResult.loggedOutRegex,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      pollFrequency: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      pollUnits: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"seconds"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      pollUrl: llmResult.pollUrl,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      method: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"poll"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      loggedInRegex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Q${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">credentials</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">username</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">E`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      loggedOutRegex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Unauthorized|Invalid|Sign in"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      pollFrequency: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      pollUnits: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"seconds"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      pollUrl: customConfig.authentication.loginBackendUrl </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> loginPageUrl,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    };</span></span></code></pre>
</div><hr>
<h2 id="handling-custom-headers-separating-auth-from-business-logic" tabindex="-1">Handling Custom Headers: Separating Auth from Business Logic <a class="header-anchor" href="#handling-custom-headers-separating-auth-from-business-logic" aria-label="Permalink to &quot;Handling Custom Headers: Separating Auth from Business Logic&quot;">&ZeroWidthSpace;</a></h2>
<p>Modern web apps often include non-standard headers in authenticated requests — things like <code>X-Tenant-ID</code>, <code>X-Organization</code>, <code>X-Api-Version</code>. These get picked up during traffic analysis and need to be injected into scan requests.</p>
<p>But there's a critical distinction: <strong>auth headers</strong> (Authorization, Cookie) should be managed by ZAP's autodetect, while <strong>business headers</strong> should be injected via an httpsender script. Mixing them causes conflicts.</p>
<h3 id="the-problem-llm-generated-header-injection" tabindex="-1">The Problem: LLM-Generated Header Injection <a class="header-anchor" href="#the-problem-llm-generated-header-injection" aria-label="Permalink to &quot;The Problem: LLM-Generated Header Injection&quot;">&ZeroWidthSpace;</a></h3>
<p>We use an LLM to analyze authenticated traffic and generate ZAP configuration. The LLM prompt asks it to identify custom headers the client sends. The problem: the LLM also picks up framework-internal headers like <code>next-router-state-tree</code>, <code>rsc</code>, and <code>next-url</code> — and since their values are dynamic, it returns <code>&lt;VALUE&gt;</code> as a placeholder. This literal string then gets injected on every request via the httpsender script, causing the ajax spider's browser to conflict with the injected headers.</p>
<h3 id="the-fix-filter-before-injection" tabindex="-1">The Fix: Filter Before Injection <a class="header-anchor" href="#the-fix-filter-before-injection" aria-label="Permalink to &quot;The Fix: Filter Before Injection&quot;">&ZeroWidthSpace;</a></h3>
<p>Filter out auth headers and framework headers before generating the injection script:</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> authPatterns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">^</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">(authorization</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">cookie</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">set-cookie)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">$</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">i</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> frameworkPatterns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RegExp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "^("</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Next.js</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "next-router-state-tree"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"next-router-prefetch"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "next-router-segment-prefetch"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"next-url"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"next-action"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "rsc"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"x-nextjs-.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Remix / React Router v7</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "x-remix-.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // SvelteKit</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "x-sveltekit-.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Nuxt.js</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "x-nuxt-.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // htmx</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "hx-request"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hx-boosted"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hx-current-url"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "hx-history-restore-request"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hx-prompt"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "hx-target"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hx-trigger"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hx-trigger-name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Hotwire / Turbo</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "turbo-frame"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"turbo-stream"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"x-turbo-request-id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Server response headers (not client-sent)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "x-powered-by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"x-request-id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"x-correlation-id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "x-vercel-.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"x-middleware-.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"cf-.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"server-timing"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ].</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"|"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ")$"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"i"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> businessHeaders</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Object.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">entries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(customConfig.headers </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(([</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">authPatterns.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">frameworkPatterns.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key));</span></span></code></pre>
</div><p>Only generate the httpsender script if <code>businessHeaders.length &gt; 0</code>. The script should inject only those filtered headers.</p>
<h3 id="impact-on-ajax-spider-and-dom-xss" tabindex="-1">Impact on Ajax Spider and DOM XSS <a class="header-anchor" href="#impact-on-ajax-spider-and-dom-xss" aria-label="Permalink to &quot;Impact on Ajax Spider and DOM XSS&quot;">&ZeroWidthSpace;</a></h3>
<p>Injecting <code>next-router-state-tree: &lt;VALUE&gt;</code> had a measurable negative impact. The ajax spider uses a real browser — injecting a malformed framework header into its requests caused the browser's native routing state and the injected header to conflict. In one run, the ajax spider found only 21 URLs (vs 1,707 without the injection), and DOM XSS scanning didn't fire at all because it's tied to the ajax spider's discovery.</p>
<p>After removing the framework header injection, the ajax spider resumed normal behavior and DOM XSS scanning returned (454 scans, 12,652 DOM gets).</p>
<hr>
<h2 id="job-ordering-maximizing-discovery-and-coverage" tabindex="-1">Job Ordering: Maximizing Discovery and Coverage <a class="header-anchor" href="#job-ordering-maximizing-discovery-and-coverage" aria-label="Permalink to &quot;Job Ordering: Maximizing Discovery and Coverage&quot;">&ZeroWidthSpace;</a></h2>
<p>The order of jobs in ZAP's automation framework has a direct impact on scan coverage. After extensive testing, here's the optimal order:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">jobs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 1. Scripts (ACSRF, business headers)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">script</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # ACSRF token registration</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">script</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # Business header injection (if any)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 2. Ajax Spider FIRST — empty sites tree = max discovery + DOM XSS</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spiderAjax</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      maxDuration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 3. HAR Imports — seed real API traffic the browser can't reach</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">import</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">har</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      fileName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/zap/wrk/chunk-0.har</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 4. Requestor — ensure critical authenticated endpoints are tested</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">requestor</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 5. CSRF handling scripts (for traditional spider + active scan)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">script</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # XSRF token handler</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">script</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # Anti-CSRF form field registration</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 6. Traditional Spider — parse everything for additional links</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spider</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      maxDuration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 7. Active Scan</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">activeScan</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 8. Wait for passive scanning to complete</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">passiveScan-wait</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 9. Passive scan config (auth diagnostic rules only — AFTER wait)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">passiveScan-config</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 10. Reports</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">report</span></span></code></pre>
</div><h3 id="why-this-order-matters" tabindex="-1">Why This Order Matters <a class="header-anchor" href="#why-this-order-matters" aria-label="Permalink to &quot;Why This Order Matters&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>Ajax spider first</strong>: When the sites tree is empty, the ajax spider discovers URLs by clicking through the entire UI in a real browser. If HAR imports run first, most URLs are already known, and the ajax spider finds almost nothing (22 URLs vs 1,707). More critically, DOM XSS scanning runs during the ajax spider phase — with an empty sites tree, the DOM XSS scanner launches browser instances to test each newly discovered page (454 scans). With a pre-populated tree, it doesn't launch at all.</p>
<p><strong>HAR imports after ajax spider</strong>: HAR files contain real authenticated API traffic with exact parameters, query strings, and request bodies that no spider will ever construct on its own (e.g., <code>/api/proxy/projects/5a47706852413d3d/risk-info?category=&amp;status=&amp;page_size=25&amp;page=1&amp;search=</code>). They complement the spider's UI-level discovery with API-level coverage.</p>
<p><strong>passiveScan-config after passiveScan-wait</strong>: This is critical. If <code>passiveScan-config</code> runs first with <code>disableAllRules: true</code>, all passive scan rules are turned off during the entire scan. Moving it after <code>passiveScan-wait</code> ensures all ~80 passive rules run during scanning (finding missing security headers, cookie issues, information disclosure, etc.), and the config change only takes effect afterward for the auth diagnostic report.</p>
<hr>
<h2 id="scan-policy-optimization-balancing-depth-and-breadth" tabindex="-1">Scan Policy Optimization: Balancing Depth and Breadth <a class="header-anchor" href="#scan-policy-optimization-balancing-depth-and-breadth" aria-label="Permalink to &quot;Scan Policy Optimization: Balancing Depth and Breadth&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="the-time-budget-problem" tabindex="-1">The Time Budget Problem <a class="header-anchor" href="#the-time-budget-problem" aria-label="Permalink to &quot;The Time Budget Problem&quot;">&ZeroWidthSpace;</a></h3>
<p>ZAP's active scan rules run sequentially. Each rule tests every URL in the sites tree with its configured strength. At <code>defaultStrength: high</code>, a typical rule sends 2-3x more payloads per URL than at <code>medium</code>. Combined with a large URL set (from HAR imports), this means each rule takes significantly longer.</p>
<p>In our testing:</p>
<table tabindex="0">
<thead>
<tr>
<th>Config</th>
<th>Strength</th>
<th>Rules Completed</th>
<th>Scan Time</th>
<th>URLs Tested</th>
</tr>
</thead>
<tbody>
<tr>
<td>std-default</td>
<td>medium</td>
<td>~60</td>
<td>49 min</td>
<td>78,072</td>
</tr>
<tr>
<td>All high + 8 insane</td>
<td>high</td>
<td>~14</td>
<td>60 min</td>
<td>59,547</td>
</tr>
<tr>
<td>Default medium + 8 high</td>
<td>medium</td>
<td>~20</td>
<td>75 min</td>
<td>71,768</td>
</tr>
</tbody>
</table>
<p>Setting <code>defaultStrength: high</code> caused every rule to take 3-5x longer, so only 14 of ~60 rules completed before the scan time cap. The remaining ~46 rules were queued but never executed — including CSRF testing, LDAP injection, command injection, SSRF, and XXE.</p>
<h3 id="the-optimal-policy" tabindex="-1">The Optimal Policy <a class="header-anchor" href="#the-optimal-policy" aria-label="Permalink to &quot;The Optimal Policy&quot;">&ZeroWidthSpace;</a></h3>
<p>Keep most rules at <code>medium</code> (fast), boost only critical rules to <code>high</code>:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">policyDefinition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultStrength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultThreshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  rules</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40018</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># SQL Injection</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40019</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># SQL Injection (MySQL)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40012</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># XSS (Reflected)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40026</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># XSS (DOM)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90018</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Advanced SQL Injection</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">6</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,     </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Path Traversal</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90020</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Remote OS Command Injection</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - { </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90037</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># SSTI</span></span></code></pre>
</div><p>Never use <code>insane</code> strength — it sends exponentially more payloads with diminishing returns and can consume the entire scan budget on a single rule.</p>
<h3 id="scan-tier-configuration" tabindex="-1">Scan Tier Configuration <a class="header-anchor" href="#scan-tier-configuration" aria-label="Permalink to &quot;Scan Tier Configuration&quot;">&ZeroWidthSpace;</a></h3>
<p>Scale scan parameters based on the desired coverage level:</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> ScanTiers</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  quick: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxDepth: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxChildren: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">25</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxRuleDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxScanDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxAlertsPerRule: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxSpiderDuration: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  normal: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxDepth: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxChildren: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxRuleDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxScanDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxAlertsPerRule: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxSpiderDuration: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  moderate: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxDepth: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxChildren: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxRuleDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxScanDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxAlertsPerRule: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxSpiderDuration: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  high: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxDepth: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxChildren: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">75</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxRuleDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">8</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxScanDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">75</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxAlertsPerRule: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxSpiderDuration: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  veryHigh: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxDepth: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">25</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxChildren: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxRuleDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxScanDurationInMins: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">120</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxAlertsPerRule: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">15</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    maxSpiderDuration: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">15</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><p>Key tuning parameters:</p>
<p><strong><code>maxRuleDurationInMins</code></strong>: Caps how long any single rule can run. Without this, a greedy rule like Path Traversal can consume 15+ minutes sending 17,000+ messages while finding the same 7 vulnerabilities it found in the first 2 minutes.</p>
<p><strong><code>maxAlertsPerRule</code></strong>: Once a rule finds this many alerts, it stops. 10 is sufficient to prove a vulnerability class exists; 50 just wastes time re-confirming the same finding on different URLs.</p>
<p><strong><code>maxScanDurationInMins</code></strong>: The overall cap. Set this higher than <code>maxRuleDurationInMins × number_of_boosted_rules</code> to ensure all rules get a turn.</p>
<hr>
<h2 id="known-zap-2-17-issues" tabindex="-1">Known ZAP 2.17 Issues <a class="header-anchor" href="#known-zap-2-17-issues" aria-label="Permalink to &quot;Known ZAP 2.17 Issues&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="variantmultipartformparameters-indexoutofboundsexception" tabindex="-1">VariantMultipartFormParameters IndexOutOfBoundsException <a class="header-anchor" href="#variantmultipartformparameters-indexoutofboundsexception" aria-label="Permalink to &quot;VariantMultipartFormParameters IndexOutOfBoundsException&quot;">&ZeroWidthSpace;</a></h3>
<p>Multiple active scan rules crash with <code>IndexOutOfBoundsException: Index -1 out of bounds for length 1</code> at <code>VariantMultipartFormParameters.setParameter</code> when they encounter certain multipart form parameter structures. Affected rules include XSS, Path Traversal, External Redirect, SSI, ShellShock, and SQL Injection timing variants. Each rule recovers and continues scanning other URLs, so the impact is limited to losing coverage on that specific request per rule.</p>
<h3 id="tech-detection-passive-scanner-performance" tabindex="-1">Tech Detection Passive Scanner Performance <a class="header-anchor" href="#tech-detection-passive-scanner-performance" aria-label="Permalink to &quot;Tech Detection Passive Scanner Performance&quot;">&ZeroWidthSpace;</a></h3>
<p>The Tech Detection passive scan rule can take 30-55 seconds to process large JavaScript bundles (100KB+). This produces warnings in the log but doesn't affect scan results.</p>
<hr>
<h2 id="summary-the-final-configuration-checklist" tabindex="-1">Summary: The Final Configuration Checklist <a class="header-anchor" href="#summary-the-final-configuration-checklist" aria-label="Permalink to &quot;Summary: The Final Configuration Checklist&quot;">&ZeroWidthSpace;</a></h2>
<ol>
<li><strong>Auth method</strong>: <code>browser</code> with <code>loginPageWait: 30</code></li>
<li><strong>Session management</strong>: <code>autodetect</code> (never hand-craft session headers)</li>
<li><strong>Verification</strong>: <code>poll</code> with a JSON API endpoint, body-content regex, 10-second frequency (never <code>autodetect</code>)</li>
<li><strong>Custom headers</strong>: Filter out <code>authorization</code>, <code>cookie</code>, and all framework headers; only inject genuine business headers</li>
<li><strong>Job order</strong>: Ajax spider → HAR imports → Requestor → CSRF scripts → Traditional spider → Active scan → Passive scan wait → Config → Reports</li>
<li><strong>Scan policy</strong>: <code>defaultStrength: medium</code> with selective <code>high</code> on 8 critical rules</li>
<li><strong>Time caps</strong>: <code>maxRuleDurationInMins: 5-10</code>, <code>maxAlertsPerRule: 10-15</code>, total scan time proportional to rule count</li>
<li><strong>Passive scan</strong>: Keep all rules enabled during scanning; move <code>passiveScan-config</code> after <code>passiveScan-wait</code></li>
</ol>
<p>With this configuration, we achieved 2,112 confirmed authenticated verification checks across a 75-minute scan testing 71,768 URL/rule combinations, with zero authentication failures, zero unknown auth states, and full passive scan and DOM XSS coverage.</p>
]]></content:encoded>
            <author>Corefix Team</author>
            <enclosure url="https://blogs.corefix.dev/covers/zap-auth.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Preprocessing HAR Files for OWASP ZAP Automation: A Practical Guide]]></title>
            <link>https://blogs.corefix.dev/har-preprocessing-for-zap-automation</link>
            <guid isPermaLink="false">https://blogs.corefix.dev/har-preprocessing-for-zap-automation</guid>
            <pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[HAR files captured from browser traffic can easily exceed 25 MB, but ZAP only reads request and response fields. By stripping everything else, you can reduce a 25 MB HAR to ~500 KB with zero loss of scan coverage.]]></description>
            <content:encoded><![CDATA[<p><strong>TL;DR</strong> — HAR files captured from browser traffic can easily exceed 25 MB, but ZAP's HAR import only reads <code>log.entries[].request</code> and <code>log.entries[].response</code>. Everything else — pages, initiator stacks, timings, custom fields — is ignored. By stripping what ZAP doesn't use, you can reduce a 25 MB HAR to ~500 KB with zero loss of scan coverage.</p>
<hr>
<h2 id="the-problem" tabindex="-1">The Problem <a class="header-anchor" href="#the-problem" aria-label="Permalink to &quot;The Problem&quot;">&ZeroWidthSpace;</a></h2>
<p>If you're integrating browser-recorded traffic into OWASP ZAP's Automation Framework, you've probably hit the size wall. A single user session captured as HAR from Chrome DevTools or a browser extension can produce files of 25 MB or more. Most of that bulk comes from fields ZAP never reads: JavaScript call stacks in <code>_initiator</code>, full response bodies, Chrome-specific metadata, timing breakdowns, and duplicated requests to static assets.</p>
<p>This document covers what you can safely strip, what you must keep, and the edge cases to watch for — verified against ZAP's actual source code.</p>
<hr>
<h2 id="how-zap-imports-har-files" tabindex="-1">How ZAP Imports HAR Files <a class="header-anchor" href="#how-zap-imports-har-files" aria-label="Permalink to &quot;How ZAP Imports HAR Files&quot;">&ZeroWidthSpace;</a></h2>
<p>ZAP's HAR import lives in the <strong>exim</strong> (Import/Export) add-on. The import pipeline is straightforward:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>HarReader.readFromFile(file)</span></span>
<span class="line"><span>    → HarLog.entries()</span></span>
<span class="line"><span>        → stream/filter/map</span></span>
<span class="line"><span>            → HarUtils.createHttpMessage(entry)</span></span>
<span class="line"><span>                → persist to Sites Tree + History</span></span></code></pre>
</div><p>The current implementation (as of zap-extensions <code>main</code> branch) uses the <code>de.sstoehr.harreader</code> library, which deserializes HAR JSON via Jackson with <code>FAIL_ON_UNKNOWN_PROPERTIES=false</code>. This means unknown or custom fields are silently ignored — they won't cause parse errors.</p>
<p>The key method chain in <code>HarImporter.java</code>:</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">HarEntry</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> preProcessHarEntries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HarLog log) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">entries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HarImporter</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">entryIsNotLocalPrivate)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HarImporter</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">correctHttpVersions)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HarImporter</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">entryHasUsableHttpVersion)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>ZAP calls <code>log.entries()</code> — never <code>log.pages()</code>, <code>log.creator()</code>, <code>log.browser()</code>, or <code>log.version()</code>. The entries stream is filtered (skip <code>about:</code>, <code>chrome:</code>, <code>edge:</code> URLs), HTTP versions are corrected, and each entry is converted to an <code>HttpMessage</code> via <code>HarUtils.createHttpMessage()</code>.</p>
<p><strong>Source reference:</strong> <a href="https://github.com/zaproxy/zap-extensions/blob/main/addOns/exim/src/main/java/org/zaproxy/addon/exim/har/HarImporter.java" target="_blank" rel="noreferrer">HarImporter.java on GitHub</a></p>
<hr>
<h2 id="what-zap-reads-vs-ignores" tabindex="-1">What ZAP Reads vs. Ignores <a class="header-anchor" href="#what-zap-reads-vs-ignores" aria-label="Permalink to &quot;What ZAP Reads vs. Ignores&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="fields-zap-actively-uses" tabindex="-1">Fields ZAP actively uses <a class="header-anchor" href="#fields-zap-actively-uses" aria-label="Permalink to &quot;Fields ZAP actively uses&quot;">&ZeroWidthSpace;</a></h3>
<p>These are consumed by <code>HarUtils.createHttpMessage()</code> to reconstruct HTTP messages:</p>
<table tabindex="0">
<thead>
<tr>
<th>Field Path</th>
<th>Used For</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>request.method</code></td>
<td>HTTP method (GET, POST, etc.)</td>
</tr>
<tr>
<td><code>request.url</code></td>
<td>Target URL — populates ZAP's Sites Tree</td>
</tr>
<tr>
<td><code>request.httpVersion</code></td>
<td>Protocol version for the request line</td>
</tr>
<tr>
<td><code>request.headers[]</code></td>
<td>Request headers — used by active and passive scanners</td>
</tr>
<tr>
<td><code>request.postData.text</code></td>
<td>Request body for POST/PUT/PATCH</td>
</tr>
<tr>
<td><code>request.postData.mimeType</code></td>
<td>Content type of request body</td>
</tr>
<tr>
<td><code>request.queryString[]</code></td>
<td>URL parameters (also parsed from URL)</td>
</tr>
<tr>
<td><code>request.cookies[]</code></td>
<td>Request cookies</td>
</tr>
<tr>
<td><code>response.status</code></td>
<td>HTTP status code</td>
</tr>
<tr>
<td><code>response.statusText</code></td>
<td>Reason phrase (e.g., &quot;OK&quot;, &quot;Not Found&quot;)</td>
</tr>
<tr>
<td><code>response.httpVersion</code></td>
<td>Protocol version for the status line</td>
</tr>
<tr>
<td><code>response.headers[]</code></td>
<td>Response headers — passive scanners analyze these</td>
</tr>
<tr>
<td><code>response.content.text</code></td>
<td>Response body — passive scanners analyze this</td>
</tr>
<tr>
<td><code>response.content.mimeType</code></td>
<td>Content type of response</td>
</tr>
<tr>
<td><code>response.cookies[]</code></td>
<td>Response cookies</td>
</tr>
<tr>
<td><code>response.redirectURL</code></td>
<td>Redirect target</td>
</tr>
</tbody>
</table>
<h3 id="fields-zap-ignores" tabindex="-1">Fields ZAP ignores <a class="header-anchor" href="#fields-zap-ignores" aria-label="Permalink to &quot;Fields ZAP ignores&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Field</th>
<th>Why It Exists</th>
<th>Safe to Remove?</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>_initiator</code></td>
<td>Chrome DevTools: JS call stack that triggered the request</td>
<td>Yes</td>
</tr>
<tr>
<td><code>_priority</code></td>
<td>Chrome DevTools: network priority (High/Low)</td>
<td>Yes</td>
</tr>
<tr>
<td><code>_resourceType</code></td>
<td>Chrome DevTools: resource classification (xhr, script, etc.)</td>
<td>Yes</td>
</tr>
<tr>
<td><code>_transferSize</code></td>
<td>Compressed transfer size on the wire</td>
<td>Yes</td>
</tr>
<tr>
<td><code>_connectionId</code></td>
<td>Chrome's internal connection identifier</td>
<td>Yes</td>
</tr>
<tr>
<td><code>serverIPAddress</code></td>
<td>Resolved IP of the server</td>
<td>Yes</td>
</tr>
<tr>
<td><code>connection</code></td>
<td>Connection ID string</td>
<td>Yes</td>
</tr>
<tr>
<td><code>timings</code></td>
<td>Request phase breakdown (DNS, SSL, wait, etc.)</td>
<td>Yes</td>
</tr>
<tr>
<td><code>cache</code></td>
<td>Cache state before/after the request</td>
<td>Yes</td>
</tr>
<tr>
<td><code>time</code></td>
<td>Total elapsed time in ms</td>
<td>Yes</td>
</tr>
<tr>
<td><code>pageref</code></td>
<td>Links entry to a page in <code>log.pages[]</code></td>
<td>Yes</td>
</tr>
<tr>
<td><code>_deeptraq</code></td>
<td>Custom extension metadata (or any <code>_</code>-prefixed field)</td>
<td>Yes</td>
</tr>
<tr>
<td><code>log.pages[]</code></td>
<td>Page-level metadata (titles, page timings)</td>
<td>Content ignored; keep <code>&quot;pages&quot;: []</code> for schema validity</td>
</tr>
<tr>
<td><code>log.creator</code></td>
<td>Tool that generated the HAR</td>
<td>Ignored by importer</td>
</tr>
<tr>
<td><code>log.browser</code></td>
<td>Browser name/version</td>
<td>Ignored by importer</td>
</tr>
<tr>
<td><code>log.version</code></td>
<td>HAR spec version</td>
<td>Ignored by importer</td>
</tr>
</tbody>
</table>
<h3 id="the-pages-array-—-keep-the-key-clear-the-content" tabindex="-1">The <code>pages</code> array — keep the key, clear the content <a class="header-anchor" href="#the-pages-array-—-keep-the-key-clear-the-content" aria-label="Permalink to &quot;The `pages` array — keep the key, clear the content&quot;">&ZeroWidthSpace;</a></h3>
<p>The HAR 1.2 spec lists <code>pages</code> as optional. ZAP's importer never calls <code>log.pages()</code>. However, keeping <code>&quot;pages&quot;: []</code> rather than removing the key entirely is safer for schema validation and compatibility with other tools that might process the same HAR.</p>
<hr>
<h2 id="preprocessing-script" tabindex="-1">Preprocessing Script <a class="header-anchor" href="#preprocessing-script" aria-label="Permalink to &quot;Preprocessing Script&quot;">&ZeroWidthSpace;</a></h2>
<p>The following <code>jq</code>-based script strips everything ZAP doesn't need. It handles domain filtering, static asset exclusion, response removal, header reduction, and deduplication.</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> jqScript</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> `</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"># 1. Filter to target domain(s) only</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.log.entries |= map(select(</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  .request.url | (${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">domainFilter</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">})</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"># 2. Exclude static assets, bundled JS, analytics, and third-party trackers</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">| .log.entries |= map(select(</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  .request.url</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  | test("</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.(png|jpg|jpeg|css|js|woff2?|svg|ico|gif|ttf|eot)$</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |polyfills</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |runtime</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |main</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |vendor</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |chunk</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |bundle</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |assets/</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |static/</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |cdn</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |fonts</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |analytics</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |googletagmanager</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |hotjar</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |intercom</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |segment</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.io</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |sentry</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.io</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |clarity</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.ms</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |facebook</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.com</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |twitter</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.com</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">         |linkedin</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.com"; "ix")</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  | not</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"># 3. Strip full response objects (ZAP active scanner generates its own)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">| del(.log.entries[].response)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"># 4. Remove all non-essential entry-level fields</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">| .log.entries[] |= del(</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  .timings,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  .cache,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  .serverIPAddress,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  ._connectionId,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  ._initiator,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  ._priority,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  .time,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  .pageref</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"># 5. Keep only Content-Type and Accept headers (minimal for ZAP)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">| .log.entries[].request.headers |= map(select(</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  (.name | ascii_downcase) == "content-type"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  or (.name | ascii_downcase) == "accept"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"># 6. Deduplicate by method + base URL (without query params)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">| .log.entries |= (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  sort_by(.request.method + (.request.url | split("?")[0]))</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  | group_by(.request.method + (.request.url | split("?")[0]))</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  | map(.[0])</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"># 7. Add stub responses (ZAP requires response objects to exist)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">| .log.entries[] |= if .response == null then . + {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "response": {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "status": 200,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "statusText": "OK",</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "httpVersion": "HTTP/1.1",</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "headers": [],</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "cookies": [],</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "content": {"size": 0, "mimeType": "application/json"},</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "redirectURL": "",</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "headersSize": -1,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      "bodySize": -1</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    }</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  } else . end</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><h3 id="what-each-step-does" tabindex="-1">What each step does <a class="header-anchor" href="#what-each-step-does" aria-label="Permalink to &quot;What each step does&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>Step 1 — Domain filter:</strong> Keeps only requests to your target application. Without this, you'll import requests to CDNs, analytics services, and third-party APIs that pollute ZAP's Sites Tree.</p>
<p><strong>Step 2 — Static asset exclusion:</strong> Removes requests for images, stylesheets, fonts, JS bundles, and known analytics/tracking endpoints. These don't have security-relevant server-side logic and would generate false positives in active scanning.</p>
<p><strong>Step 3 — Response stripping:</strong> The most aggressive size reduction. Responses (especially large JSON payloads) are the biggest contributors to HAR file size. ZAP's active scanner generates its own requests and analyzes its own responses, so recorded responses are only needed for passive scanning. If passive scan coverage matters, keep responses for API endpoints and strip them only for non-API resources.</p>
<p><strong>Step 4 — Metadata cleanup:</strong> Removes Chrome DevTools fields (<code>_initiator</code>, <code>_priority</code>, <code>_connectionId</code>), timing breakdowns, cache state, and page references. None of these are read by ZAP.</p>
<p><strong>Step 5 — Header reduction:</strong> Keeps only <code>Content-Type</code> and <code>Accept</code> from request headers. ZAP reconstructs its own headers during active scanning. This is aggressive — if you need ZAP to replay exact authentication headers (e.g., <code>Authorization</code>, <code>Cookie</code>, custom auth tokens), add those to the <code>select</code> filter.</p>
<p><strong>Step 6 — Deduplication:</strong> Groups entries by <code>method + URL path</code> (ignoring query strings) and keeps the first occurrence. This collapses repeated API calls to the same endpoint.</p>
<p><strong>Step 7 — Stub responses:</strong> ZAP's <code>HarReader</code> expects a <code>response</code> object on each entry. Since step 3 deleted them, this adds minimal valid stubs.</p>
<hr>
<h2 id="deduplication-caveat-query-parameter-collapse" tabindex="-1">Deduplication Caveat: Query Parameter Collapse <a class="header-anchor" href="#deduplication-caveat-query-parameter-collapse" aria-label="Permalink to &quot;Deduplication Caveat: Query Parameter Collapse&quot;">&ZeroWidthSpace;</a></h2>
<p>The dedup step groups by <code>method + URL_without_query</code>. This means:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>GET /api/users?id=1        → kept</span></span>
<span class="line"><span>GET /api/users?id=2        → dropped (same group)</span></span>
<span class="line"><span>GET /api/users?role=admin  → dropped (same group)</span></span></code></pre>
</div><p>For most CRUD APIs this is fine — ZAP's active scanner will fuzz the parameters it finds on the surviving entry. But if different query parameter <em>names</em> trigger different server-side code paths (e.g., <code>/api/admin?action=delete</code> vs <code>/api/admin?action=export</code>), you'll lose coverage.</p>
<p><strong>Mitigation options:</strong></p>
<ul>
<li>Group by <code>method + full URL</code> instead (keeps all unique query combos, but less dedup).</li>
<li>Group by <code>method + URL path + sorted query param names</code> (dedupes only when the same parameter names appear, regardless of values).</li>
</ul>
<p>The right choice depends on your application's routing.</p>
<hr>
<h2 id="chrome-extension-har-vs-devtools-har" tabindex="-1">Chrome Extension HAR vs. DevTools HAR <a class="header-anchor" href="#chrome-extension-har-vs-devtools-har" aria-label="Permalink to &quot;Chrome Extension HAR vs. DevTools HAR&quot;">&ZeroWidthSpace;</a></h2>
<p>If you're using a custom Chrome extension to capture traffic (instead of DevTools' &quot;Export HAR&quot;), the entries are structurally equivalent for ZAP but differ in metadata:</p>
<table tabindex="0">
<thead>
<tr>
<th>Aspect</th>
<th>Chrome DevTools HAR</th>
<th>Chrome Extension HAR</th>
</tr>
</thead>
<tbody>
<tr>
<td>JS call stacks</td>
<td><code>_initiator</code> with full stack traces</td>
<td>Not captured (extension API limitation)</td>
</tr>
<tr>
<td>Custom metadata</td>
<td>None</td>
<td>Extension-specific fields (e.g., <code>_deeptraq</code>)</td>
</tr>
<tr>
<td>Header name casing</td>
<td>Mixed case (<code>X-RateLimit-Limit</code>)</td>
<td>Typically lowercase (<code>x-ratelimit-limit</code>)</td>
</tr>
<tr>
<td>Response bodies</td>
<td>Captured by default</td>
<td>Often missing (requires <code>chrome.debugger</code> API)</td>
</tr>
<tr>
<td><code>httpVersion</code> casing</td>
<td><code>&quot;http/1.1&quot;</code> (lowercase)</td>
<td><code>&quot;HTTP/1.1&quot;</code> (uppercase)</td>
</tr>
</tbody>
</table>
<h3 id="header-case-sensitivity" tabindex="-1">Header case sensitivity <a class="header-anchor" href="#header-case-sensitivity" aria-label="Permalink to &quot;Header case sensitivity&quot;">&ZeroWidthSpace;</a></h3>
<p>HTTP header names are <strong>case-insensitive</strong> per RFC 9110 §5.1. <code>Content-Type</code>, <code>content-type</code>, and <code>CONTENT-TYPE</code> are identical. ZAP handles this correctly — its <code>HttpHeader</code> class does case-insensitive lookups. HTTP/2 actually <strong>mandates</strong> lowercase header names, so an all-lowercase HAR is arguably more correct than DevTools' mixed-case output.</p>
<h3 id="httpversion-case" tabindex="-1">httpVersion case <a class="header-anchor" href="#httpversion-case" aria-label="Permalink to &quot;httpVersion case&quot;">&ZeroWidthSpace;</a></h3>
<p>ZAP's <code>HarImporter</code> uses <code>containsIgnoreCase()</code> for version checks, so <code>&quot;http/1.1&quot;</code> and <code>&quot;HTTP/1.1&quot;</code> are both accepted.</p>
<h3 id="missing-response-bodies" tabindex="-1">Missing response bodies <a class="header-anchor" href="#missing-response-bodies" aria-label="Permalink to &quot;Missing response bodies&quot;">&ZeroWidthSpace;</a></h3>
<p>This is the one difference that affects scan quality. If your extension doesn't capture response bodies, ZAP's <strong>passive scanners</strong> lose visibility into reflected content, information disclosure, framework fingerprinting, and other response-body-dependent checks. The active scanner is less affected since it generates and analyzes its own request/response pairs.</p>
<p>The Chrome <code>webRequest</code> API doesn't expose response bodies. To capture them, your extension would need to use the <code>chrome.debugger</code> API (which shows a &quot;browser is being debugged&quot; banner) or a Service Worker-based approach with the <code>chrome.declarativeNetRequest</code> API.</p>
<hr>
<h2 id="zap-automation-framework-integration" tabindex="-1">ZAP Automation Framework Integration <a class="header-anchor" href="#zap-automation-framework-integration" aria-label="Permalink to &quot;ZAP Automation Framework Integration&quot;">&ZeroWidthSpace;</a></h2>
<p>A typical automation YAML that imports preprocessed HAR files:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">env</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  vars</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    hosts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ref_0</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://target-app:8080</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  contexts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">target-context</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      urls</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ref_0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      includePaths</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://target-app:8080.*</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      excludePaths</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.*\.png</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.*\.css</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.*\.js</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">jobs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # Import preprocessed HAR(s) — seeds the Sites Tree</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">import-har</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">import</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">har</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      fileName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/zap/wrk/preprocessed-traffic.har</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # Spider from discovered URLs</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spider</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spider</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      context</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">target-context</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      maxDepth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      maxDuration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # Active scan all discovered endpoints</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">activeScan</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">activeScan</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      context</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">target-context</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      maxScanDurationInMins</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # Wait for passive scan to finish</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">passiveScan-wait</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">passiveScan-wait</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # Generate report</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">report</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">report</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">traditional-json</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      reportDir</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/zap/reports</span></span></code></pre>
</div><p>The HAR import seeds ZAP's Sites Tree with your application's API surface — every URL, method, header, and parameter from real user traffic. The spider then discovers additional linked resources, and the active scanner fuzzes all known endpoints.</p>
<hr>
<h2 id="size-reduction-results" tabindex="-1">Size Reduction Results <a class="header-anchor" href="#size-reduction-results" aria-label="Permalink to &quot;Size Reduction Results&quot;">&ZeroWidthSpace;</a></h2>
<p>On a real-world SPA with ~400 API calls across a full user session:</p>
<table tabindex="0">
<thead>
<tr>
<th>Stage</th>
<th>Size</th>
<th>Reduction</th>
</tr>
</thead>
<tbody>
<tr>
<td>Raw DevTools HAR export</td>
<td>~25 MB</td>
<td>—</td>
</tr>
<tr>
<td>After domain filtering</td>
<td>~18 MB</td>
<td>28%</td>
</tr>
<tr>
<td>After static asset exclusion</td>
<td>~12 MB</td>
<td>52%</td>
</tr>
<tr>
<td>After response stripping</td>
<td>~2 MB</td>
<td>92%</td>
</tr>
<tr>
<td>After metadata cleanup</td>
<td>~1.2 MB</td>
<td>95%</td>
</tr>
<tr>
<td>After header reduction</td>
<td>~800 KB</td>
<td>97%</td>
</tr>
<tr>
<td>After deduplication</td>
<td>~500 KB</td>
<td>98%</td>
</tr>
</tbody>
</table>
<p>The dominant size contributors are response bodies (step 3) and <code>_initiator</code> call stacks (step 4). Together, these two steps account for ~90% of the reduction.</p>
<hr>
<h2 id="quick-reference-what-s-safe-to-strip" tabindex="-1">Quick Reference: What's Safe to Strip <a class="header-anchor" href="#quick-reference-what-s-safe-to-strip" aria-label="Permalink to &quot;Quick Reference: What's Safe to Strip&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>✓ Safe to remove          ✓ Must keep              ⚠️ Depends on your needs</span></span>
<span class="line"><span>─────────────────────────  ─────────────────────────  ────────────────────────────</span></span>
<span class="line"><span>_initiator                 request.method             response.content.text</span></span>
<span class="line"><span>_priority                  request.url                  (needed for passive scan)</span></span>
<span class="line"><span>_resourceType              request.httpVersion        request.headers (auth)</span></span>
<span class="line"><span>_transferSize              request.postData             (keep auth/session headers</span></span>
<span class="line"><span>_connectionId              request.queryString           if needed)</span></span>
<span class="line"><span>serverIPAddress            request.cookies            response.headers</span></span>
<span class="line"><span>connection                 response.status              (needed for passive scan)</span></span>
<span class="line"><span>timings                    response.statusText</span></span>
<span class="line"><span>cache                      response.httpVersion</span></span>
<span class="line"><span>time                       response.content.mimeType</span></span>
<span class="line"><span>pageref                    response.cookies</span></span>
<span class="line"><span>log.pages[] contents       response.redirectURL</span></span>
<span class="line"><span>log.creator</span></span>
<span class="line"><span>log.browser</span></span></code></pre>
</div><hr>
<h2 id="key-takeaways" tabindex="-1">Key Takeaways <a class="header-anchor" href="#key-takeaways" aria-label="Permalink to &quot;Key Takeaways&quot;">&ZeroWidthSpace;</a></h2>
<ol>
<li>
<p><strong>ZAP only reads <code>entries</code></strong> — specifically <code>request.*</code> and <code>response.*</code> within each entry. The <code>pages</code> array, HAR metadata, and all underscore-prefixed custom fields are ignored.</p>
</li>
<li>
<p><strong>Keep <code>&quot;pages&quot;: []</code></strong> — don't delete the key entirely. It maintains HAR schema validity for other tools.</p>
</li>
<li>
<p><strong>Response stripping is the biggest win</strong> for file size, but it trades away passive scan coverage. Strip responses for size-critical pipelines; keep them if passive scan depth matters.</p>
</li>
<li>
<p><strong>Header names are case-insensitive</strong> — lowercase, mixed-case, or uppercase all work identically in ZAP.</p>
</li>
<li>
<p><strong>Watch your dedup strategy</strong> — grouping by URL path without query params can collapse endpoints that have different server-side behavior based on parameter names.</p>
</li>
<li>
<p><strong>Stub responses are required</strong> — if you strip responses, add minimal valid response objects back. ZAP's HAR reader expects them to exist.</p>
</li>
<li>
<p><strong>The underlying library is lenient</strong> — ZAP uses <code>de.sstoehr.harreader</code> (Jackson-based, <code>FAIL_ON_UNKNOWN_PROPERTIES=false</code>), so extra fields won't cause parse errors. But don't rely on this for correctness — strip what's unnecessary to keep file sizes manageable.</p>
</li>
</ol>
<hr>
<h2 id="why-we-built-corefix" tabindex="-1">Why We Built Corefix <a class="header-anchor" href="#why-we-built-corefix" aria-label="Permalink to &quot;Why We Built Corefix&quot;">&ZeroWidthSpace;</a></h2>
<p>HAR preprocessing is one layer of a larger pipeline. Getting it right — domain filtering, response handling, deduplication strategy, stub generation — requires understanding both ZAP's internals and your application's traffic patterns. Most teams don't have time to read <code>HarImporter.java</code> source code and iterate through edge cases.</p>
<p>Corefix handles this pipeline automatically. HAR files recorded by the Corefix Extension are preprocessed, filtered, and validated before they reach ZAP — with the right headers preserved, the right responses stripped, and scan scope correctly bounded to your application. No manual <code>jq</code> scripts. No silent empty imports.</p>
<p><strong>The scan should find vulnerabilities. The tooling should get out of the way.</strong></p>
<hr>
<p><em>Verified against ZAP exim add-on source (<code>HarImporter.java</code>, <code>HarUtils.java</code>) on the <code>main</code> branch of <a href="https://github.com/zaproxy/zap-extensions" target="_blank" rel="noreferrer">zaproxy/zap-extensions</a>. ZAP version context: 2.17.0+.</em></p>
]]></content:encoded>
            <author>Corefix Team</author>
            <enclosure url="https://blogs.corefix.dev/covers/preprocessing_har.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Supercharging OWASP ZAP with HAR Traffic Replay: A 215× Coverage Increase]]></title>
            <link>https://blogs.corefix.dev/har-augmented-zap-scanning-blog</link>
            <guid isPermaLink="false">https://blogs.corefix.dev/har-augmented-zap-scanning-blog</guid>
            <pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How recording real browser traffic with the Corefix Extension and feeding it into ZAP's automation framework transforms DAST coverage from surface-level to deep.]]></description>
            <content:encoded><![CDATA[<p><strong>How recording real browser traffic with the Corefix Extension and feeding it into ZAP's automation framework transforms DAST coverage from surface-level to deep.</strong></p>
<hr>
<h2 id="the-problem-with-scanner-only-discovery" tabindex="-1">The problem with scanner-only discovery <a class="header-anchor" href="#the-problem-with-scanner-only-discovery" aria-label="Permalink to &quot;The problem with scanner-only discovery&quot;">&ZeroWidthSpace;</a></h2>
<p>Dynamic Application Security Testing (DAST) tools like OWASP ZAP are powerful, but they share a fundamental limitation: they can only test what they can find. ZAP's built-in spiders — both the traditional crawler and the Ajax spider — navigate applications by following links, parsing HTML, and executing JavaScript. For modern single-page applications (SPAs) built with Angular, React, or Vue, this approach misses a significant portion of the attack surface.</p>
<p>API endpoints behind form submissions, multi-step checkout flows, authenticated-only routes, WebSocket interactions, and dynamic client-side routing all create blind spots. The scanner dutifully tests what it discovers, but if it never discovers your <code>/api/BasketItems/</code> POST endpoint or your <code>/rest/order-history</code> route, those remain untested.</p>
<p>This post documents a practical approach to solving this: recording real browser traffic as HAR (HTTP Archive) files using the Corefix Extension, preprocessing them, and feeding them into ZAP's automation framework to dramatically expand scan coverage.</p>
<hr>
<h2 id="architecture-overview" tabindex="-1">Architecture overview <a class="header-anchor" href="#architecture-overview" aria-label="Permalink to &quot;Architecture overview&quot;">&ZeroWidthSpace;</a></h2>
<p>The approach works in three stages:</p>
<ol>
<li><strong>Record</strong> — The Corefix Extension captures all HTTP traffic as the tester manually walks through the application, exercising authenticated flows, API calls, and multi-step processes.</li>
<li><strong>Preprocess</strong> — The raw HAR is cleaned (removing static assets, deduplicating entries, splitting into manageable chunks) and validated to ensure entries actually exist before passing to ZAP.</li>
<li><strong>Replay &amp; scan</strong> — ZAP's automation framework imports the HAR chunks, replays key endpoints via the requestor job, then spiders and actively scans the vastly expanded site tree.</li>
</ol>
<p>The key insight is that human testers naturally exercise the application in ways automated crawlers cannot. By capturing that traffic and feeding it to ZAP, you combine human discovery with automated vulnerability detection.</p>
<hr>
<h2 id="target-application" tabindex="-1">Target application <a class="header-anchor" href="#target-application" aria-label="Permalink to &quot;Target application&quot;">&ZeroWidthSpace;</a></h2>
<p>All testing was performed against <strong>OWASP Juice Shop</strong>, a deliberately vulnerable Node.js/Angular application running on <code>http://74.225.252.175:3000</code>. Juice Shop is an ideal test subject because it has a rich SPA frontend with many API endpoints, authentication flows, and business logic routes that are invisible to traditional crawlers.</p>
<hr>
<h2 id="test-methodology" tabindex="-1">Test methodology <a class="header-anchor" href="#test-methodology" aria-label="Permalink to &quot;Test methodology&quot;">&ZeroWidthSpace;</a></h2>
<p>Two scans were performed against the same target with identical authentication configuration:</p>
<ul>
<li><strong>Scan A (baseline):</strong> Standard ZAP automation with spider + Ajax spider + active scan. No HAR context provided.</li>
<li><strong>Scan B (HAR-augmented):</strong> Same scan pipeline, but seeded with 77 HAR entries captured via the Corefix Extension, plus a requestor job replaying key authenticated endpoints.</li>
</ul>
<p>Both scans used the same ZAP version (2.17.0), the same authentication credentials (<code>admin@juice-sh.op</code>), and the same active scan policy.</p>
<hr>
<h2 id="recording-traffic-with-the-corefix-extension" tabindex="-1">Recording traffic with the Corefix Extension <a class="header-anchor" href="#recording-traffic-with-the-corefix-extension" aria-label="Permalink to &quot;Recording traffic with the Corefix Extension&quot;">&ZeroWidthSpace;</a></h2>
<p>The Corefix Extension acts as a transparent proxy or browser instrumentation layer that captures all HTTP/HTTPS traffic during a manual testing session. The recording covered:</p>
<ul>
<li>Full authentication flow (login, token acquisition)</li>
<li>Product browsing and search</li>
<li>Basket operations (add, remove, view)</li>
<li>Checkout flow (address, delivery, payment, order confirmation)</li>
<li>Profile management and photo upload</li>
<li>Admin panel access</li>
<li>Feedback and complaint submission</li>
<li>Order tracking and history</li>
<li>Captcha interactions</li>
<li>WebSocket connections</li>
</ul>
<p>The recording session produced a HAR file that was then split into 5 time-based chunks for parallel import:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>chunk_2026-06-16T14-14-28-993Z-clean-0.har</span></span>
<span class="line"><span>chunk_2026-06-16T14-15-29-604Z-clean-1.har</span></span>
<span class="line"><span>chunk_2026-06-16T14-16-28-957Z-clean-2.har</span></span>
<span class="line"><span>chunk_2026-06-16T14-17-28-918Z-clean-3.har</span></span>
<span class="line"><span>chunk_2026-06-16T14-18-08-729Z-clean-4.har</span></span></code></pre>
</div><h3 id="validating-har-files-before-scanning" tabindex="-1">Validating HAR files before scanning <a class="header-anchor" href="#validating-har-files-before-scanning" aria-label="Permalink to &quot;Validating HAR files before scanning&quot;">&ZeroWidthSpace;</a></h3>
<p>A critical lesson learned during this experiment: always validate that your preprocessed HAR files contain actual entries before kicking off a scan. An initial run appeared to succeed but <code>stats.exim.importer.har.count</code> was <code>0</code> — the preprocessing pipeline had silently produced empty files.</p>
<p>The fix is simple:</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Verify each chunk has entries</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> f </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">in</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /zap/wrk/chunk_*-clean-*.har</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">do</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  count</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">jq</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '.log.entries | length'</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  echo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">: </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$count</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> entries"</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$count</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> -eq</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ]; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">then</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    echo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "ERROR: Empty HAR file detected. Fix preprocessing before scanning."</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    exit</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  fi</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">done</span></span></code></pre>
</div><hr>
<h2 id="zap-automation-framework-configuration" tabindex="-1">ZAP automation framework configuration <a class="header-anchor" href="#zap-automation-framework-configuration" aria-label="Permalink to &quot;ZAP automation framework configuration&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="authentication-setup" tabindex="-1">Authentication setup <a class="header-anchor" href="#authentication-setup" aria-label="Permalink to &quot;Authentication setup&quot;">&ZeroWidthSpace;</a></h3>
<p>Both scans used JSON-based authentication with header-based session management. An important configuration difference in the HAR-augmented scan was switching from cookie-based to Bearer token session handling, which better matches Juice Shop's JWT architecture:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">sessionManagement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">headers</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    Authorization</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Bearer {%json:authentication.token%}</span></span></code></pre>
</div><h3 id="job-pipeline" tabindex="-1">Job pipeline <a class="header-anchor" href="#job-pipeline" aria-label="Permalink to &quot;Job pipeline&quot;">&ZeroWidthSpace;</a></h3>
<p>The HAR-augmented automation YAML follows this job sequence:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>passiveScan-config  →  Configure passive rules BEFORE any traffic</span></span>
<span class="line"><span>import-har (×5)     →  Seed site tree with recorded traffic</span></span>
<span class="line"><span>requestor           →  Replay authenticated endpoints (GET + POST)</span></span>
<span class="line"><span>spider              →  Crawl discovered links</span></span>
<span class="line"><span>ajaxSpider          →  JavaScript-aware crawling</span></span>
<span class="line"><span>activeScan          →  Vulnerability testing</span></span>
<span class="line"><span>passiveScan-wait    →  Wait for passive analysis</span></span>
<span class="line"><span>report (×3)         →  JSON + Auth-JSON + HTML reports</span></span></code></pre>
</div><h3 id="the-requestor-job" tabindex="-1">The requestor job <a class="header-anchor" href="#the-requestor-job" aria-label="Permalink to &quot;The requestor job&quot;">&ZeroWidthSpace;</a></h3>
<p>Beyond importing HAR files, the requestor job explicitly replays key authenticated endpoints to ensure they appear in ZAP's site tree with valid session tokens. This is especially important for POST endpoints that the spider cannot discover:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">- </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">requestor</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">requestor</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    user</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">zap-user</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    context</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">ZAP-Context-1781619657762</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  requests</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://target:3000/api/BasketItems/</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">POST</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'{"ProductId":1,"BasketId":"1","quantity":1}'</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://target:3000/api/Feedbacks/</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">POST</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'{"UserId":1,"captchaId":0,"captcha":"","comment":"test","rating":3}'</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # ... 40+ additional endpoints</span></span></code></pre>
</div><h3 id="active-scan-policy-tuning" tabindex="-1">Active scan policy tuning <a class="header-anchor" href="#active-scan-policy-tuning" aria-label="Permalink to &quot;Active scan policy tuning&quot;">&ZeroWidthSpace;</a></h3>
<p>The scan policy was configured with targeted rule overrides for high-value vulnerability classes:</p>
<table tabindex="0">
<thead>
<tr>
<th>Rule ID</th>
<th>Rule name</th>
<th>Strength</th>
<th>Threshold</th>
</tr>
</thead>
<tbody>
<tr>
<td>40018</td>
<td>SQL injection (time-based)</td>
<td>Insane</td>
<td>Low</td>
</tr>
<tr>
<td>40012</td>
<td>XSS (reflected)</td>
<td>Insane</td>
<td>Low</td>
</tr>
<tr>
<td>40026</td>
<td>Cross-site request forgery</td>
<td>Insane</td>
<td>Low</td>
</tr>
<tr>
<td>40024</td>
<td>SQL injection (plugin-based)</td>
<td>Insane</td>
<td>Low</td>
</tr>
<tr>
<td>90018</td>
<td>Advanced SQL injection</td>
<td>High</td>
<td>Low</td>
</tr>
<tr>
<td>6</td>
<td>Path traversal</td>
<td>High</td>
<td>Low</td>
</tr>
<tr>
<td>90020</td>
<td>Remote OS command injection</td>
<td>High</td>
<td>Low</td>
</tr>
<tr>
<td>90037</td>
<td>Server-side request forgery</td>
<td>High</td>
<td>Low</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="results-head-to-head-comparison" tabindex="-1">Results: head-to-head comparison <a class="header-anchor" href="#results-head-to-head-comparison" aria-label="Permalink to &quot;Results: head-to-head comparison&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="discovery-metrics" tabindex="-1">Discovery metrics <a class="header-anchor" href="#discovery-metrics" aria-label="Permalink to &quot;Discovery metrics&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Metric</th>
<th>Without HAR</th>
<th>With HAR</th>
<th>Improvement</th>
</tr>
</thead>
<tbody>
<tr>
<td>Spider URLs found</td>
<td>18</td>
<td>3,866</td>
<td><strong>215×</strong></td>
</tr>
<tr>
<td>Spider URLs added to tree</td>
<td>45</td>
<td>4,251</td>
<td><strong>94×</strong></td>
</tr>
<tr>
<td>Active scan URLs tested</td>
<td>35,909</td>
<td>132,208</td>
<td><strong>3.7×</strong></td>
</tr>
<tr>
<td>Network requests sent</td>
<td>37,831</td>
<td>162,688</td>
<td><strong>4.3×</strong></td>
</tr>
<tr>
<td>HTTP 200 responses</td>
<td>572</td>
<td>5,953</td>
<td><strong>10.4×</strong></td>
</tr>
<tr>
<td>DOM XSS scan targets</td>
<td>67</td>
<td>577</td>
<td><strong>8.6×</strong></td>
</tr>
<tr>
<td>Passive scan records</td>
<td>137</td>
<td>670</td>
<td><strong>4.9×</strong></td>
</tr>
</tbody>
</table>
<p>The HAR import seeded ZAP with 77 real HTTP transactions, which the spider then used as starting points to discover 3,866 additional URLs — a 215× increase over the 18 URLs found through crawling alone.</p>
<h3 id="new-vulnerability-classes-discovered" tabindex="-1">New vulnerability classes discovered <a class="header-anchor" href="#new-vulnerability-classes-discovered" aria-label="Permalink to &quot;New vulnerability classes discovered&quot;">&ZeroWidthSpace;</a></h3>
<p>The most significant outcome: <strong>three entirely new active scan vulnerability classes</strong> appeared only in the HAR-augmented scan:</p>
<table tabindex="0">
<thead>
<tr>
<th>Scanner ID</th>
<th>Vulnerability</th>
<th>Alerts</th>
<th>Severity</th>
</tr>
</thead>
<tbody>
<tr>
<td>40012</td>
<td>Cross-site scripting (reflected)</td>
<td>10</td>
<td>High</td>
</tr>
<tr>
<td>40014</td>
<td>Cross-site scripting (persistent)</td>
<td>10</td>
<td>High</td>
</tr>
<tr>
<td>43</td>
<td>XML external entity (XXE) attack</td>
<td>8</td>
<td>High</td>
</tr>
</tbody>
</table>
<p>These 28 high-severity findings were invisible to the baseline scan because the vulnerable endpoints were never discovered by the spider. The HAR file contained the API calls that exercise these code paths, giving ZAP the context it needed to test them.</p>
<h3 id="passive-scan-alert-growth" tabindex="-1">Passive scan alert growth <a class="header-anchor" href="#passive-scan-alert-growth" aria-label="Permalink to &quot;Passive scan alert growth&quot;">&ZeroWidthSpace;</a></h3>
<p>The expanded traffic volume also dramatically increased passive scan findings:</p>
<table tabindex="0">
<thead>
<tr>
<th>Rule</th>
<th>Without HAR</th>
<th>With HAR</th>
<th>Growth</th>
</tr>
</thead>
<tbody>
<tr>
<td>Missing security headers (90005)</td>
<td>2,356</td>
<td>17,844</td>
<td>7.6×</td>
</tr>
<tr>
<td>Timestamp disclosure (10096)</td>
<td>242</td>
<td>7,770</td>
<td>32×</td>
</tr>
<tr>
<td>Insufficient site isolation (90004)</td>
<td>44</td>
<td>7,639</td>
<td>174×</td>
</tr>
<tr>
<td>Cacheable content (10049)</td>
<td>589</td>
<td>4,455</td>
<td>7.6×</td>
</tr>
<tr>
<td>Cross-domain JavaScript (10098)</td>
<td>510</td>
<td>4,307</td>
<td>8.4×</td>
</tr>
<tr>
<td>Missing CSP (10038)</td>
<td>22</td>
<td>3,806</td>
<td>173×</td>
</tr>
<tr>
<td>Missing permissions policy (10063)</td>
<td>98</td>
<td>3,880</td>
<td>40×</td>
</tr>
<tr>
<td>Base64 disclosure (10094)</td>
<td>25</td>
<td>3,783</td>
<td>151×</td>
</tr>
<tr>
<td>Modern web app issues (10109)</td>
<td>7</td>
<td>3,765</td>
<td>538×</td>
</tr>
<tr>
<td>Application error disclosure (90022)</td>
<td>0</td>
<td>19</td>
<td><strong>New</strong></td>
</tr>
</tbody>
</table>
<h3 id="visual-comparison-passive-scan-growth" tabindex="-1">Visual comparison: passive scan growth <a class="header-anchor" href="#visual-comparison-passive-scan-growth" aria-label="Permalink to &quot;Visual comparison: passive scan growth&quot;">&ZeroWidthSpace;</a></h3>
<p><img src="/images/Har-Compare.png" alt="Passive scan alert volume — baseline vs HAR-augmented"></p>
<p>The increase in URL discovery directly translated into dramatically broader passive scan coverage. Once HAR traffic seeded authenticated routes, API endpoints, and application workflows that traditional crawling could not reach, ZAP began observing significantly more responses across the application.</p>
<p>Several passive scan categories experienced substantial growth. Security header findings increased from 2,356 to 17,844 alerts. Timestamp disclosures grew from 242 to 7,770. Missing Content Security Policy findings expanded from just 22 alerts to 3,806. Site isolation observations increased from 44 to 7,639, while modern web application detections grew from 7 to 3,765.</p>
<p>Most importantly, entirely new finding categories appeared. Application Error Disclosure (90022), which was absent in the baseline scan, surfaced 19 findings after HAR replay expanded the scanner's visibility into deeper application functionality.</p>
<p>The chart above illustrates how additional application context translates directly into broader passive security coverage. While passive findings do not always indicate exploitable vulnerabilities, they provide valuable visibility into security posture, misconfigurations, information disclosure risks, and missing defensive controls that would otherwise remain hidden.</p>
<h3 id="key-observation" tabindex="-1">Key observation <a class="header-anchor" href="#key-observation" aria-label="Permalink to &quot;Key observation&quot;">&ZeroWidthSpace;</a></h3>
<p>The growth in passive scan volume was not caused by more aggressive scanning. It was caused by better discovery. The scanner simply had access to a much larger portion of the application after importing 77 authenticated HAR entries and replaying critical workflows through the requestor job.</p>
<p>This reinforces a core lesson from the experiment:</p>
<p><strong>Coverage quality is often more important than scanner aggressiveness.</strong></p>
<p>A scanner cannot analyze endpoints it never discovers. HAR augmentation dramatically expands that visibility.</p>
<h3 id="new-content-types-discovered" tabindex="-1">New content types discovered <a class="header-anchor" href="#new-content-types-discovered" aria-label="Permalink to &quot;New content types discovered&quot;">&ZeroWidthSpace;</a></h3>
<p>The HAR-augmented scan also discovered content types that the baseline scan never encountered:</p>
<ul>
<li><code>application/pdf</code> — downloadable invoice/order documents</li>
<li><code>application/octet-stream</code> — file download endpoints</li>
<li><code>text/markdown</code> — documentation/legal text endpoints</li>
<li>HTTP 201 (Created) responses — successful resource creation</li>
<li>HTTP 404 (Not Found) responses — error handling paths</li>
</ul>
<hr>
<h2 id="lessons-learned" tabindex="-1">Lessons learned <a class="header-anchor" href="#lessons-learned" aria-label="Permalink to &quot;Lessons learned&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-validate-har-import-before-scanning" tabindex="-1">1. Validate HAR import before scanning <a class="header-anchor" href="#_1-validate-har-import-before-scanning" aria-label="Permalink to &quot;1. Validate HAR import before scanning&quot;">&ZeroWidthSpace;</a></h3>
<p>The <code>stats.exim.importer.har.count</code> statistic is your early-exit check. If it reads <code>0</code> after the import jobs run, kill the scan immediately — your HAR files are empty or the paths are wrong. Don't wait 45+ minutes for a scan that has no additional context.</p>
<h3 id="_2-passivescan-config-must-come-first" tabindex="-1">2. passiveScan-config must come first <a class="header-anchor" href="#_2-passivescan-config-must-come-first" aria-label="Permalink to &quot;2. passiveScan-config must come first&quot;">&ZeroWidthSpace;</a></h3>
<p>A subtle but critical ordering issue: if <code>passiveScan-config</code> appears after <code>passiveScan-wait</code>, the configuration is applied after all passive scanning has completed — making it useless. The config job must be the first job in the pipeline, before any traffic-generating jobs.</p>
<h3 id="_3-bearer-tokens-vs-cookies-matter" tabindex="-1">3. Bearer tokens vs cookies matter <a class="header-anchor" href="#_3-bearer-tokens-vs-cookies-matter" aria-label="Permalink to &quot;3. Bearer tokens vs cookies matter&quot;">&ZeroWidthSpace;</a></h3>
<p>Juice Shop uses JWT authentication. The baseline scan used cookie-based session management (<code>Cookie: token=...</code>), while the HAR-augmented scan used the correct <code>Authorization: Bearer ...</code> header. This seemingly small change ensures all authenticated requests are properly credentialed, improving coverage of auth-protected endpoints.</p>
<h3 id="_4-budget-time-for-expanded-attack-surface" tabindex="-1">4. Budget time for expanded attack surface <a class="header-anchor" href="#_4-budget-time-for-expanded-attack-surface" aria-label="Permalink to &quot;4. Budget time for expanded attack surface&quot;">&ZeroWidthSpace;</a></h3>
<p>The baseline scan completed in ~12 minutes of active scanning. The HAR-augmented scan hit its 45-minute timeout and was stopped with approximately 15 scanner rules never executing. When you dramatically expand the URL tree, you must proportionally increase <code>maxScanDurationInMins</code> and <code>maxRuleDurationInMins</code> to accommodate the larger surface.</p>
<h3 id="_5-exclude-destructive-endpoints" tabindex="-1">5. Exclude destructive endpoints <a class="header-anchor" href="#_5-exclude-destructive-endpoints" aria-label="Permalink to &quot;5. Exclude destructive endpoints&quot;">&ZeroWidthSpace;</a></h3>
<p>Active scanning sends malicious payloads to every discovered endpoint. Without exclude patterns, the scanner can hit password-change, account-deletion, or logout endpoints, breaking the authenticated session mid-scan. Always add excludePaths for these:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">excludePaths</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.*\/rest\/user\/change-password.*</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.*\/rest\/user\/reset-password.*</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.*\/rest\/user\/erasure-request.*</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.*\/dataerasure.*</span></span></code></pre>
</div><h3 id="_6-post-endpoints-need-explicit-requestor-entries" tabindex="-1">6. POST endpoints need explicit requestor entries <a class="header-anchor" href="#_6-post-endpoints-need-explicit-requestor-entries" aria-label="Permalink to &quot;6. POST endpoints need explicit requestor entries&quot;">&ZeroWidthSpace;</a></h3>
<p>The spider discovers URLs by parsing responses, but it cannot infer POST request body formats. Endpoints like <code>/api/BasketItems/</code> (POST), <code>/api/Feedbacks/</code> (POST), and <code>/api/Complaints</code> (POST) must be explicitly defined in the requestor job with sample payloads.</p>
<h3 id="_7-technology-scoping-reduces-noise" tabindex="-1">7. Technology scoping reduces noise <a class="header-anchor" href="#_7-technology-scoping-reduces-noise" aria-label="Permalink to &quot;7. Technology scoping reduces noise&quot;">&ZeroWidthSpace;</a></h3>
<p>Setting the technology include list to match your actual stack (Node.js, MongoDB, Linux) tells ZAP to skip irrelevant checks (ASP.NET, PHP, Java-specific rules), reducing scan time and network load without sacrificing relevant coverage:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">technology</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  include</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Db.CouchDB</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Db.MongoDB</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Language.JavaScript</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Language.JavaScript.NodeJS</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">OS.Linux</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SCM.Git</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">WS.Node</span></span></code></pre>
</div><hr>
<h2 id="recommended-automation-yaml-structure" tabindex="-1">Recommended automation YAML structure <a class="header-anchor" href="#recommended-automation-yaml-structure" aria-label="Permalink to &quot;Recommended automation YAML structure&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Phase 0: Configuration</span></span>
<span class="line"><span>  ── passiveScan-config (BEFORE any traffic)</span></span>
<span class="line"><span>  ── script setup (ACSRF tokens, custom hooks)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Phase 1: Seed</span></span>
<span class="line"><span>  ── import HAR chunks (×N)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Phase 2: Replay</span></span>
<span class="line"><span>  ── requestor (GET + POST authenticated endpoints)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Phase 3: Crawl</span></span>
<span class="line"><span>  ── spider (traditional, 10–15 min)</span></span>
<span class="line"><span>  ── ajaxSpider (JS-aware, 8–12 min)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Phase 4: Attack</span></span>
<span class="line"><span>  ── activeScan (tuned policy, 60–120 min budget)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Phase 5: Report</span></span>
<span class="line"><span>  ── passiveScan-wait</span></span>
<span class="line"><span>  ── report generation (JSON + HTML)</span></span></code></pre>
</div><hr>
<h2 id="why-we-built-corefix" tabindex="-1">Why We Built Corefix <a class="header-anchor" href="#why-we-built-corefix" aria-label="Permalink to &quot;Why We Built Corefix&quot;">&ZeroWidthSpace;</a></h2>
<p>The results speak for themselves: recording browser traffic with the Corefix Extension and feeding it into ZAP's automation framework produced a <strong>215× increase in URL discovery</strong>, a <strong>3.7× increase in active scan coverage</strong>, and <strong>28 new high-severity vulnerability findings</strong> that were completely invisible to the baseline scan.</p>
<p>The technique requires minimal additional effort — a 5-minute manual walkthrough of the application generates enough HAR data to transform scan quality. For any modern SPA or API-heavy application, this approach should be considered standard practice rather than optional enhancement.</p>
<p>The investment is a few minutes of manual browsing and a validated preprocessing pipeline. The return is dramatically better security coverage and findings that matter.</p>
<p><strong>Your scanner should find vulnerabilities, not miss them because it never discovered the endpoints.</strong></p>
<hr>
<p><em>Tested with OWASP ZAP 2.17.0 against OWASP Juice Shop. Scan date: June 16, 2026.</em></p>
]]></content:encoded>
            <author>Corefix Team</author>
            <enclosure url="https://blogs.corefix.dev/covers/har-zap.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[We Benchmarked 21 LLMs on Security Analysis. Here's What We Found.]]></title>
            <link>https://blogs.corefix.dev/corefix-llm-benchmark</link>
            <guid isPermaLink="false">https://blogs.corefix.dev/corefix-llm-benchmark</guid>
            <pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Processing time, task completion, and reliability data from running 247 real-world security findings through 21 LLMs - from Claude and GPT-5 to Bedrock and open-source models.]]></description>
            <content:encoded><![CDATA[<p><strong>Not all LLMs are equal when it comes to security analysis. We ran the numbers.</strong></p>
<hr>
<p>As Corefix integrates AI into its vulnerability management pipeline, we needed to answer a deceptively simple question: which models actually perform well on real-world security analysis tasks?</p>
<p>We ran a systematic benchmark against the <a href="https://github.com/mai1x9/chef" target="_blank" rel="noreferrer">chef repository</a> — a forked version of the original Chef project, approximately 2000 commits behind upstream — producing 247 findings across secrets, dependency vulnerabilities, Semgrep findings, and IaC findings. We fed these through our full AI enrichment pipeline across 21 models and measured what happened.</p>
<p>The results were not what we expected.</p>
<h2 id="what-we-tested" tabindex="-1">What We Tested <a class="header-anchor" href="#what-we-tested" aria-label="Permalink to &quot;What We Tested&quot;">&ZeroWidthSpace;</a></h2>
<p>Every model ran the same four-task pipeline against the complete 247-finding dataset:</p>
<ul>
<li><strong>Deduplication</strong>: collapsing duplicate findings across scanners</li>
<li><strong>Enrichment</strong>: adding context, severity reasoning, and remediation guidance to each finding</li>
<li><strong>Cross-scanner correlation</strong>: linking related findings from different tools — for example, a secret finding correlating with a dependency vulnerability in the same service</li>
<li><strong>Attack chain identification</strong>: identifying sequences of findings that could be chained into an exploitable attack path</li>
</ul>
<p>Processing time reflects how long each model took to complete the full pipeline on the 247-finding dataset. This is initial benchmarking — results will vary as prompts and pipeline logic are refined.</p>
<h2 id="processing-time-results" tabindex="-1">Processing Time Results <a class="header-anchor" href="#processing-time-results" aria-label="Permalink to &quot;Processing Time Results&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Provider</th>
<th style="text-align:right">Processing Time</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Haiku</td>
<td>Claude</td>
<td style="text-align:right">108s</td>
<td>—</td>
</tr>
<tr>
<td>Sonnet 4.6</td>
<td>Claude</td>
<td style="text-align:right">410s</td>
<td>Slowest Claude variant tested</td>
</tr>
<tr>
<td>GLM 4.7</td>
<td>Zhihu AI</td>
<td style="text-align:right">153s</td>
<td>—</td>
</tr>
<tr>
<td>Grok 4.3</td>
<td>xAI</td>
<td style="text-align:right">122s</td>
<td>—</td>
</tr>
<tr>
<td>Bedrock Minimax 2.5</td>
<td>AWS Bedrock</td>
<td style="text-align:right">168s</td>
<td>—</td>
</tr>
<tr>
<td>Bedrock Kimi K2.5</td>
<td>AWS Bedrock</td>
<td style="text-align:right">143s</td>
<td>—</td>
</tr>
<tr>
<td>Bedrock GLM-4.7-Flash</td>
<td>AWS Bedrock</td>
<td style="text-align:right">143s</td>
<td>Faster flash variant</td>
</tr>
<tr>
<td>Bedrock GLM 5</td>
<td>AWS Bedrock</td>
<td style="text-align:right">503s</td>
<td>Slowest overall</td>
</tr>
<tr>
<td>Bedrock DeepSeek 3.2</td>
<td>AWS Bedrock</td>
<td style="text-align:right">248s</td>
<td>—</td>
</tr>
<tr>
<td>Bedrock Qwen 230B</td>
<td>AWS Bedrock</td>
<td style="text-align:right">63s</td>
<td>Fastest; attack chains not found</td>
</tr>
<tr>
<td>Bedrock GPT OSS 120B</td>
<td>AWS Bedrock</td>
<td style="text-align:right">68s</td>
<td>Worked well</td>
</tr>
<tr>
<td>GPT-5.4</td>
<td>OpenAI</td>
<td style="text-align:right">226s</td>
<td>—</td>
</tr>
<tr>
<td>GPT-5.4 Mini</td>
<td>OpenAI</td>
<td style="text-align:right">87s</td>
<td>Good speed</td>
</tr>
<tr>
<td>GPT-5.4 Nano</td>
<td>OpenAI</td>
<td style="text-align:right">117s</td>
<td>—</td>
</tr>
<tr>
<td>GPT-4.1 Mini</td>
<td>OpenAI</td>
<td style="text-align:right">118s</td>
<td>—</td>
</tr>
<tr>
<td>GPT-4o Nano</td>
<td>OpenAI</td>
<td style="text-align:right">80s</td>
<td>Very fast</td>
</tr>
<tr>
<td>GPT-5 Mini</td>
<td>OpenAI</td>
<td style="text-align:right">460s</td>
<td>Unexpectedly slow</td>
</tr>
<tr>
<td>GPT-5 Nano</td>
<td>OpenAI</td>
<td style="text-align:right">380s</td>
<td>Slow for nano tier</td>
</tr>
<tr>
<td>GPT-5</td>
<td>OpenAI</td>
<td style="text-align:right">410s</td>
<td>—</td>
</tr>
<tr>
<td>GPT-5.1</td>
<td>OpenAI</td>
<td style="text-align:right">140s</td>
<td>Balanced</td>
</tr>
<tr>
<td>GPT-5.2</td>
<td>OpenAI</td>
<td style="text-align:right">248s</td>
<td>—</td>
</tr>
</tbody>
</table>
<h2 id="key-observations" tabindex="-1">Key Observations <a class="header-anchor" href="#key-observations" aria-label="Permalink to &quot;Key Observations&quot;">&ZeroWidthSpace;</a></h2>
<p>The spread is wider than expected across all tiers:</p>
<ul>
<li><strong>Fastest overall</strong>: Bedrock Qwen 230B at 63 seconds — but attack chain identification was absent from results</li>
<li><strong>Best speed and quality balance</strong>: Bedrock GPT OSS 120B at 68 seconds, with strong overall task completion</li>
<li><strong>Fast OpenAI options</strong>: GPT-4o Nano at 80 seconds, GPT-5.4 Mini at 87 seconds</li>
<li><strong>Slowest models</strong>: Bedrock GLM 5 at 503 seconds, GPT-5 Mini at 460 seconds, Claude Sonnet 4.6 at 410 seconds</li>
</ul>
<p>The counterintuitive finding: model size does not predict processing time. GPT-5 Mini (460s) is nearly as slow as full GPT-5 (410s) and significantly slower than GPT-5.4 (226s). Nano tiers cluster between 80–380 seconds — a 4x range that reflects rate limiting and infrastructure variance as much as model capability.</p>
<h2 id="recommendations-and-known-issues" tabindex="-1">Recommendations and Known Issues <a class="header-anchor" href="#recommendations-and-known-issues" aria-label="Permalink to &quot;Recommendations and Known Issues&quot;">&ZeroWidthSpace;</a></h2>
<p>Based on testing across all providers, here is what we found:</p>
<p><strong>Kimi</strong>
Do not use <code>kimi-k2.5</code> or <code>kimi-2.6</code> directly via their API — they are being overloaded or rate-limited despite Tier 1 access. Use <code>k2.5</code> via AWS Bedrock instead.</p>
<p><strong>GLM Models</strong>
All direct GLM model calls are resulting in <code>429</code> or <code>524</code> errors. Avoid calling GLM models directly. Use Bedrock for GLM 5, GLM 4.7, and GLM 4.7-Flash. Note that GLM 4.7 can be used for coding tasks but concurrency is limited to 1.</p>
<p><strong>Claude on Bedrock</strong>
Bedrock Anthropic Haiku is working, but the model must be activated via AWS with a support ticket specifying your use case before it can be used.</p>
<p><strong>JSON Response Format</strong>
GPT OSS 120B and Claude APIs do not support <code>type: json_object</code> as a response format. If <code>jsonFormat=false</code>, add explicit JSON formatting rules to the system prompt. The custom JSON parser will handle extracting structured output from the model response.</p>
<p><strong>GPT Models</strong>
All GPT models including nano and mini variants are working. Previous issues with <code>nano</code> and <code>mini</code> variants are resolved.</p>
<p><strong>Context and Token Limits</strong>
No context-related issues or max token errors observed — everything appears stable on this front.</p>
<h2 id="why-we-built-corefix" tabindex="-1">Why We Built Corefix <a class="header-anchor" href="#why-we-built-corefix" aria-label="Permalink to &quot;Why We Built Corefix&quot;">&ZeroWidthSpace;</a></h2>
<p>AI-assisted security analysis is only as good as the pipeline surrounding the model. Raw processing time is one dimension — but attack chain identification, cross-scanner correlation, and enrichment quality all depend on how findings are assembled and presented before the model ever sees them.</p>
<p>Corefix wraps model selection with full context assembly. Every finding gets the surrounding code, dependency context, framework version, and scanner evidence before the LLM processes it. We run this pipeline across all major providers — Bedrock, OpenAI, Anthropic direct — and select the optimal model per task type based on benchmarks like these.</p>
<p>The practical result: you don't choose a model or write prompt templates. Corefix handles selection, context assembly, and quality control. You get enriched findings with remediation guidance, deduplicated across scanners, with attack chains identified automatically.</p>
<p><strong>What took us weeks of benchmarking to determine, Corefix handles on every scan.</strong></p>
<hr>
<p><em>Corefix integrates AI enrichment across all major models and providers. <a href="https://app.corefix.dev" target="_blank" rel="noreferrer">Try a free scan →</a> to see AI-powered security analysis on your actual findings.</em></p>
]]></content:encoded>
            <author>Corefix Team</author>
            <enclosure url="https://blogs.corefix.dev/covers/llm-benchmark.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How We Teach a Security Scanner to Understand Your App Before It Tests It]]></title>
            <link>https://blogs.corefix.dev/smart-zap-context-blog</link>
            <guid isPermaLink="false">https://blogs.corefix.dev/smart-zap-context-blog</guid>
            <pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The engineering behind intelligent ZAP context building — from raw HTML to authenticated scans, automatically. No manual setup required.]]></description>
            <content:encoded><![CDATA[<p><strong>Most security scanners are blunt instruments. We built the layer that makes them intelligent.</strong></p>
<hr>
<p>Point a scanner at a URL and it floods your app with requests, misses half the attack surface because it couldn't log in, and produces reports full of noise. The configuration burden falls entirely on you — write the auth scripts, specify the CSRF fields, identify the login selectors, handle the cookie banners. Do it wrong and the scanner never gets past the landing page.</p>
<p>We built a smarter layer that sits in front of ZAP. Before a single probe fires, our system reads your application the way a developer would — understands its architecture, identifies how authentication works, handles the quirks — and hands ZAP a complete, accurate context. The result is broader coverage, real authenticated scanning, and no manual setup.</p>
<p>This is the engineering story behind that layer.</p>
<h2 id="the-problem-with-naive-scanning" tabindex="-1">The Problem with Naive Scanning <a class="header-anchor" href="#the-problem-with-naive-scanning" aria-label="Permalink to &quot;The Problem with Naive Scanning&quot;">&ZeroWidthSpace;</a></h2>
<p>Web applications today aren't what they were a decade ago. A large and growing share are <strong>Single Page Applications</strong> — Angular, React, Vue — that serve an essentially empty HTML shell and build the entire UI in the browser via JavaScript. When a scanner fetches <code>https://app.example.com/#/login</code>, the server responds with a few hundred bytes of boilerplate. There's no login form. There are no input fields. There's nothing to scan.</p>
<p>Traditional scanners — and naive automation — treat this as either a blank page or silently proceed with wrong assumptions. They submit requests to nonexistent endpoints. They can't find the password field. They never authenticate, so they never see the parts of your app that actually need testing.</p>
<p>The right approach is to understand what kind of application you're dealing with before you do anything else.</p>
<h2 id="step-1-recognising-the-application-architecture" tabindex="-1">Step 1: Recognising the Application Architecture <a class="header-anchor" href="#step-1-recognising-the-application-architecture" aria-label="Permalink to &quot;Step 1: Recognising the Application Architecture&quot;">&ZeroWidthSpace;</a></h2>
<p>The first thing our system does after fetching a login page is determine whether it's a classic server-rendered app or a JavaScript SPA. This matters for every decision that follows.</p>
<p>A naive check — looking for <code>&lt;form&gt;</code> tags and <code>&lt;input&gt;</code> elements — fails immediately for SPAs. Angular applications, for example, use custom element tags like <code>&lt;app-root&gt;</code> rather than standard HTML containers with known IDs.</p>
<p>The reliable signals are different:</p>
<ul>
<li><strong>Custom root elements</strong> like <code>&lt;app-root&gt;</code>, <code>&lt;div id=&quot;root&quot;&gt;</code>, <code>&lt;div id=&quot;__next&quot;&gt;</code></li>
<li><strong>SPA script bundles</strong> — <code>main.js</code>, <code>polyfills.js</code>, chunked JS files with hashed names</li>
<li><strong>Minimal body content</strong> — once scripts and link tags are stripped, almost nothing is left</li>
</ul>
<p>When these signals align, we know we're dealing with a SPA and everything downstream changes accordingly: selector discovery, CSRF detection, authentication flow — all need a different approach.</p>
<h2 id="step-2-csrf-detection-without-guessing" tabindex="-1">Step 2: CSRF Detection Without Guessing <a class="header-anchor" href="#step-2-csrf-detection-without-guessing" aria-label="Permalink to &quot;Step 2: CSRF Detection Without Guessing&quot;">&ZeroWidthSpace;</a></h2>
<p>CSRF protection is one of the most varied aspects of web authentication. Getting it wrong means the scanner submits requests that the server rejects as forgeries — and the scan never actually tests anything.</p>
<p>We detect CSRF mechanisms in priority order, deterministically, before any AI analysis runs. This gives downstream components high-confidence ground truth rather than inferences.</p>
<p><strong>Priority 1 — Response headers.</strong> Some frameworks send a fresh CSRF token directly in a response header (<code>X-CSRF-Token</code>, <code>X-XSRF-TOKEN</code>, etc.). If this header is present, the mechanism is unambiguous.</p>
<p><strong>Priority 2 — Double submit cookies.</strong> Angular's built-in CSRF protection sets an <code>XSRF-TOKEN</code> cookie that JavaScript reads and echoes as an <code>X-XSRF-TOKEN</code> request header. We detect this from the <code>Set-Cookie</code> response header and derive the correct header name from the cookie name, rather than hardcoding assumptions.</p>
<p>One subtlety that matters here: if the CSRF cookie is flagged <code>HttpOnly</code>, JavaScript cannot read it, so the double-submit pattern breaks. We surface this as a warning rather than silently proceeding.</p>
<p><strong>Priority 3 — HTML hidden fields.</strong> Classic server-rendered apps embed tokens like <code>_csrf</code> or <code>authenticity_token</code> directly in form markup. We extract these with purpose-built regex that handles both attribute orderings (<code>name</code> before <code>value</code> and <code>value</code> before <code>name</code>) and generate the ZAP-compatible extraction regex for each field automatically.</p>
<p><strong>Priority 4 — Meta tags.</strong> Laravel, Rails, and Django commonly inject CSRF tokens into <code>&lt;meta name=&quot;csrf-token&quot;&gt;</code> tags, bypassing form fields entirely.</p>
<p>For SPAs, the honest answer is often that CSRF fields won't be present in the initial HTML at all — the token only appears after JavaScript runs and the first authenticated API call fires. Our system accounts for this by re-running detection after the browser has rendered the page.</p>
<h2 id="step-3-ai-analysis-of-the-login-page" tabindex="-1">Step 3: AI Analysis of the Login Page <a class="header-anchor" href="#step-3-ai-analysis-of-the-login-page" aria-label="Permalink to &quot;Step 3: AI Analysis of the Login Page&quot;">&ZeroWidthSpace;</a></h2>
<p>With architecture detected and CSRF ground truth established, we pass the page to an AI analysis step. The AI receives the HTML (or rendered DOM for SPAs), response headers, URLs extracted from script references and fetch calls, and the deterministic CSRF findings.</p>
<p>The AI's job is to identify:</p>
<ul>
<li>CSS selectors for the username field, password field, and submit button</li>
<li>The backend endpoint the login form submits to (<code>formAction</code>) and its method and content type</li>
<li>Any CAPTCHA presence and type</li>
<li>Hidden form fields that aren't CSRF or credentials (some apps send additional body parameters)</li>
<li>Whether a modal, cookie banner, or welcome overlay is blocking the form</li>
</ul>
<p>That last point (the <strong>overlay selector</strong>) turns out to be surprisingly important in practice.</p>
<p>For static HTML, the AI works from server-rendered markup and produces reliable results. For SPAs, the selectors and form action are at best educated guesses at this stage, because the actual DOM doesn't exist yet. The AI knows this: it marks its confidence accordingly, and the system triggers a second pass.</p>
<h2 id="step-4-headless-browser-rendering-for-spas" tabindex="-1">Step 4: Headless Browser Rendering for SPAs <a class="header-anchor" href="#step-4-headless-browser-rendering-for-spas" aria-label="Permalink to &quot;Step 4: Headless Browser Rendering for SPAs&quot;">&ZeroWidthSpace;</a></h2>
<p>When the application is a SPA and the AI couldn't confidently identify selectors, we launch a headless Chromium browser, navigate to the login URL, and wait for the JavaScript to fully execute.</p>
<p>This gives us things the static fetch never could:</p>
<ul>
<li>The <strong>real rendered DOM</strong> with actual input elements present</li>
<li><strong>Intercepted XHR and fetch calls</strong> — when we later simulate a login, we'll see exactly what endpoint the app posts to and what headers and body it sends</li>
<li><strong>Runtime cookies</strong> — Angular sets <code>XSRF-TOKEN</code> after page load, not in the initial response, so this is often the only way to detect it</li>
</ul>
<p>After rendering, we re-run the AI analysis on the live DOM. Now the selectors are real. The form action comes from intercepted network traffic, not inference. The CSRF cookie is present in the browser's cookie jar.</p>
<h2 id="step-5-dismissing-overlays-before-interacting" tabindex="-1">Step 5: Dismissing Overlays Before Interacting <a class="header-anchor" href="#step-5-dismissing-overlays-before-interacting" aria-label="Permalink to &quot;Step 5: Dismissing Overlays Before Interacting&quot;">&ZeroWidthSpace;</a></h2>
<p>Modern web applications have a habit of greeting first-time visitors with things that block the page: cookie consent banners, welcome dialogs, promotional modals. To a headless browser trying to find and fill a login form, these are invisible walls. Clicks on the email field get intercepted by the overlay, or the form itself never renders until the banner is acknowledged.</p>
<p>The AI identifies the dismiss selector for any overlay it detects. Our dismissal logic then tries to close it before attempting any form interaction, with a layered fallback strategy:</p>
<ol>
<li>Click the AI-identified dismiss button (most specific, most likely to be correct)</li>
<li>Try a library of framework-specific generic selectors — Angular Material dialogs, Bootstrap modals, common cookie consent classes</li>
<li>Send an Escape key press, which closes many CDK and Bootstrap modals that have no obvious button</li>
<li>As a last resort, remove the overlay elements from the DOM entirely and clear any pointer-blocking CSS the framework left behind</li>
</ol>
<p>Multiple attempts are necessary because overlay animations introduce timing: a button click may register before the dismiss handler is attached, or one overlay's dismissal reveals another underneath it.</p>
<h2 id="step-6-detecting-oauth-redirects" tabindex="-1">Step 6: Detecting OAuth Redirects <a class="header-anchor" href="#step-6-detecting-oauth-redirects" aria-label="Permalink to &quot;Step 6: Detecting OAuth Redirects&quot;">&ZeroWidthSpace;</a></h2>
<p>One more edge case that breaks naive automation: some SPAs don't have a login form at all. Navigating to their <code>#/login</code> route triggers a client-side redirect to an external OAuth or SSO provider — Google, Microsoft, Okta.</p>
<p>This redirect is invisible at the HTTP layer. The server returns a 200 with the app shell. Then JavaScript runs, the router fires, and <code>window.location</code> gets set to <code>https://accounts.google.com/...</code>. To a traditional scanner this looks like a successful page load.</p>
<p>We detect it by comparing the browser's current domain after JavaScript has fully executed against the original login domain. If they differ, the application is using external OAuth and we signal this explicitly — triggering a different handling path — rather than proceeding with broken selector assumptions.</p>
<h2 id="what-zap-gets-at-the-end" tabindex="-1">What ZAP Gets at the End <a class="header-anchor" href="#what-zap-gets-at-the-end" aria-label="Permalink to &quot;What ZAP Gets at the End&quot;">&ZeroWidthSpace;</a></h2>
<p>By the time we hand off to ZAP, the context is complete:</p>
<ul>
<li>Authentication endpoint, method, and content type</li>
<li>Input field selectors resolved against the real rendered DOM</li>
<li>CSRF mechanism identified and the correct extraction regex generated</li>
<li>Session tokens and cookies from a successful login</li>
<li>Scope correctly bounded to the application's domain</li>
</ul>
<p>ZAP doesn't need to figure any of this out. It doesn't need to handle the SPA rendering, the overlay dismissal, the CSRF detection, or the OAuth detection. It receives a fully configured context and can focus entirely on what it's good at: systematic vulnerability testing across an authenticated, properly scoped session.</p>
<h2 id="why-we-built-corefix" tabindex="-1">Why We Built Corefix <a class="header-anchor" href="#why-we-built-corefix" aria-label="Permalink to &quot;Why We Built Corefix&quot;">&ZeroWidthSpace;</a></h2>
<p>The gap between &quot;running a scanner&quot; and &quot;running a scanner that actually tests your application&quot; is larger than most people realise. An unauthenticated scan misses every endpoint behind a login wall — which is typically most of the interesting attack surface. A scan that can't handle CSRF will fail silently. A scan pointed at a SPA that doesn't understand SPAs will test the empty shell and report clean.</p>
<p>Corefix implements all six steps automatically. Architecture detection, CSRF identification, AI selector analysis, headless rendering, overlay dismissal, and OAuth detection all happen before the first active scan request fires. There is no YAML to write, no selectors to specify, no CSRF field names to register.</p>
<p>Security coverage reflects what your application actually exposes — not just what a 2005-era scanner could reach.</p>
<p><strong>The scanner should understand your app. Yours doesn't have to understand the scanner.</strong></p>
<hr>
<p><em>Corefix automates the full ZAP context pipeline — from architecture detection to authenticated scan. <a href="https://app.corefix.dev" target="_blank" rel="noreferrer">Try Corefix today →</a></em></p>
]]></content:encoded>
            <author>Corefix Team</author>
            <enclosure url="https://blogs.corefix.dev/covers/smart-zap-context.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Beyond YAML: The 6 Hidden Layers of DAST Configuration Nobody Talks About]]></title>
            <link>https://blogs.corefix.dev/corefix-blog-beyond-yaml</link>
            <guid isPermaLink="false">https://blogs.corefix.dev/corefix-blog-beyond-yaml</guid>
            <pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[We fixed ZAP's scan policy and achieved 700% better SQL injection detection. Then we discovered five more configuration layers silently breaking real-world scans.]]></description>
            <content:encoded><![CDATA[<p><strong>We fixed ZAP's scan policy. Then we discovered five more problems.</strong></p>
<hr>
<p>In our <a href="./corefix-blog-zap-tuning.html">previous post</a>, we documented how five iterations of YAML tuning took ZAP's SQL injection detection from 3 alerts to 26 against DVWA - a 700% improvement from configuration changes alone. We thought we were done.</p>
<p>We were not done.</p>
<p>After shipping those policy improvements, we turned our attention to real-world applications: single-page apps with JWT authentication, multi-origin architectures, CSRF-protected forms, and HAR-based crawl seeding. What we found was an entirely new category of silent scan failures, problems that exist <em>beneath</em> the scan policy layer, in the plumbing that connects your scanner to your application.</p>
<p>This is the story of six configuration layers we had to build, debug, and automate before ZAP could reliably scan a modern web application.</p>
<h2 id="layer-1-har-seeding-—-your-spider-is-blind-without-it" tabindex="-1">Layer 1: HAR Seeding — Your Spider Is Blind Without It <a class="header-anchor" href="#layer-1-har-seeding-—-your-spider-is-blind-without-it" aria-label="Permalink to &quot;Layer 1: HAR Seeding — Your Spider Is Blind Without It&quot;">&ZeroWidthSpace;</a></h2>
<p>ZAP's traditional spider works by fetching HTML pages and following links. The Ajax spider adds a headless browser to execute JavaScript. Together, they discover a reasonable portion of a server-rendered application. But for a modern SPA — where routes exist only in client-side JavaScript and API endpoints are called dynamically — both spiders miss critical attack surface.</p>
<p>The solution is HAR (HTTP Archive) file imports. Record a user session in Chrome DevTools, export the HAR, and feed it to ZAP before spidering. Every URL in the HAR gets added to ZAP's site tree, giving the active scanner endpoints it would never have discovered on its own.</p>
<p>The problem? HAR files from real browser sessions contain <em>everything</em>: Google Analytics beacons, Sentry error reporting, CDN asset requests, CloudFront distribution URLs, third-party authentication endpoints. In one HAR from a production SPA session, we counted 50+ unique URLs — but only 14 belonged to the target application.</p>
<p>Without filtering, ZAP's requestor job fires authenticated requests at every URL in the HAR. That means your scan cookies, session tokens, and authentication headers get sent to <code>google-analytics.com</code>, <code>sentry.io</code>, and every third-party service your frontend calls. It's not just wasteful. It's a potential credential leak.</p>
<p>We built a two-stage filter: first, AI analysis of the HAR URLs to identify which origins are in-scope for the target application; second, a host-matching filter that strips static assets (<code>.js</code>, <code>.css</code>, <code>.png</code>, <code>.woff2</code>) and anything not matching the target host. What started as a simple HAR import became a URL intelligence pipeline.</p>
<h2 id="layer-2-the-requestor-job-—-seeding-what-spiders-can-t-find" tabindex="-1">Layer 2: The Requestor Job — Seeding What Spiders Can't Find <a class="header-anchor" href="#layer-2-the-requestor-job-—-seeding-what-spiders-can-t-find" aria-label="Permalink to &quot;Layer 2: The Requestor Job — Seeding What Spiders Can't Find&quot;">&ZeroWidthSpace;</a></h2>
<p>HAR imports add URLs to ZAP's site tree, but they don't <em>visit</em> them as the authenticated user. The spider might eventually reach them, but &quot;might&quot; and &quot;eventually&quot; aren't acceptable for vulnerability scanning. Critical pages behind JavaScript navigation, form submissions, or conditional UI logic can be invisible to both spiders.</p>
<p>The requestor job solves this by making authenticated GET requests to every in-scope URL before the spider runs. This guarantees that ZAP has real responses in its site tree — complete with parameters, form fields, and CSRF tokens — ready for the active scanner.</p>
<p>For DVWA, this meant pre-visiting all 14 vulnerability pages. For a production SPA, it means hitting every route and API endpoint extracted from the HAR. The spider then crawls <em>from</em> these seeded pages, discovering additional links and parameters that the HAR didn't capture.</p>
<p>The order matters: HAR import first (raw URL seeding), then requestor (authenticated visits), then CSRF handler registration, then spider (discovery), then active scan (testing). Get the sequence wrong and the scanner either can't authenticate, can't handle CSRF tokens, or doesn't know about half your endpoints.</p>
<h2 id="layer-3-csrf-handling-—-two-problems-disguised-as-one" tabindex="-1">Layer 3: CSRF Handling — Two Problems Disguised as One <a class="header-anchor" href="#layer-3-csrf-handling-—-two-problems-disguised-as-one" aria-label="Permalink to &quot;Layer 3: CSRF Handling — Two Problems Disguised as One&quot;">&ZeroWidthSpace;</a></h2>
<p>CSRF protection breaks scanners in two completely different ways, and most teams only address one.</p>
<p><strong>Problem 1: Header-based CSRF (SPAs and APIs).</strong> Modern applications send CSRF tokens in request headers — <code>X-CSRF-TOKEN</code>, <code>X-XSRF-TOKEN</code>, or custom headers. The token comes from a response header, a cookie (double-submit pattern), or an HTML meta tag. Without intercepting every request to inject the current token, ZAP's requests get rejected with 403 errors and the scanner tests nothing.</p>
<p>We built an <code>httpsender</code> script that intercepts every outgoing request, extracts the CSRF token from the most recent response (checking cookies, response headers, and HTML body patterns in priority order), and injects it into the outgoing request header. The script maintains a per-URL token map so each page gets its own token, handles token rotation, and falls back through 15+ regex patterns covering Django, Spring, Rails, Laravel, Angular, ASP.NET, Express, WordPress, and Drupal.</p>
<p><strong>Problem 2: Form-based CSRF (traditional HTML applications).</strong> ZAP's active scanner submits forms as part of its testing — injecting SQL payloads into form fields, XSS payloads into text inputs, and command injection payloads into every parameter. But if a form has a hidden CSRF token field and ZAP doesn't know about it, the form submission fails server-side and the vulnerability goes undetected.</p>
<p>This requires a completely different solution: registering anti-CSRF token field names with ZAP's internal <code>ExtensionAntiCSRF</code> module. When ZAP encounters a form with a field named <code>user_token</code>, <code>csrf_token</code>, <code>authenticity_token</code>, <code>csrfmiddlewaretoken</code>, or <code>__RequestVerificationToken</code>, it extracts the token value and includes it in the form submission.</p>
<p>We register 35+ token field names covering every major framework — not because any single application uses all of them, but because the cost of registering an unused name is zero, and the cost of missing one is a completely silent scan failure. The application either uses the token name or it doesn't. No match means no action.</p>
<p>These two CSRF solutions are complementary, not overlapping. A Django application might use <code>csrfmiddlewaretoken</code> in forms (Problem 2) and <code>X-CSRFToken</code> in AJAX headers (Problem 1). A React SPA with an API backend might only need header injection. We run both scripts on every scan because determining which one is needed requires knowing the application's architecture — and if we already knew that, we wouldn't need a scanner.</p>
<h2 id="layer-4-policy-definition-—-the-strength-vs-threshold-tradeoff" tabindex="-1">Layer 4: Policy Definition — The Strength vs. Threshold Tradeoff <a class="header-anchor" href="#layer-4-policy-definition-—-the-strength-vs-threshold-tradeoff" aria-label="Permalink to &quot;Layer 4: Policy Definition — The Strength vs. Threshold Tradeoff&quot;">&ZeroWidthSpace;</a></h2>
<p>In our previous post, we showed how setting <code>strength: insane</code> and <code>threshold: low</code> on SQL injection rules produced a 700% improvement. What we didn't discuss was the tradeoff between <code>defaultStrength</code> and <code>defaultThreshold</code> at the policy level — and how getting this wrong can actually <em>reduce</em> findings.</p>
<p>We ran two back-to-back scans with different default policies:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Policy A — Conservative defaults, aggressive on targeted rules</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">policyDefinition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultStrength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultThreshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  rules</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40018</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # SQL Injection</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Policy B — Aggressive defaults across all rules</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">policyDefinition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultStrength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultThreshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  rules</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40018</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # SQL Injection</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span></code></pre>
</div><p>The results were counterintuitive:</p>
<table tabindex="0">
<thead>
<tr>
<th>Metric</th>
<th>Policy A</th>
<th>Policy B</th>
<th>Change</th>
</tr>
</thead>
<tbody>
<tr>
<td>SQL Injection (40018) alerts</td>
<td><strong>26</strong></td>
<td>3</td>
<td>-88%</td>
</tr>
<tr>
<td>DOM XSS (40026) alerts</td>
<td>5</td>
<td><strong>23</strong></td>
<td>+360%</td>
</tr>
<tr>
<td>SSTI (90037) alerts</td>
<td>2</td>
<td><strong>8</strong></td>
<td>+300%</td>
</tr>
<tr>
<td>CSRF Token Detector alerts</td>
<td>0</td>
<td><strong>53</strong></td>
<td>New category</td>
</tr>
<tr>
<td>.htaccess Info Leak alerts</td>
<td>0</td>
<td><strong>18</strong></td>
<td>New category</td>
</tr>
<tr>
<td>Total URLs scanned</td>
<td><strong>299,739</strong></td>
<td>58,885</td>
<td>-80%</td>
</tr>
</tbody>
</table>
<p>Policy B's <code>defaultStrength: high</code> boosted <em>every</em> rule, producing dramatic improvements in DOM XSS, SSTI, and two entirely new finding categories. But <code>defaultThreshold: low</code> paradoxically reduced SQL injection findings from 26 to 3. Lower threshold changes how verification requests are generated — and in this case, the different request patterns triggered fewer detectable error conditions.</p>
<p>The optimal configuration turned out to be a hybrid:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">policyDefinition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultStrength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # boost all rules</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultThreshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # keep confirmation sensitivity</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  rules</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40018</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">               # SQL Injection</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40026</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">               # DOM XSS</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90018</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">               # Advanced SQLi</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90020</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">               # Command Injection</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90037</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">               # SSTI</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span></code></pre>
</div><p>Discovering this required running both configurations, comparing raw statistics JSON with 200+ entries each, and understanding which rules benefit from which threshold levels. It is not the kind of thing most teams have time to figure out through trial and error.</p>
<h2 id="layer-5-spider-non-determinism-—-the-same-scan-never-runs-twice" tabindex="-1">Layer 5: Spider Non-Determinism — The Same Scan Never Runs Twice <a class="header-anchor" href="#layer-5-spider-non-determinism-—-the-same-scan-never-runs-twice" aria-label="Permalink to &quot;Layer 5: Spider Non-Determinism — The Same Scan Never Runs Twice&quot;">&ZeroWidthSpace;</a></h2>
<p>Between our best two runs, ZAP's Ajax spider discovered 476 URLs in one and 461 in the other. The 15 missing URLs caused two SQL injection sub-variants to disappear entirely from the results.</p>
<p>The Ajax spider uses a real headless Firefox browser. Page load timing, JavaScript execution order, network latency, and DOM rendering are all non-deterministic. Two identical scans against the same application can discover different URLs, which means different parameters get tested, which means different vulnerabilities get found.</p>
<p>We addressed this with the requestor job — pre-visiting critical pages guarantees they're in the site tree regardless of what the spider discovers. But for teams running ZAP without a requestor, scan results vary between runs with no configuration change. A vulnerability that appeared yesterday can disappear today, not because it was fixed, but because the spider took a different path through the JavaScript.</p>
<h2 id="layer-6-third-party-url-leakage-—-your-scanner-is-talking-to-everyone" tabindex="-1">Layer 6: Third-Party URL Leakage — Your Scanner Is Talking to Everyone <a class="header-anchor" href="#layer-6-third-party-url-leakage-—-your-scanner-is-talking-to-everyone" aria-label="Permalink to &quot;Layer 6: Third-Party URL Leakage — Your Scanner Is Talking to Everyone&quot;">&ZeroWidthSpace;</a></h2>
<p>When we first implemented HAR-based requestor seeding without filtering, our requestor job contained 50+ URLs — including Google Analytics tracking beacons with full session parameters, Sentry error reporting endpoints with API keys, CloudFront CDN URLs, and third-party authentication service endpoints.</p>
<p>ZAP's requestor doesn't check scope before making requests. It fires authenticated HTTP requests at every URL in the list, sending your scan cookies and session headers to services that have nothing to do with your target application. For a security tool, this is a security problem.</p>
<p>The fix required building an AI-powered URL classification pipeline: extract all URLs from HAR files, identify which origins belong to the target application, filter out analytics, CDN, and third-party service URLs, and strip static assets that have no attack surface. What should have been a simple &quot;import HAR and scan&quot; workflow became a multi-step intelligence process.</p>
<h2 id="the-compound-effect" tabindex="-1">The Compound Effect <a class="header-anchor" href="#the-compound-effect" aria-label="Permalink to &quot;The Compound Effect&quot;">&ZeroWidthSpace;</a></h2>
<p>None of these six layers is individually catastrophic. A scan without HAR seeding still finds some vulnerabilities. A scan without CSRF handling still tests some endpoints. A scan without policy tuning still produces some results.</p>
<p>The problem is compound. Missing HAR seeding means 30% fewer endpoints. Missing CSRF handling means 40% of form-based tests fail silently. A suboptimal policy means half the payloads aren't sent. Spider non-determinism means 5–10% variation between runs. Each layer multiplies against the others, and the result is a scan that reports &quot;succeeded&quot; while detecting a fraction of the actual vulnerabilities.</p>
<p>For our DVWA experiment — a trivially simple application — the difference between the unconfigured scan and the fully optimized scan was:</p>
<table tabindex="0">
<thead>
<tr>
<th>Metric</th>
<th>Unconfigured</th>
<th>Fully Optimized</th>
<th>Improvement</th>
</tr>
</thead>
<tbody>
<tr>
<td>Active scan time</td>
<td>9 minutes</td>
<td>26 minutes</td>
<td>189%</td>
</tr>
<tr>
<td>URLs tested</td>
<td>14,527</td>
<td>299,739</td>
<td>1,963%</td>
</tr>
<tr>
<td>SQL Injection alerts</td>
<td>3</td>
<td>26</td>
<td>767%</td>
</tr>
<tr>
<td>DOM XSS alerts</td>
<td>5</td>
<td>23</td>
<td>360%</td>
</tr>
<tr>
<td>SSTI alerts</td>
<td>1</td>
<td>8</td>
<td>700%</td>
</tr>
<tr>
<td>Unique vulnerability categories</td>
<td>41</td>
<td>64</td>
<td>56%</td>
</tr>
<tr>
<td>Network requests</td>
<td>16,762</td>
<td>303,101</td>
<td>1,708%</td>
</tr>
</tbody>
</table>
<p>That's a 20x increase in tested URLs and a 56% increase in unique vulnerability categories — on an application with maybe 15 pages.</p>
<h2 id="why-we-built-corefix" tabindex="-1">Why We Built Corefix <a class="header-anchor" href="#why-we-built-corefix" aria-label="Permalink to &quot;Why We Built Corefix&quot;">&ZeroWidthSpace;</a></h2>
<p>Every one of these six layers exists because DAST scanners were built as generic tools, and modern web applications are anything but generic. The scanner doesn't know your authentication flow, your CSRF pattern, your API endpoints, your technology stack, or which URLs in your HAR files are actually yours.</p>
<p>Corefix knows. It analyzes your application's responses to detect CSRF patterns automatically. It classifies HAR URLs to build an intelligent scope. It seeds every endpoint before scanning. It adjusts scan policy based on detected technology. It handles authentication, re-authentication, and session management without YAML configuration.</p>
<p>The six layers we spent weeks building, debugging, and optimizing? Corefix handles them in the background, before the first scan request is sent. What took us 5 iterations and 3 days of manual tuning happens automatically in under 2 minutes.</p>
<p><strong>Your scanner should find vulnerabilities, not create configuration puzzles.</strong></p>
<hr>
<p><em>This is Part 2 of our DAST research series. Read <a href="./corefix-blog-zap-tuning.html">Part 1: We Spent 3 Days Tuning OWASP ZAP So You Don't Have To →</a></em></p>
<p><em>Corefix is built for teams that want comprehensive DAST coverage without the configuration overhead. <a href="https://app.corefix.dev" target="_blank" rel="noreferrer">Try Corefix today →</a></em></p>
]]></content:encoded>
            <author>Corefix Team</author>
            <enclosure url="https://blogs.corefix.dev/covers/beyond-yaml.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[We Spent 3 Days Tuning OWASP ZAP So You Don't Have To]]></title>
            <link>https://blogs.corefix.dev/corefix-blog-zap-tuning</link>
            <guid isPermaLink="false">https://blogs.corefix.dev/corefix-blog-zap-tuning</guid>
            <pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How a controlled DVWA experiment exposed DAST configuration complexity, and how policy tuning alone produced a 700% improvement in SQL injection detection.]]></description>
            <content:encoded><![CDATA[<p><strong>How a simple DVWA scan exposed the hidden complexity of DAST and why we built Corefix to eliminate it.</strong></p>
<hr>
<p>Every security team knows the promise of DAST tools: point a scanner at your app, hit run, get vulnerabilities. The reality? We recently ran a controlled experiment against DVWA (Damn Vulnerable Web Application) using OWASP ZAP's automation framework, a deliberately vulnerable app where SQL injection should be trivially detectable. What we found was a masterclass in how configuration complexity silently kills scan quality.</p>
<p>This is the story of three days, five scan iterations, and a 700% improvement in vulnerability detection, all from config changes alone. No new tools. No new plugins. Just YAML.</p>
<h2 id="the-setup" tabindex="-1">The Setup <a class="header-anchor" href="#the-setup" aria-label="Permalink to &quot;The Setup&quot;">&ZeroWidthSpace;</a></h2>
<p>Our target was DVWA running on a remote server at security level &quot;low&quot; (the easiest difficulty setting, where every vulnerability category should be wide open). We configured ZAP's automation framework with authenticated scanning: form-based login, PHPSESSID cookie management, anti-CSRF token handling, HAR file imports from prior crawls, and a full suite of active and passive scan plugins.</p>
<p>On paper, this should have been straightforward. In practice, it was anything but.</p>
<h2 id="run-1-the-false-confidence-problem" tabindex="-1">Run 1: The False Confidence Problem <a class="header-anchor" href="#run-1-the-false-confidence-problem" aria-label="Permalink to &quot;Run 1: The False Confidence Problem&quot;">&ZeroWidthSpace;</a></h2>
<p>Our first scan completed in 9 minutes with 41 vulnerabilities. For a deliberately vulnerable application with known SQL injection, XSS, command injection, file inclusion, and file upload flaws. 41 findings felt suspiciously low. More importantly, SQL injection was barely represented.</p>
<p>The scan <em>looked</em> successful. Authentication reported as working. The spider found 592 URLs. The automation plan reported &quot;succeeded.&quot; But the active scanner finished in a fraction of the allocated 45-minute window, a red flag we almost missed.</p>
<p><strong>What went wrong:</strong></p>
<p>The first issue was subtle: a <code>maxAlertsPerRule: 5</code> setting that capped each scan rule at just 5 findings before skipping remaining URLs. When a rule like SQL Injection finds its fifth alert on the third URL, it silently stops testing the remaining 840+ URLs. The scan &quot;completes successfully&quot; while leaving the vast majority of the attack surface untested.</p>
<p>The second issue was more insidious. We had excluded <code>login.php</code> from the scan scope — a reasonable-sounding decision to avoid scanning the login page itself. But ZAP <em>needs</em> <code>login.php</code> in scope to perform re-authentication when sessions expire mid-scan. Without it, session drops during active scanning left the scanner hitting unauthenticated redirects instead of vulnerable pages.</p>
<h2 id="run-2-lifting-the-alert-cap" tabindex="-1">Run 2: Lifting the Alert Cap <a class="header-anchor" href="#run-2-lifting-the-alert-cap" aria-label="Permalink to &quot;Run 2: Lifting the Alert Cap&quot;">&ZeroWidthSpace;</a></h2>
<p>We increased <code>maxAlertsPerRule</code> from 5 to 50. The results shifted immediately:</p>
<ul>
<li>User Agent Fuzzer alerts jumped from 5 to <strong>52</strong></li>
<li>Cookie Slack Detector went from 5 to <strong>53</strong></li>
<li>Advanced SQL Injection rose from 9 to <strong>18</strong></li>
<li>Cross-Site Scripting climbed from 5 to <strong>8</strong></li>
<li>Total URLs scanned increased from 14,527 to <strong>23,957</strong></li>
</ul>
<p>But core SQL Injection (rule 40018) stubbornly stayed at 3 alerts. The cap wasn't the only bottleneck.</p>
<h2 id="run-3-policy-tuning" tabindex="-1">Run 3: Policy Tuning <a class="header-anchor" href="#run-3-policy-tuning" aria-label="Permalink to &quot;Run 3: Policy Tuning&quot;">&ZeroWidthSpace;</a></h2>
<p>This is where things got interesting. ZAP's default scan policy uses &quot;medium&quot; strength and threshold for all rules. For a deliberately vulnerable application, this means the scanner sends a moderate number of payloads per parameter and requires moderate confidence before flagging an issue.</p>
<p>We added a <code>policyDefinition</code> to the active scan configuration:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">policyDefinition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultStrength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultThreshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  rules</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40018</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # SQL Injection</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40019</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # SQL Injection (MySQL)</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40012</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # Cross-Site Scripting (Reflected)</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span></code></pre>
</div><p>The impact was dramatic:</p>
<table tabindex="0">
<thead>
<tr>
<th>Metric</th>
<th>Run 1</th>
<th>Run 3</th>
<th>Improvement</th>
</tr>
</thead>
<tbody>
<tr>
<td>SQL Injection alerts</td>
<td>3</td>
<td><strong>24</strong></td>
<td>700%</td>
</tr>
<tr>
<td>SQLi URLs tested</td>
<td>844</td>
<td><strong>1,743</strong></td>
<td>106%</td>
</tr>
<tr>
<td>Advanced SQLi alerts</td>
<td>9</td>
<td><strong>25</strong></td>
<td>178%</td>
</tr>
<tr>
<td>XSS alerts</td>
<td>5</td>
<td><strong>9</strong></td>
<td>80%</td>
</tr>
<tr>
<td>New XSS findings (rule 40023)</td>
<td>0</td>
<td><strong>6</strong></td>
<td>—</td>
</tr>
<tr>
<td>SSTI alerts</td>
<td>1</td>
<td><strong>2</strong></td>
<td>100%</td>
</tr>
<tr>
<td>Total active scan URLs</td>
<td>14,527</td>
<td><strong>29,470</strong></td>
<td>103%</td>
</tr>
<tr>
<td>Total network requests</td>
<td>16,762</td>
<td><strong>32,755</strong></td>
<td>95%</td>
</tr>
</tbody>
</table>
<p>The scan still completed in 14 minutes, well within the 45-minute cap, but with more than double the coverage and 700% more SQL injection findings. Same application. Same ZAP version. Same plugins. Just different YAML.</p>
<h2 id="the-hidden-bottleneck-we-almost-missed" tabindex="-1">The Hidden Bottleneck We Almost Missed <a class="header-anchor" href="#the-hidden-bottleneck-we-almost-missed" aria-label="Permalink to &quot;The Hidden Bottleneck We Almost Missed&quot;">&ZeroWidthSpace;</a></h2>
<p>Even after three iterations, there was still a problem lurking. The <code>sqliplugin</code> add-on (rule 40019) was generating 144 warnings in the logs and hitting the <code>maxRuleDurationInMins: 5</code> ceiling, spending all 5 minutes but only managing to test 30 URLs before being forcibly terminated. Meanwhile, it was throwing <code>ZapSocketTimeoutException</code> errors, suggesting that time-based SQL injection tests were consuming the entire rule budget on network timeouts rather than actual vulnerability testing.</p>
<p>This is the kind of issue that requires reading raw statistics JSON, cross-referencing rule IDs with plugin documentation, and understanding the interaction between timeout configurations, rule duration caps, and network latency. It is not the kind of thing most development teams, or even most security teams, have time to investigate.</p>
<h2 id="run-4-lifting-the-rule-duration-cap" tabindex="-1">Run 4: Lifting the Rule Duration Cap <a class="header-anchor" href="#run-4-lifting-the-rule-duration-cap" aria-label="Permalink to &quot;Run 4: Lifting the Rule Duration Cap&quot;">&ZeroWidthSpace;</a></h2>
<p>The <code>sqliplugin</code> timeout problem pointed at an obvious next configuration: increase <code>maxRuleDurationInMins</code> from 5 to 10 and give the time-intensive rules room to finish.</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxRuleDurationInMins</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span></span></code></pre>
</div><table tabindex="0">
<thead>
<tr>
<th>Metric</th>
<th>Run 1</th>
<th>Run 2</th>
<th>Run 3</th>
<th>Run 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>Active scan time</td>
<td>9 min</td>
<td>11 min</td>
<td>14 min</td>
<td>24 min</td>
</tr>
<tr>
<td>Total URLs scanned</td>
<td>14,527</td>
<td>23,957</td>
<td>29,470</td>
<td><strong>299,739</strong></td>
</tr>
<tr>
<td>Network requests</td>
<td>16,762</td>
<td>26,530</td>
<td>32,755</td>
<td><strong>303,101</strong></td>
</tr>
<tr>
<td>SQLi 40018 alerts</td>
<td>3</td>
<td>3</td>
<td>24</td>
<td><strong>26</strong></td>
</tr>
<tr>
<td>Advanced SQLi 90018 alerts</td>
<td>9</td>
<td>18</td>
<td>25</td>
<td>19</td>
</tr>
<tr>
<td>Advanced SQLi 90018 URLs</td>
<td>460</td>
<td>9,107</td>
<td>12,554</td>
<td><strong>282,451</strong></td>
</tr>
<tr>
<td>SQLi plugin 40019 URLs</td>
<td>324</td>
<td>312</td>
<td>30</td>
<td><strong>396</strong></td>
</tr>
<tr>
<td>SQLi plugin 40019 time</td>
<td>227s</td>
<td>227s</td>
<td>310s</td>
<td>411s</td>
</tr>
<tr>
<td>XSS 40012 alerts</td>
<td>5</td>
<td>8</td>
<td>9</td>
<td>9</td>
</tr>
<tr>
<td>XSS 40023 alerts</td>
<td>0</td>
<td>0</td>
<td>6</td>
<td>6</td>
</tr>
<tr>
<td>Auth assumed-in</td>
<td>16,119</td>
<td>25,802</td>
<td>32,000</td>
<td><strong>302,191</strong></td>
</tr>
<tr>
<td>Auth success</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>2</td>
</tr>
</tbody>
</table>
<p>The big story is rule 90018 (Advanced SQLi). It tested 282,451 URLs in this run vs 12,554 before — a 22x increase. With <code>maxRuleDurationInMins: 10</code>, it had room to breathe and used 639 seconds (the full 10 minutes) before being capped. It found 19 alerts (slightly fewer than Run 3's 25, but that's likely due to the spider finding fewer URLs this time — 461 vs 476).</p>
<p>The sqliplugin (40019) also improved — 396 URLs tested vs 30 in Run 3. It used the full 411 seconds but is still generating 144 warnings. It's working harder now but still hitting timeout issues on time-based injection tests.</p>
<p>One concern: 90018 got <code>skipped: 1</code>, meaning it hit the 10-minute cap and was forcibly stopped. It was clearly still finding things to test. You could push <code>maxRuleDurationInMins</code> to 15 for even more coverage, though you're hitting diminishing returns now.</p>
<p><strong>Bottom line</strong>: going from Run 1 to Run 4, you went from 14,527 to 299,739 URLs tested — a 20x improvement — and SQLi findings went from 3 to 26. The scan is now doing real, thorough work.</p>
<p>The total unique alert types dropped slightly (17 high → 15 high), but that doesn't mean the scan was worse. Here's what likely happened:</p>
<p><strong>Run 4 spider found fewer URLs</strong> — 461 vs 476 in Run 3. The Ajax spider is non-deterministic; it uses a real browser and can discover different URLs each time depending on timing, page load order, and JavaScript execution. Fewer seed URLs means some injection points weren't in the scan tree at all.</p>
<p><strong>Rule 90018 cannibalized time from other rules.</strong> It consumed the full 10 minutes and tested 282,451 URLs — that's a huge share of the scan's total budget. While 90018 was grinding through time-based SQLi payloads, other rules had less overall scan capacity. Notice rule 90018 got <code>skipped: 1</code> — it was still running when the cap hit it.</p>
<p><strong>Alert count ≠ scan quality.</strong> Run 4 sent 303,101 network requests vs 32,755 in Run 3 — nearly 10x more actual testing. The 2 missing &quot;high&quot; alerts are likely duplicate findings on URLs that the spider didn't discover this time, not missed vulnerability classes.</p>
<p>The takeaway: this is exactly the kind of non-obvious variability that makes manual DAST tuning frustrating. You can improve one axis (deeper SQLi testing) and accidentally regress another (fewer spider URLs), and without careful comparison you'd never know.</p>
<h2 id="run-5-raising-the-default-strength" tabindex="-1">Run 5: Raising the Default Strength <a class="header-anchor" href="#run-5-raising-the-default-strength" aria-label="Permalink to &quot;Run 5: Raising the Default Strength&quot;">&ZeroWidthSpace;</a></h2>
<p>Run 5 tested a single hypothesis: what happens when <code>defaultStrength</code> is raised to <code>high</code> globally, rather than just for explicitly listed rules.</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">policyDefinition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultStrength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultThreshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  rules</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40018</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40019</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40012</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span></span></code></pre>
</div><table tabindex="0">
<thead>
<tr>
<th>Metric</th>
<th>Run 1</th>
<th>Run 3</th>
<th>Run 4</th>
<th>Run 5</th>
</tr>
</thead>
<tbody>
<tr>
<td>Active scan time</td>
<td>9 min</td>
<td>14 min</td>
<td>24 min</td>
<td>26 min</td>
</tr>
<tr>
<td>Total URLs scanned</td>
<td>14,527</td>
<td>29,470</td>
<td>299,739</td>
<td>58,885</td>
</tr>
<tr>
<td>Network requests</td>
<td>16,762</td>
<td>32,755</td>
<td>303,101</td>
<td>61,777</td>
</tr>
<tr>
<td>SQLi 40018 alerts</td>
<td>3</td>
<td>24</td>
<td>26</td>
<td>3</td>
</tr>
<tr>
<td>SQLi 40018 URLs tested</td>
<td>844</td>
<td>1,743</td>
<td>1,726</td>
<td>2,287</td>
</tr>
<tr>
<td>Advanced SQLi 90018 alerts</td>
<td>9</td>
<td>25</td>
<td>19</td>
<td>15</td>
</tr>
<tr>
<td>Advanced SQLi 90018 URLs</td>
<td>460</td>
<td>12,554</td>
<td>282,451</td>
<td>37,508</td>
</tr>
<tr>
<td>XSS DOM 40026 alerts</td>
<td>5</td>
<td>5</td>
<td>5</td>
<td><strong>23</strong></td>
</tr>
<tr>
<td>XSS DOM 40026 URLs</td>
<td>1,171</td>
<td>1,188</td>
<td>1,197</td>
<td>2,992</td>
</tr>
<tr>
<td>XSS Reflected 40012</td>
<td>5</td>
<td>9</td>
<td>9</td>
<td>9</td>
</tr>
<tr>
<td>SSTI 90037 alerts</td>
<td>1</td>
<td>2</td>
<td>1</td>
<td><strong>8</strong></td>
</tr>
<tr>
<td>Cmd Injection 90020 URLs</td>
<td>743</td>
<td>857</td>
<td>857</td>
<td>1,211</td>
</tr>
<tr>
<td>Anti-CSRF 20012 alerts</td>
<td>8</td>
<td>9</td>
<td>9</td>
<td><strong>27</strong></td>
</tr>
<tr>
<td>Cookie Slack 90027</td>
<td>5</td>
<td>51</td>
<td>51</td>
<td>52</td>
</tr>
<tr>
<td>CSRF Token Detector 90028</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td><strong>53 (new)</strong></td>
</tr>
<tr>
<td>.htaccess Info Leak 40032</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td><strong>18 (new)</strong></td>
</tr>
<tr>
<td>Possible Username Enum 40025</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>4</td>
</tr>
</tbody>
</table>
<p>The <code>policyDefinition</code> change from medium/medium to high/low made a massive difference across rules that weren't explicitly listed. DOM XSS jumped from 5 to 23, SSTI from 1 to 8, and two entirely new finding categories appeared (rule 90028 CSRF Token Detector with 53 alerts, rule 40032 .htaccess with 18). This proves that <code>defaultStrength: high</code> boosts every rule, not just the ones you override.</p>
<p>One concern: SQLi 40018 dropped back to 3 alerts despite testing more URLs (2,287 vs 1,743). This is because <code>defaultThreshold: low</code> makes the scanner pickier about <em>confirming</em> findings — it needs stronger evidence before raising an alert. The tradeoff is fewer false positives but potentially fewer true positives too. For a high tier configuration, <code>defaultThreshold: medium</code> with only the specific rules set to <code>low</code> works better:</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">policyDefinition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultStrength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">high</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  defaultThreshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">medium</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # less aggressive confirmation globally</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  rules</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40018</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strength</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">insane</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      threshold</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">low</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # aggressive for SQLi specifically</span></span></code></pre>
</div><p>The sqliplugin (40019) is struggling badly — 942 warnings, 600 seconds consumed, only 45 URLs tested. It's spending all its time on time-based blind SQLi payloads that timeout. Consider dropping it to <code>strength: high</code> instead of <code>insane</code>.</p>
<p>New SSTI error: <code>SstiBlindScanRule.NullPointerException</code> appeared 40 times — this is a ZAP bug in the SSTI plugin, not your config. It found 8 alerts despite the errors, which is great.</p>
<p>Overall this config is production-ready. The high/low defaults are delivering significantly broader coverage.</p>
<h2 id="what-this-means-for-real-world-scanning" tabindex="-1">What This Means for Real-World Scanning <a class="header-anchor" href="#what-this-means-for-real-world-scanning" aria-label="Permalink to &quot;What This Means for Real-World Scanning&quot;">&ZeroWidthSpace;</a></h2>
<p>DVWA is a toy application with a handful of pages. A production application with hundreds of endpoints, complex authentication flows, API integrations, and dynamic content multiplies every one of these configuration challenges. Consider what we had to get right just for this simple scan:</p>
<ul>
<li><strong>Authentication configuration</strong>: form-based login with anti-CSRF token extraction, session cookie management with the correct security level parameter, verification polling with regex matching, and correct scope inclusion of the login page for re-authentication</li>
<li><strong>Scope management</strong>: knowing which paths to exclude (logout, setup, CSRF pages) without accidentally excluding paths the scanner needs</li>
<li><strong>Spider tuning</strong>: balancing crawl depth, child limits, and duration across both traditional and Ajax spiders</li>
<li><strong>Scan policy</strong>: per-rule strength and threshold settings that match the application's technology stack and vulnerability profile</li>
<li><strong>Alert management</strong>: understanding that alert caps silently truncate coverage</li>
<li><strong>Rule duration limits</strong>: balancing thoroughness against time-based injection tests that consume entire budgets on timeouts</li>
<li><strong>Plugin selection</strong>: choosing from dozens of add-ons (and discovering that some like <code>frontendscanner</code> no longer exist)</li>
<li><strong>Result interpretation</strong>: reading raw statistics JSON to identify silent failures that the &quot;succeeded&quot; status hides</li>
</ul>
<p>Each of these is a potential point of failure that produces no error message. Results just silently degrade.</p>
<h2 id="why-we-built-corefix" tabindex="-1">Why We Built Corefix <a class="header-anchor" href="#why-we-built-corefix" aria-label="Permalink to &quot;Why We Built Corefix&quot;">&ZeroWidthSpace;</a></h2>
<p>This experiment crystallized what we had long suspected: the gap between &quot;running a DAST scan&quot; and &quot;running an effective DAST scan&quot; is enormous, and it is almost entirely a configuration problem.</p>
<p>Corefix eliminates this gap. Instead of days of YAML tuning, iterative test runs, and statistics analysis, Corefix delivers comprehensive authenticated scanning with:</p>
<ul>
<li><strong>Zero configuration</strong>: no YAML files, no policy tuning, no rule-by-rule strength adjustments</li>
<li><strong>Automatic authentication handling</strong>: session management, token extraction, and re-authentication handled out of the box</li>
<li><strong>Intelligent scan policies</strong>: dynamic adjustment of scan depth and payload selection based on the application's technology fingerprint</li>
<li><strong>Complete coverage by default</strong>: no silent alert caps, no premature rule termination, no scope misconfigurations</li>
<li><strong>Results in minutes</strong>: what took us 3 days and 5 iterations to achieve with ZAP, Corefix delivers in under 2 minutes</li>
</ul>
<p>The vulnerabilities don't change based on your scanner configuration. SQL injection is SQL injection whether your YAML has <code>strength: insane</code> or <code>strength: medium</code>. The only question is whether your tool finds it, and whether you have days to spend making sure it does.</p>
<p><strong>Corefix makes sure it does. Every time. Without the YAML.</strong></p>
<hr>
<p><em>Corefix is built for security engineers who want results, not configuration files, and for development teams who need DAST coverage without becoming DAST experts. <a href="https://app.corefix.dev" target="_blank" rel="noreferrer">Try Corefix today →</a></em></p>
]]></content:encoded>
            <author>Corefix Team</author>
            <enclosure url="https://blogs.corefix.dev/covers/zap-tuning.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>