<?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>Forem: Aiden Bolin</title>
    <description>The latest articles on Forem by Aiden Bolin (@aiden_bolin_c3f6ef002625c).</description>
    <link>https://forem.com/aiden_bolin_c3f6ef002625c</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3906607%2Fe6bdb864-8f09-41ec-a9d4-401cc0ef18af.png</url>
      <title>Forem: Aiden Bolin</title>
      <link>https://forem.com/aiden_bolin_c3f6ef002625c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aiden_bolin_c3f6ef002625c"/>
    <language>en</language>
    <item>
      <title>I built a CVE patch-ops tool for indie SaaS shops in a weekend (open scan, honest comparison vs vuls.io)</title>
      <dc:creator>Aiden Bolin</dc:creator>
      <pubDate>Fri, 01 May 2026 14:02:25 +0000</pubDate>
      <link>https://forem.com/aiden_bolin_c3f6ef002625c/i-built-a-cve-patch-ops-tool-for-indie-saas-shops-in-a-weekend-open-scan-honest-comparison-vs-3llk</link>
      <guid>https://forem.com/aiden_bolin_c3f6ef002625c/i-built-a-cve-patch-ops-tool-for-indie-saas-shops-in-a-weekend-open-scan-honest-comparison-vs-3llk</guid>
      <description>&lt;h1&gt;
  
  
  I built a CVE patch-ops tool for indie SaaS shops in a weekend (open scan, honest comparison vs vuls.io)
&lt;/h1&gt;

&lt;p&gt;Last week Hostinger emailed every customer about CVE-2026-31431 — the "Copy Fail" Linux kernel local-privilege-escalation. A 732-byte Python script gets root via &lt;code&gt;algif_aead&lt;/code&gt; AF_ALG + &lt;code&gt;splice()&lt;/code&gt;. Every kernel between 2017 and the upstream fix in early 2026.&lt;/p&gt;

&lt;p&gt;I run a one-person SaaS on a Hostinger VPS. I read the advisory, dropped a &lt;code&gt;modprobe&lt;/code&gt; blacklist, and was patched in 30 minutes. Then I realized: most indie SaaS founders running their own boxes wouldn't read that email until tomorrow. Some would never read it at all.&lt;/p&gt;

&lt;p&gt;Patch ops for the indie-SaaS tier doesn't exist. vuls.io is great if you have a security engineer with half a day. Enterprise tools are unaffordable. The gap is "I have 1–10 Linux servers, I know I should patch, the workflow is too annoying to do consistently."&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;StackPatch&lt;/strong&gt; — &lt;code&gt;curl https://mindsparkstack.com/scan.sh | bash&lt;/code&gt; for a free anonymous CVE check, $99 lifetime founder seat for hourly monitoring with a public audit URL.&lt;/p&gt;

&lt;p&gt;This post is the build log: architecture, the honest vs-vuls comparison, and the bash one-liner you can run on your VPS in five seconds to see what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  The free quickscan: 5 seconds, 0 signup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://mindsparkstack.com/scan.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script reads &lt;code&gt;/etc/os-release&lt;/code&gt;, &lt;code&gt;uname -r&lt;/code&gt;, the top 200 packages from &lt;code&gt;dpkg-query&lt;/code&gt;, and POSTs them to a public API. The API runs the live USN feed (Ubuntu) and the Debian Security Tracker (~110K fix-records across bookworm/trixie/bullseye) against your inventory using &lt;code&gt;dpkg --compare-versions&lt;/code&gt;, returns matching CVEs with the exact remediation command.&lt;/p&gt;

&lt;p&gt;The source is served as &lt;code&gt;text/plain&lt;/code&gt; so you can &lt;code&gt;curl https://mindsparkstack.com/scan.sh&lt;/code&gt; and read it before piping to bash. No persistent state server-side beyond a 5-min cache. No identifying info collected (no hostname, no IP, no env vars).&lt;/p&gt;

&lt;p&gt;On a real noble box with &lt;code&gt;openssh-client 1:9.6p1-3ubuntu13.10&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== StackPatch quickscan ===
  distro:   ubuntu
  codename: noble
  kernel:   6.8.0-100-generic
  packages: 187

⚠️  2 active CVE matches on your stack right now (worst: high).
   Run the recommended commands above. To monitor every server hourly...

  [HIGH] CVE-2026-31431  Linux kernel "Copy Fail" — local-priv-esc via algif_aead
        why: Linux kernel local-priv-esc; 732-byte Python script gets root...
        match: Running kernel: 6.8.0-100-generic
        recommend: Apply persistent modprobe blacklist for algif_aead now...

  [HIGH] USN-8222-1  OpenSSH 9.6p1 vulnerabilities
        match: openssh-client: installed 1:9.6p1-3ubuntu13.10 &amp;lt; fixed 1:9.6p1-3ubuntu13.16
        recommend: sudo apt-get install --only-upgrade -y openssh-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the demo. Five seconds, real CVEs, exact commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture: file-based, Python stdlib, no DB
