<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Svetlin Minkov</title>
    <description>The latest articles on DEV Community by Svetlin Minkov (@foxvr-sudo).</description>
    <link>https://dev.to/foxvr-sudo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4037650%2F503e847f-0278-4ebb-9a3a-7e7c7e057738.png</url>
      <title>DEV Community: Svetlin Minkov</title>
      <link>https://dev.to/foxvr-sudo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/foxvr-sudo"/>
    <language>en</language>
    <item>
      <title>I built an open-source web vulnerability scanner — here's what I learned about scan fatigue</title>
      <dc:creator>Svetlin Minkov</dc:creator>
      <pubDate>Mon, 20 Jul 2026 08:08:42 +0000</pubDate>
      <link>https://dev.to/foxvr-sudo/i-built-an-open-source-web-vulnerability-scanner-heres-what-i-learned-about-scan-fatigue-3f7n</link>
      <guid>https://dev.to/foxvr-sudo/i-built-an-open-source-web-vulnerability-scanner-heres-what-i-learned-about-scan-fatigue-3f7n</guid>
      <description>&lt;p&gt;For the last few months I've been building &lt;strong&gt;BugBounty Arsenal&lt;/strong&gt; — an open-source web application security scanner — mostly to scratch my own itch. I want to share the two problems that shaped it, because they're the parts I'm most proud of.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with running a scanner
&lt;/h2&gt;

&lt;p&gt;Anyone who has run a DAST tool on a schedule knows the failure mode: you scan every day, and every day it hands you the &lt;em&gt;same&lt;/em&gt; 40 findings. After a week you stop reading the reports. The signal drowns in noise.&lt;/p&gt;

&lt;p&gt;So the first thing I built wasn't a detector — it was a &lt;strong&gt;diff&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scheduled scans in BBA don't notify you on every finding. Each run is compared against the previous completed run of the same schedule, keyed on a signature of &lt;code&gt;title + detector + url + severity&lt;/code&gt;. You only get pinged on what's &lt;strong&gt;new&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New host exposed since yesterday? Alert.&lt;/li&gt;
&lt;li&gt;Same misconfig you already triaged? Silence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Triage state (confirmed / false-positive) also &lt;strong&gt;carries over&lt;/strong&gt; between runs, so once you've dismissed something, it stays dismissed. That single change turned the scanner from "another dashboard I ignore" into something I actually read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it fail a build
&lt;/h2&gt;

&lt;p&gt;The second thing: a scanner is only useful in a pipeline if it can &lt;strong&gt;block&lt;/strong&gt; a bad deploy.&lt;/p&gt;

&lt;p&gt;There's a zero-dependency Python CLI (standard library only — no &lt;code&gt;pip install&lt;/code&gt;) that starts a scan, waits for it, and exits non-zero when findings cross a severity threshold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python bba_scan.py &lt;span class="nt"&gt;--target&lt;/span&gt; https://staging.example.com &lt;span class="nt"&gt;--category&lt;/span&gt; 1 &lt;span class="nt"&gt;--fail-on&lt;/span&gt; high
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit &lt;code&gt;0&lt;/code&gt; = clean, &lt;code&gt;1&lt;/code&gt; = threshold breached. Drop that into any CI and a regression in your attack surface blocks the merge.&lt;/p&gt;

&lt;p&gt;It also exports &lt;strong&gt;SARIF&lt;/strong&gt;, so findings land in GitHub's &lt;em&gt;Security → Code scanning&lt;/em&gt; tab right next to CodeQL — with severity, rule and a link back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FoxVR-sudo/BugBounty-Arsenal/.github/actions/bugbounty-scan@master&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.BBA_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://staging.example.com&lt;/span&gt;
    &lt;span class="na"&gt;fail-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;high&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's actually in it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;53 active + 10 passive detectors&lt;/strong&gt; — XSS, SSRF, CORS, security headers, injection, exposed secrets, auth flaws…&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled scans&lt;/strong&gt; with the new-findings diff above&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attack surface management&lt;/strong&gt; — tracks hosts / tech / ports across scans&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triage workflow&lt;/strong&gt; — confirmed / false-positive, carried between runs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST API + API keys&lt;/strong&gt;, and an &lt;strong&gt;SSRF guard&lt;/strong&gt; that blocks internal/infra targets by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stack: Django + DRF, Celery/Redis, PostgreSQL, React — the whole thing comes up with &lt;code&gt;docker compose up&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it / self-host it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Source (self-host with Docker Compose): &lt;a href="https://github.com/FoxVR-sudo/Bug-Bounty-Arsenal-v.3" rel="noopener noreferrer"&gt;https://github.com/FoxVR-sudo/Bug-Bounty-Arsenal-v.3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hosted, free to use: &lt;a href="https://bugbounty-arsenal.net" rel="noopener noreferrer"&gt;https://bugbounty-arsenal.net&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only scan targets you own or are authorized to test.&lt;/p&gt;

&lt;p&gt;I'd genuinely love feedback — especially on &lt;strong&gt;detector accuracy&lt;/strong&gt; (false positives are the enemy) and what would make it fit into your workflow. What does &lt;em&gt;your&lt;/em&gt; scan-fatigue look like?&lt;/p&gt;

</description>
      <category>security</category>
      <category>opensource</category>
      <category>python</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
