{"id":2624,"date":"2026-06-02T03:22:32","date_gmt":"2026-06-02T03:22:32","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/06\/02\/i-built-an-open-source-dast-scanner-that-outfound-zap\/"},"modified":"2026-06-02T03:22:32","modified_gmt":"2026-06-02T03:22:32","slug":"i-built-an-open-source-dast-scanner-that-outfound-zap","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/06\/02\/i-built-an-open-source-dast-scanner-that-outfound-zap\/","title":{"rendered":"I built an open-source DAST scanner that outfound ZAP"},"content":{"rendered":"<div>\n<div>\n<div data-article-id=\"3799640\" id=\"article-body\">\n<p>I built KageSec.<\/p>\n<p><a href=\"https:\/\/media2.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6d0yd1jg054xm3t19cm.png\"><\/a><\/p>\n<hr>\n<h2> <a name=\"whats-wrong-with-existing-dast-tools\" href=\"#whats-wrong-with-existing-dast-tools\"> <\/a> What&#8217;s wrong with existing DAST tools <\/h2>\n<p><strong>Nuclei<\/strong> is great \u2014 ProjectDiscovery built something genuinely impressive. But it&#8217;s a template-matching engine, not a DAST scanner. It hits the root URL, matches YAML patterns and reports findings. It does not crawl your app, discover parameters, or inject payloads into forms. The companies charging enterprise pricing for &#8220;Nuclei as a service&#8221; are essentially charging you for a UI on top of a YAML runner.<\/p>\n<p><strong>ZAP<\/strong> is the other go-to. It crawls. It injects. But it generates a lot of noise, misses logic-layer vulnerabilities, and has no AI filtering step to tell you which findings are actually exploitable.<\/p>\n<p>The gap is: something that crawls like ZAP, runs templates like Nuclei, and uses AI to cut the noise.<\/p>\n<hr>\n<h2> <a name=\"the-benchmark\" href=\"#the-benchmark\"> <\/a> The benchmark <\/h2>\n<p>I tested against <a href=\"https:\/\/ginandjuice.shop\/\" target=\"_blank\" rel=\"noopener noreferrer\">ginandjuice.shop<\/a> \u2014 <\/p>\n<p>Here&#8217;s what each tool found:<\/p>\n<div>\n<table>\n<thead>\n<tr>\n<th><\/th>\n<th>KageSec<\/th>\n<th>ZAP<\/th>\n<th>Nuclei (standalone)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Total findings<\/strong><\/td>\n<td><strong>21<\/strong><\/td>\n<td><strong>7<\/strong><\/td>\n<td><strong>12<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Critical<\/td>\n<td>4<\/td>\n<td>0<\/td>\n<td>0<\/td>\n<\/tr>\n<tr>\n<td>High<\/td>\n<td>4<\/td>\n<td>4<\/td>\n<td>0<\/td>\n<\/tr>\n<tr>\n<td>Medium<\/td>\n<td>5<\/td>\n<td>3<\/td>\n<td>0<\/td>\n<\/tr>\n<tr>\n<td>Low \/ Info<\/td>\n<td>8<\/td>\n<td>0<\/td>\n<td>12<\/td>\n<\/tr>\n<tr>\n<td>Scan time<\/td>\n<td>10m 22s<\/td>\n<td>~25 min<\/td>\n<td>6m<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>Nuclei&#8217;s 12 findings were all INFO \u2014 missing HTTP headers on the root URL. It found zero actual vulnerabilities because it never crawled the app or injected anything. That&#8217;s not a criticism of Nuclei. It&#8217;s just not what it&#8217;s for.<\/p>\n<h3> <a name=\"vulnerability-breakdown\" href=\"#vulnerability-breakdown\"> <\/a> Vulnerability breakdown <\/h3>\n<div>\n<table>\n<thead>\n<tr>\n<th>Vulnerability<\/th>\n<th>KageSec<\/th>\n<th>ZAP<\/th>\n<th>Nuclei<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>OS Command Injection<\/td>\n<td>\u2705 CRITICAL<\/td>\n<td>\u2717<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>XML External Entity (XXE)<\/td>\n<td>\u2705 CRITICAL<\/td>\n<td>\u2717<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>AngularJS CSTI<\/td>\n<td>\u2705 CRITICAL<\/td>\n<td>\u2717<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>DOM-Based XSS<\/td>\n<td>\u2705 HIGH<\/td>\n<td>\u2705 HIGH<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>Reflected XSS<\/td>\n<td>\u2705 HIGH<\/td>\n<td>\u2705 HIGH<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>SSI Injection<\/td>\n<td>\u2705 HIGH<\/td>\n<td>\u2717<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>SQL Injection<\/td>\n<td>\u2717<\/td>\n<td>\u2705 HIGH<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>Missing CSRF Protection<\/td>\n<td>\u2705 MEDIUM<\/td>\n<td>\u2705 MEDIUM<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<tr>\n<td>Business Logic<\/td>\n<td>\u2705 MEDIUM<\/td>\n<td>\u2717<\/td>\n<td>\u2717<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>KageSec missed SQL injection. ZAP missed OS command injection and XXE. Neither tool is complete \u2014 that&#8217;s an honest benchmark.<\/p>\n<hr>\n<h2> <a name=\"how-it-works\" href=\"#how-it-works\"> <\/a> How it works <\/h2>\n<h3> <a name=\"1-crawl-first-scan-everything-found\" href=\"#1-crawl-first-scan-everything-found\"> <\/a> 1. Crawl first, scan everything found <\/h3>\n<p>KageSec uses Playwright to crawl the app like a real browser. JavaScript rendered, SPAs handled, forms discovered. Every page found becomes a scan target. Nuclei never does this.<\/p>\n<h3> <a name=\"2-61-exploitation-modules-per-page\" href=\"#2-61-exploitation-modules-per-page\"> <\/a> 2. 61 exploitation modules per page <\/h3>\n<p>For each page, KageSec runs 61 vulnerability modules concurrently \u2014 XSS, SQLi, SSRF, SSTI, XXE, deserialization, request smuggling, prototype pollution, JWT attacks and more. Each module is an active exploit attempt, not a passive header check.<\/p>\n<h3> <a name=\"3-a-go-template-engine-that-isnt-nuclei\" href=\"#3-a-go-template-engine-that-isnt-nuclei\"> <\/a> 3. A Go template engine that isn&#8217;t Nuclei <\/h3>\n<p>I built <code>kagesec-engine<\/code> \u2014 a purpose-built Go binary that runs 7,417 HTTP-compatible Nuclei templates. It is not a Nuclei wrapper. The key differences:<\/p>\n<ul>\n<li> <strong>Confidence scoring<\/strong> \u2014 every finding gets a 0.0\u20131.0 score, not a binary match\/no-match<\/li>\n<li> <strong>Stack fingerprinting<\/strong> \u2014 identifies your tech stack first, runs relevant templates first<\/li>\n<li> <strong>Real-time streaming<\/strong> \u2014 JSON Lines streamed as findings arrive, Python reads them live<\/li>\n<li> <strong>Auth-aware<\/strong> \u2014 inherits all session cookies, bearer tokens, and headers from the main scan<\/li>\n<li> <strong>7,417 templates in ~2 minutes<\/strong> with 50 goroutines<\/li>\n<\/ul>\n<h3> <a name=\"4-ai-verification\" href=\"#4-ai-verification\"> <\/a> 4. AI verification <\/h3>\n<p>With an AI provider configured (Claude, GPT-4o, Gemini, Mistral, or Ollama locally), KageSec sends findings to the model with full request\/response context and asks: <em>is this actually exploitable?<\/em> False positives get filtered. Real findings get an exploitability verdict.<\/p>\n<p>Without AI verification, you&#8217;re relying on heuristics. With it, you&#8217;re relying on a model that has read every CVE write-up on the internet.<\/p>\n<h3> <a name=\"5-canarybased-false-positive-elimination-for-parameters\" href=\"#5-canarybased-false-positive-elimination-for-parameters\"> <\/a> 5. Canary-based false positive elimination for parameters <\/h3>\n<p>The classic DAST noise problem: you inject into a parameter, get a reflection, call it XSS. But the app reflects everything. It&#8217;s just echoing input. KageSec uses a canary baseline (the same approach as Burp Param Miner and Arjun): inject a known-safe random <br \/> value first, establish what &#8220;normal&#8221; looks like, then compare attack payloads against that baseline. If your XSS payload reflects but so does <code>kagesec-canary-a3f9<\/code>, it&#8217;s not a finding.<\/p>\n<hr>\n<h2> <a name=\"getting-started\" href=\"#getting-started\"> <\/a> Getting started <\/h2>\n<p> <\/markdown-accessiblity-table>\u2026<\/article>\n<\/div><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/zulhilmi_53f9b4ccb7861abc\/i-built-an-open-source-dast-scanner-that-outfound-zap-4llo\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I built KageSec. What&#8217;s wrong with existing DAST tools Nuclei is great \u2014 ProjectDiscovery built something genuinely impressive. But it&#8217;s a template-matching engine, not a DAST scanner. It hits the root URL, matches YAML patterns and reports findings. It does not crawl your app, discover parameters, or inject payloads into forms. The companies charging enterprise [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[41],"tags":[],"class_list":["post-2624","post","type-post","status-publish","format-standard","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/2624","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=2624"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/2624\/revisions"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=2624"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=2624"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=2624"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}