&lt;/h2&gt;

&lt;p&gt;The whole backend is JSONL files on disk + cron + a small Next.js layer. There's no Postgres. There's no Kubernetes. The matcher is one Python script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Pseudocode of the matcher join
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;usn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cached_usns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;usn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release_packages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_codename&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;installed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_inventory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;dpkg_lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;Finding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few details that mattered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;dpkg --compare-versions&lt;/code&gt; for Debian-policy-correct version comparison.&lt;/strong&gt; Lexicographic compare is wrong for &lt;code&gt;1:9.6p1-3ubuntu13.10&lt;/code&gt; vs &lt;code&gt;1:9.6p1-3ubuntu13.16&lt;/code&gt; (lex says "10" &amp;gt; "16"). Spawning &lt;code&gt;dpkg&lt;/code&gt; once per pair is cheap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-filter by codename.&lt;/strong&gt; USN &lt;code&gt;release_packages&lt;/code&gt; is keyed by &lt;code&gt;noble | jammy | focal | bionic&lt;/code&gt;. Reading the user's &lt;code&gt;/etc/os-release&lt;/code&gt; &lt;code&gt;VERSION_CODENAME&lt;/code&gt; upfront lets the matcher skip 90% of records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cap the USN window.&lt;/strong&gt; I scan the last 200 USNs (sorted by ID). Older ones are fine to stale; if a 2017 USN matters to your 2024 box, you have bigger problems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debian Security Tracker is huge.&lt;/strong&gt; The &lt;code&gt;tracker.json&lt;/code&gt; is 70 MB with 36K fix-records per release. I pre-build per-codename indexes (&lt;code&gt;{package: [{cve, fixed_version, urgency}]}&lt;/code&gt;) once daily so the matcher loads ~12 MB instead of 70 MB per request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inventory + matcher run on three crons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="m"&gt;3&lt;/span&gt; * * * *           &lt;span class="n"&gt;inventory&lt;/span&gt;   (&lt;span class="n"&gt;reads&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;os&lt;/span&gt;-&lt;span class="n"&gt;release&lt;/span&gt;, &lt;span class="n"&gt;uname&lt;/span&gt;, &lt;span class="n"&gt;dpkg&lt;/span&gt;, &lt;span class="n"&gt;docker&lt;/span&gt;, &lt;span class="n"&gt;ports&lt;/span&gt;, &lt;span class="n"&gt;modprobe&lt;/span&gt;)
&lt;span class="m"&gt;23&lt;/span&gt;,&lt;span class="m"&gt;53&lt;/span&gt; * * * *       &lt;span class="n"&gt;USN&lt;/span&gt; &lt;span class="n"&gt;poll&lt;/span&gt;    (&lt;span class="n"&gt;Ubuntu&lt;/span&gt; &lt;span class="n"&gt;Security&lt;/span&gt; &lt;span class="n"&gt;Notices&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;, &lt;span class="n"&gt;twice&lt;/span&gt; &lt;span class="n"&gt;hourly&lt;/span&gt;)
&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; * * *           &lt;span class="n"&gt;DSA&lt;/span&gt; &lt;span class="n"&gt;poll&lt;/span&gt;    (&lt;span class="n"&gt;Debian&lt;/span&gt; &lt;span class="n"&gt;Security&lt;/span&gt; &lt;span class="n"&gt;Tracker&lt;/span&gt;, &lt;span class="n"&gt;daily&lt;/span&gt; — &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;huge&lt;/span&gt;)
&lt;span class="m"&gt;33&lt;/span&gt; * * * *          &lt;span class="n"&gt;matcher&lt;/span&gt;     (&lt;span class="n"&gt;joins&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt; × &lt;span class="n"&gt;USN&lt;/span&gt; × &lt;span class="n"&gt;DSA&lt;/span&gt;, &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="n"&gt;JSONL&lt;/span&gt;)
&lt;span class="m"&gt;40&lt;/span&gt; * * * *          &lt;span class="n"&gt;alerts&lt;/span&gt;      (&lt;span class="n"&gt;emails&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole thing. The Next.js layer is a thin wrapper that exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/stackpatch/quickscan&lt;/code&gt; — anonymous POST, returns matches in &amp;lt;1s&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/stackpatch/enroll&lt;/code&gt; — paid customer enrolls a server with a token, returns audit URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/stackpatch/inventory&lt;/code&gt; — authenticated inventory POST from the agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/patch/audit/&amp;lt;slug&amp;gt;&lt;/code&gt; — public posture page per server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The killer workflow: the public audit URL
&lt;/h2&gt;

&lt;p&gt;This is what I didn't see in any other tool. Every monitored server gets a URL like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://mindsparkstack.com/patch/audit/mss-vps&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It shows current findings, applied mitigations, recent resolutions, package counts, kernel state. Updates hourly. Customers (or your customers' enterprise prospects) can verify your security posture without an NDA, without a stale PDF.&lt;/p&gt;

&lt;p&gt;When a $3K/year customer asks "how do you handle server security updates?" — you send the link. Done.&lt;/p&gt;

&lt;p&gt;This isn't a security scanner. It's a security-response receipt. That distinction is the whole reason the product exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest vs vuls.io
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://vuls.io" rel="noopener noreferrer"&gt;vuls.io&lt;/a&gt; is the obvious comparison. It's free, OSS, mature since 2016, 10K+ GitHub stars, supports Ubuntu/Debian/RHEL/CentOS/Amazon Linux/openSUSE/Alpine/FreeBSD/Windows.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://mindsparkstack.com/patch/vs-vuls" rel="noopener noreferrer"&gt;/patch/vs-vuls&lt;/a&gt; as an honest 12-row green/red/grey side-by-side. Honest enough that the page actively recommends vuls.io if you fit those constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a security engineer with half a day to set up &lt;code&gt;go-cve-dictionary&lt;/code&gt; + &lt;code&gt;goval-dictionary&lt;/code&gt; + &lt;code&gt;gost&lt;/code&gt; + &lt;code&gt;cve-search&lt;/code&gt; and rebuild them on cron&lt;/li&gt;
&lt;li&gt;You need FreeBSD / Windows / openSUSE support&lt;/li&gt;
&lt;li&gt;Compliance forbids any package data leaving your network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick StackPatch instead if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're a one-person SaaS shop with 1–10 boxes on Ubuntu, Debian, Alpine, AlmaLinux, or Rocky Linux&lt;/li&gt;
&lt;li&gt;You want the answer in 5 minutes, not half a day&lt;/li&gt;
&lt;li&gt;You want the exact &lt;code&gt;apt&lt;/code&gt; / &lt;code&gt;apk&lt;/code&gt; / &lt;code&gt;dnf&lt;/code&gt; / kernel-reboot / modprobe-blacklist one-liner, not just a CVE link&lt;/li&gt;
&lt;li&gt;You want a public audit URL for sales due diligence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a Sunday-afternoon scanner project sounds fun: vuls.io. If you want to be patched and provably so by tomorrow morning: StackPatch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What V1 doesn't do
&lt;/h2&gt;

&lt;p&gt;I'm not going to lie about the gaps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No FreeBSD / Windows / openSUSE yet&lt;/strong&gt; (V1+ covers Ubuntu, Debian, Alpine, AlmaLinux, Rocky Linux — 18 release versions across those 5 distros, 41K unique CVEs cross-indexed from USN + DSA + Alpine secdb + OSV-rpm + NVD)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No auto-apply&lt;/strong&gt; (deliberate — you should review the command, security-product trust is fragile)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No multi-user RBAC, no SSO, no compliance attestations&lt;/strong&gt; (this is for solo founders, not security teams)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Kubernetes&lt;/strong&gt; (out of scope for V1; you're not running k8s on a $5/mo VPS anyway)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No SLA on remediation&lt;/strong&gt; (we tell you the command; you run it)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of those are dealbreakers, vuls.io or an enterprise tool is the right call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing: $99 lifetime for the first 50
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free quickscan:&lt;/strong&gt; anonymous, no signup, no limits. Run it on as many boxes as you like.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$99 lifetime founder seat:&lt;/strong&gt; 3 servers, hourly monitoring, real-time email alerts, public audit URL, every V2+ feature included. &lt;strong&gt;50 only&lt;/strong&gt;, then it's a monthly subscription tier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no growth-hack reason for the lifetime price. It's a forcing function — a recurring-revenue product is cheaper for me long-term but it lets me delay shipping. Lifetime sales mean I have to keep adding founders, which means I have to keep shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://mindsparkstack.com/scan.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;code&gt;https://mindsparkstack.com/scan.sh&lt;/code&gt; (read first, then pipe).&lt;br&gt;
Comparison: &lt;code&gt;https://mindsparkstack.com/patch/vs-vuls&lt;/code&gt;&lt;br&gt;
Live demo audit: &lt;code&gt;https://mindsparkstack.com/patch/audit/mss-vps&lt;/code&gt; (our own VPS, public)&lt;br&gt;
Buy ($99 lifetime, 50 only): &lt;code&gt;https://mindsparkstack.com/patch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Comments welcome — what's missing, what's wrong, what would make you switch from vuls.io. I read everything.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Aiden runs MindSparkStack. StackPatch is the patch-ops layer. Source: this is shipped on Hostinger, with Next.js 16 + Python stdlib + cron, running on the same VPS whose audit URL is public above.&lt;/em&gt;&lt;/p&gt;

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