<?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: Arturo Enrique Mata Garcia</title>
    <description>The latest articles on DEV Community by Arturo Enrique Mata Garcia (@matarturo).</description>
    <link>https://dev.to/matarturo</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%2F4116032%2Fdabe4cf7-4c46-44f9-8b14-b69e5a03b155.jpg</url>
      <title>DEV Community: Arturo Enrique Mata Garcia</title>
      <link>https://dev.to/matarturo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matarturo"/>
    <language>en</language>
    <item>
      <title>Soft-404s: The Silent Killer of DAST Scans</title>
      <dc:creator>Arturo Enrique Mata Garcia</dc:creator>
      <pubDate>Sun, 27 Sep 2026 22:58:54 +0000</pubDate>
      <link>https://dev.to/matarturo/soft-404s-the-silent-killer-of-dast-scans-417c</link>
      <guid>https://dev.to/matarturo/soft-404s-the-silent-killer-of-dast-scans-417c</guid>
      <description>&lt;p&gt;Traditional DAST tools have a dirty secret: they don't know when a page doesn't exist. And in modern SPA architectures, this blindness doesn't just generate noise—it &lt;strong&gt;corrupts the entire assessment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you're a pentester, DevSecOps engineer, or security researcher, you've seen it. You run a scan. The report says everything is fine. Then a manual test reveals a critical endpoint that was never crawled, never tested, and never reported. The scanner missed it because it couldn't distinguish a real page from a soft 404.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VORTEX Assessment Engine&lt;/strong&gt; was built to solve exactly that.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔬 What Is a Soft-404 and Why It Breaks DAST
&lt;/h2&gt;

&lt;p&gt;A soft-404 is an HTTP response that returns &lt;code&gt;200 OK&lt;/code&gt; while serving content that semantically means "not found." Unlike a hard 404, which correctly returns a &lt;code&gt;404&lt;/code&gt; status code, a soft-404 masks missing routes as legitimate pages.&lt;/p&gt;

&lt;p&gt;This is not a bug. It's a &lt;strong&gt;design pattern&lt;/strong&gt; in many modern frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Router&lt;/strong&gt;, &lt;strong&gt;Vue Router&lt;/strong&gt;, and &lt;strong&gt;Angular Router&lt;/strong&gt; often render a "not found" component with a 200 status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt; and &lt;strong&gt;Nuxt&lt;/strong&gt; handle dynamic routes dynamically, sometimes returning 200 for missing paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SPAs with client-side routing&lt;/strong&gt; are especially prone: the server always returns the same &lt;code&gt;index.html&lt;/code&gt;, and the router decides what to display.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a DAST scanner, this is a minefield:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;False positives:&lt;/strong&gt; the scanner treats the fallback as a real page and tests it, then reports findings against content that isn't a distinct endpoint at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False negatives:&lt;/strong&gt; the scanner assumes a path &lt;em&gt;is&lt;/em&gt; a fallback and skips it — dropping a genuinely reachable route from the test suite entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blind spot in targeted checks:&lt;/strong&gt; any check that probes a wordlist of known paths — API discovery, path scanning, dependency detection — has no way to tell "found it" from "the fallback said yes to everything."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken coverage:&lt;/strong&gt; none of the above shows up as a failure in the summary line. The report still says "scan complete."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditional scanners rely on &lt;strong&gt;static HTML parsing&lt;/strong&gt; and &lt;strong&gt;HTTP status codes&lt;/strong&gt;. Neither works reliably with soft-404s.&lt;/p&gt;




&lt;h2&gt;
  
  
  🕸️ Why VORTEX Handles Soft-404s Differently
&lt;/h2&gt;

&lt;p&gt;VORTEX doesn't take a &lt;code&gt;200 OK&lt;/code&gt; at face value — and that one decision is what separates a scan you can trust from a scan that just looks clean.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You stop chasing ghosts.&lt;/strong&gt; Every "page" that's really just the SPA fallback in disguise gets filtered out before it ever reaches your report. No more triaging findings against a page that was never real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You stop losing real endpoints.&lt;/strong&gt; Routes that legacy scanners write off as "probably just the 404 page" get evaluated on their actual content, not assumed away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It happens automatically, before anything else runs.&lt;/strong&gt; No config, no manual allowlisting of fallback pages, no tuning per target. The calibration runs first and every downstream check inherits the result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The result:&lt;/strong&gt; a report where "not found" means not found, and "found" means found — which is the entire point of running a scanner in the first place.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Real-World Impact
&lt;/h2&gt;

&lt;p&gt;Run against OWASP Juice Shop (Docker, port 3000): the moment VORTEX detected the SPA fallback pattern, it adjusted — and the rest of the assessment came back clean of the noise that pattern usually creates. Zero false findings traced back to fallback pages. Zero wasted cycles re-testing the same page under a different URL.&lt;/p&gt;

&lt;p&gt;That's the difference this one check makes: not a longer report, a &lt;strong&gt;trustworthy&lt;/strong&gt; one.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Why This Matters for CI/CD
&lt;/h2&gt;

&lt;p&gt;In a CI/CD pipeline, a DAST scanner that cannot handle soft-404s is worse than no scanner at all. It gives you false confidence: the pipeline goes green, coverage looks complete, and the gap only surfaces later, usually manually, usually after deployment. You deploy. You think you're covered. You're not.&lt;/p&gt;

&lt;p&gt;Calibrating for the fallback pattern before running the rest of the suite is what keeps "clean scan" from quietly meaning "we didn't actually look."&lt;/p&gt;

&lt;p&gt;What that looks like in an actual run: the calibration check isn't a separate step someone has to remember to run — it's one of 18 checks executed in the same pass, timestamped and reproducible (&lt;code&gt;2026-09-27T17:42:36&lt;/code&gt;). It also isn't the thing slowing the pipeline down: the same run completed with a 0.01s performance result. And the consequence isn't hypothetical — in this run, both API Discovery and Path Scan came back clean after the calibration step had already flagged content-based filtering as necessary, meaning those results reflect the fallback-aware logic, not a naive status-code check that got lucky.&lt;/p&gt;

&lt;p&gt;Beyond what this specific run shows, VORTEX is positioned as pipeline-native by design — no cloud dependency, no separate runtime to provision, SARIF output for native integration with code-scanning tools, and a deterministic execution model. &lt;em&gt;(Stated product characteristics, not something demonstrated by this particular PoC run.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Soft-404 calibration is not a feature. It's a prerequisite for &lt;strong&gt;accurate DAST in modern web applications&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔬 Evaluation Flow
&lt;/h2&gt;

&lt;p&gt;A typical security assessment with VORTEX follows this streamlined execution sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  WEB APP
                     │
                     ▼
            ┌─────────────────┐
            │     VORTEX      │
            │     Crawler     │
            └────────┬────────┘
                     │
                     ▼
           Soft-404 Calibration
                     │
                     ▼
          SPA / Route Discovery 
                     │
                     ▼
          JavaScript Analysis
                     │
                     ▼
          Security Assessment
                     │
                     ▼
            ┌─────────────────┐
            │    RESULTS      │
            └────────┬────────┘
                     │
            ┌────────┼────────┐
            ▼        ▼        ▼
          HTML      JSON     SARIF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The soft-404 calibration step sits early in the pipeline for a reason: everything downstream — path scanning, API discovery, the security tests themselves — depends on knowing whether &lt;code&gt;200 OK&lt;/code&gt; means "page exists" for this specific target.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Proof of Concept: Soft-404 Calibration in a Modern SPA
&lt;/h2&gt;

&lt;p&gt;The following output is from a VORTEX scan (PRO account) against OWASP Juice Shop, a deliberately vulnerable SPA built with Angular, deployed locally via Docker on port 3000. It captures a real-world scenario where the server returns 200 OK for non-existent routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@sandbox-vortex:/opt/vortex# vortex &lt;span class="nt"&gt;--local&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt; 3000

██╗   ██╗ ██████╗ ██████╗ ████████╗███████╗██╗  ██╗
██║   ██║██╔═══██╗██╔══██╗╚══██╔══╝██╔════╝╚██╗██╔╝
██║   ██║██║   ██║██████╔╝   ██║   █████╗   ╚███╔╝
╚██╗ ██╔╝██║   ██║██╔══██╗   ██║   ██╔══╝   ██╔██╗
 ╚████╔╝ ╚██████╔╝██║  ██║   ██║   ███████╗██╔╝ ██╗
  ╚═══╝   ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚══════╝╚═╝  ╚═╝  v3.0.6

Assessment Engine

LOCAL · FAST · ACTIONABLE

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;  GUMROAD_PRO license renewed via CEREBELLUM.
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; Starting VORTEX Security Audit v3.0.6
    Target: http://127.0.0.1:3000
&lt;span class="o"&gt;===========================================================================&lt;/span&gt;
Audit Progress: &lt;span class="o"&gt;[&lt;/span&gt;   6% &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; ██---------------------------- &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1/15] Discovery &amp;amp; Crawler &lt;span class="nt"&gt;---&lt;/span&gt;

&lt;span class="o"&gt;===&lt;/span&gt; DISCOVERY &lt;span class="o"&gt;(&lt;/span&gt;CRAWLER&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
&lt;span class="o"&gt;[!]&lt;/span&gt; Soft-404 Calibration             &lt;span class="o"&gt;[&lt;/span&gt;WARN] Server responds 200 to non-existent routes &lt;span class="o"&gt;(&lt;/span&gt;SPA fallback&lt;span class="o"&gt;)&lt;/span&gt; - Path Scan and API Discovery will filter by content, not just status code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Interpretation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server returned 200 OK for a non-existent route.&lt;/li&gt;
&lt;li&gt;VORTEX correctly classified this as SPA fallback behavior rather than a real page.&lt;/li&gt;
&lt;li&gt;Calibration status: WARN.&lt;/li&gt;
&lt;li&gt;Downstream checks switch to content-based filtering as a direct result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the exact scenario where legacy DAST tools fail silently. VORTEX makes the failure mode visible instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Useful Links &amp;amp; Documentation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Website &amp;amp; Docs:&lt;/strong&gt; &lt;a href="https://zerodayslab.co/docs" rel="noopener noreferrer"&gt;zerodayslab.co/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://matarturo.github.io" rel="noopener noreferrer"&gt;matarturo.github.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community Edition Install:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; install.sh https://raw.githubusercontent.com/matarturo/vortex/main/install.sh
  &lt;span class="nb"&gt;chmod&lt;/span&gt; +x install.sh
  &lt;span class="nb"&gt;sudo&lt;/span&gt; ./install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion &amp;amp; Call to Action
&lt;/h2&gt;

&lt;p&gt;Soft-404s are not a minor edge case. They are a &lt;strong&gt;fundamental blind spot&lt;/strong&gt; in traditional DAST tools, and they silently corrupt the accuracy of every scan run against a modern SPA.&lt;/p&gt;

&lt;p&gt;VORTEX was built to close that gap. The run shown above was captured on a PRO license; the soft-404 calibration step is the core mechanism this article is about, so if you're running the Community Edition, check your own output for the same &lt;code&gt;Calibracion Soft-404&lt;/code&gt; line before assuming feature parity.&lt;/p&gt;

&lt;p&gt;Clone the repository, test it against your own SPAs, and see what your current scanner has been missing. Drop a star, open an issue, or share your feedback. See you in the code.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
      <category>testing</category>
    </item>
    <item>
      <title>Taming SPAs with VORTEX: How a Crawler Actually Understands React, Vue, and Angular</title>
      <dc:creator>Arturo Enrique Mata Garcia</dc:creator>
      <pubDate>Thu, 10 Sep 2026 15:47:32 +0000</pubDate>
      <link>https://dev.to/matarturo/taming-spas-with-vortex-how-a-crawler-actually-understands-react-vue-and-angular-57dn</link>
      <guid>https://dev.to/matarturo/taming-spas-with-vortex-how-a-crawler-actually-understands-react-vue-and-angular-57dn</guid>
      <description>&lt;h3&gt;
  
  
  💡Let’s run a mental experiment.
&lt;/h3&gt;

&lt;p&gt;You open a SPA built with React. You point your favorite scanner—the one you've used for years, the one that "never fails"—to audit it. You wait. It generates a neat, polished report with a nice logo and a progress bar. And it tells you: "2 routes found. 0 critical vulnerabilities."&lt;/p&gt;

&lt;p&gt;Two routes. In an application featuring a complete dashboard, an admin panel, over forty endpoints, and half a dozen features locked behind feature flags.&lt;/p&gt;

&lt;p&gt;The scanner isn't broken. The scanner is blind. And nobody told it.&lt;/p&gt;

&lt;p&gt;That was, literally, the starting point of &lt;strong&gt;VORTEX Assessment Engine&lt;/strong&gt;. In my previous post, I covered the overall architecture: a self-hosted engine, a native binary, zero heavy dependencies, and direct CI/CD pipeline integration. Today, I want to lift the hood and show you the piece that makes all of that actually matter: the crawler, and what happens when it steps directly inside the JavaScript instead of just staring at it from the outside.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're a developer&lt;/strong&gt;, this might sting a little. If you're a DevOps engineer, you'll care about how it plugs into the pipeline. And if you're a pentester... well, you already know exactly what I'm talking about.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 HTML Is No Longer a Map. It’s a Lid.
&lt;/h2&gt;

&lt;p&gt;Let's look at the facts. This is what a &lt;code&gt;curl&lt;/code&gt; response to a modern SPA looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;script src="/static/js/main.a1b2c3.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is literally all there is. No &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt;, no forms, no routes, nothing that a 2000s-era HTML parser can chew on. The entire application—components, router, API calls, business logic, validations, everything—is compressed and minified inside that single &lt;code&gt;.js&lt;/code&gt; file, and it gets built right inside the browser in real-time the moment a user hits the page.&lt;/p&gt;

&lt;p&gt;A crawler that only knows how to read HTML is staring at a locked door with a sign saying "nothing to see here." And it actually believes it.&lt;/p&gt;

&lt;p&gt;This isn’t a minor limitation or an academic edge case. It’s the exact reason why, year after year, we keep seeing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Maps, Stripe, or Firebase API keys leaked in production for months.&lt;/li&gt;
&lt;li&gt;Complete administrative routes &lt;code&gt;(/admin, /internal, /debug)&lt;/code&gt; that nobody discovered because they only render after a specific menu click.&lt;/li&gt;
&lt;li&gt;Staging or internal microservice endpoints, referenced deep inside a bundle, that a "traditional" dynamic scan never touched.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frontend secret leakage reports aren't going down. And they won't, because the tools meant to catch them are still playing a game that ended a decade ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ How VORTEX Solves It: Stop Reading, Start Executing
&lt;/h2&gt;

&lt;p&gt;The core design decision behind the &lt;strong&gt;VORTEX crawler&lt;/strong&gt; is simple to state and much harder to build right: instead of parsing HTML, execute the application like a real user would, and observe what happens next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     WEB APPLICATION
            │
            ▼
   ┌─────────────────┐
   │     VORTEX      │
   │     Crawler     │
   └────────┬────────┘
            │
            ▼
 Rendering + Dynamic Route Discovery
            │
            ▼
    JS Bundle Extraction &amp;amp; Analysis
            │
            ▼
   Attack Surface Correlation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔍 Let's break down why that actually matters.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. 🧭 Route Discovery: Don't Read, Navigate
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;VORTEX&lt;/strong&gt; doesn't just scrape links. It interacts: it clicks, navigates, opens menus, triggers transitions, and listens to the history changes fired on-the-fly by React Router, Vue Router, or Angular Router.&lt;/p&gt;

&lt;p&gt;Why is this a big deal? Because the most interesting routes are almost never just a single obvious click away. They hide behind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dropdown menu that only renders on hover.&lt;/li&gt;
&lt;li&gt;A tab loading a lazy chunk via dynamic &lt;code&gt;import()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A route guard that only mounts a component if an active session exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that exists in the initial HTML. It only exists after something happens inside the browser. And a crawler that doesn’t simulate that "something" will simply never see it.&lt;/p&gt;

&lt;p&gt;(Yes, authentication-protected routes are a whole separate, juicy topic—&lt;strong&gt;VORTEX&lt;/strong&gt; supports credentials, &lt;code&gt;--login-path&lt;/code&gt;, and session cookie handoff to reach them—but that deserves its own dedicated post. Today, we're sticking to the core crawler.)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. 📦 Static JS Bundle Analysis: The Mistake We've All Made at Least Once
&lt;/h3&gt;

&lt;p&gt;This is, without exaggeration, my favorite part of the engine. And the most underrated feature in any DAST tool.&lt;/p&gt;

&lt;p&gt;Put yourself in the shoes of the developer writing the code, because that’s where the problem actually starts. You're deep into a sprint, you need a map to load right now, or an analytics widget to work in the staging environment. You paste the key straight into the source code, with every intention of moving it to an environment variable "as soon as I have five minutes." The PR gets reviewed—nobody notices, because nobody is checking that line by line—it gets merged, and Webpack or Vite do their job: they minify, bundle, and that key ends up shipping, completely intact, inside the &lt;code&gt;main.a1b2c3.js&lt;/code&gt; file served publicly to anyone opening the app.&lt;/p&gt;

&lt;p&gt;That exact moment—the "I'll fix it later" that never comes—is the exact kind of oversight that a static JS bundle analysis is built to catch. Nobody needs to click anything, and no suspicious requests need to be fired: the file is already public, accessible to anyone with a browser and their DevTools open, and it already contains what it shouldn't.&lt;/p&gt;

&lt;p&gt;And here’s the kicker that almost no one sees coming: most of these leaks never generate a visible HTTP request during a standard dynamic scan. They don't show up in network traffic because the issue isn't what the app does, but what the bundle contains. A purely black-box scan—one that only watches packets flying back and forth—will never touch them, because it never looked inside the file itself.&lt;/p&gt;

&lt;p&gt;If you've ever inspected your own app's bundle "out of curiosity" and found something that shouldn't be there... you know exactly what I'm talking about. And if you haven't, it’s probably worth checking before someone less friendly than you does.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. 🎯 Correlation: Active Testing Against Real Surface Area, Not Assumptions
&lt;/h3&gt;

&lt;p&gt;Once the crawler maps out the complete route tree and finishes the JS bundle analysis, &lt;strong&gt;VORTEX&lt;/strong&gt; fires active security tests—SQLi, XSS, Path Traversal, among others—directly against that discovered surface, rather than firing payloads at a handful of guessed URLs or an outdated sitemap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; SPA / Route Discovery
            │
            ▼
 JavaScript Analysis
            │
            ▼
 Security Assessment

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical difference: scanning cycles are concentrated where there is actually something to attack, instead of wasting time hitting ghost routes that never existed or that no real user could ever reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  👥Why Every Technical Role Should Care
&lt;/h2&gt;

&lt;p&gt;Because no, this isn't "just a security thing." It hits each role differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;👨‍💻&lt;strong&gt;If you're a developer:&lt;/strong&gt; that bundle you just pushed to production with a hardcoded &lt;code&gt;VITE_API_KEY&lt;/code&gt; "just to test on staging"—yeah, that one—is precisely what this crawler is designed to find before an attacker does. It's not personal. It's math.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🚀 &lt;strong&gt;If you're a DevOps / DevSecOps engineer:&lt;/strong&gt; finding an issue like this inside a pull request, before the merge, costs minutes of fixing and a single line in a changelog. Finding that exact same issue three weeks later in production—or worse, reported by an outside party, or worse still, exploited—costs an incident response, an awkward meeting, and likely a post-mortem. Bringing this analysis into the pipeline turns security gating into an early check rather than a late, ceremonial audit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🕵️‍♂️&lt;strong&gt;If you're a pentester:&lt;/strong&gt; you already know the frustration of wasting the first two hours of an engagement simply trying to manually map an application because the automated crawler gave up at the loading screen. An engine that handles that discovery for you—while handing you pre-analyzed bundle results—doesn't replace your judgment; it gives you back your time for what actually requires a human brain: exploitation and impact analysis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡Conclusion
&lt;/h2&gt;

&lt;p&gt;HTML stopped being the application map years ago. The real map lives inside the JavaScript your browser silently executes every single time you load a page. A DAST tool that doesn't understand that isn't evaluating your app: it's evaluating an empty shell with a &lt;code&gt;&amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; inside, congratulating you on how clean it looks.&lt;/p&gt;

&lt;p&gt;If you want to see the crawler running against your own SPA, the Community Edition is still available with no credit card required. And if you test it against something built with Svelte, Solid, Qwik, or any framework outside the usual trio, let me know how it behaves—that feedback is absolute gold for keeping the engine sharp.&lt;/p&gt;




&lt;p&gt;🔗 Useful Links &amp;amp; Documentation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;🌐 &lt;strong&gt;Official Website &amp;amp; Docs:&lt;/strong&gt; &lt;a href="https://zerodayslab.co/docs" rel="noopener noreferrer"&gt;zerodayslab.co/docs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;💻 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://matarturo.github.io" rel="noopener noreferrer"&gt;matarturo.github.io&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>architecture</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>security</category>
    </item>
    <item>
      <title>More Than Just a Scanner: Architecture of a Self-Hosted DAST Engine for SPAs and CI/CD</title>
      <dc:creator>Arturo Enrique Mata Garcia</dc:creator>
      <pubDate>Tue, 08 Sep 2026 20:28:52 +0000</pubDate>
      <link>https://dev.to/matarturo/more-than-just-a-scanner-architecture-of-a-self-hosted-dast-engine-for-spas-and-cicd-2m3j</link>
      <guid>https://dev.to/matarturo/more-than-just-a-scanner-architecture-of-a-self-hosted-dast-engine-for-spas-and-cicd-2m3j</guid>
      <description>&lt;p&gt;Traditional DAST tools are often a massive headache for modern development teams: they are slow, tightly coupled to third-party cloud services, and worst of all, completely blind to Single Page Applications (SPAs) built with React, Vue, or Angular where routing and application logic live entirely on the client side.&lt;/p&gt;

&lt;p&gt;If you are shipping software fast, you need a tool that speaks your language: a native, portable binary with zero heavy runtime dependencies (no Python or pip required in production) that integrates seamlessly into your CI/CD pipelines to enforce real security gatekeeping.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;VORTEX Assessment Engine&lt;/strong&gt;, developed by Arturo Mata.&lt;br&gt;
&lt;em&gt;Official documentation and interactive diagrams are also available at &lt;a href="https://matarturo.github.io" rel="noopener noreferrer"&gt;matarturo.github.io&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;If you are a pentester, DevSecOps engineer, or web developer, you have likely suffered through traditional DAST tools. They are sluggish, heavy, and above all, &lt;strong&gt;clueless about modern web applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SPAs built with React, Vue, or Angular &lt;strong&gt;rely almost entirely on JavaScript&lt;/strong&gt;. Legacy crawlers get lost, fail to discover dynamic routes, and let critical vulnerabilities slip by—alongside exposed frontend secrets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VORTEX Assessment Engine&lt;/strong&gt; was built to solve precisely that problem. It is a &lt;strong&gt;self-hosted&lt;/strong&gt;, high-performance DAST engine engineered specifically to evaluate SPAs with a &lt;strong&gt;deep focus on JavaScript analysis&lt;/strong&gt; and &lt;strong&gt;native CI/CD pipeline integration&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  🔬 Architecture: A Self-Hosted Engine
&lt;/h2&gt;

&lt;p&gt;Unlike commercial SaaS scanners that require shipping your application code to an external cloud, VORTEX runs &lt;strong&gt;entirely within your own infrastructure&lt;/strong&gt;. This guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; Your target application never leaves your secure perimeter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Control:&lt;/strong&gt; You know exactly where and how the scan execution takes place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation-First:&lt;/strong&gt; Easily embed it into a Bash script or native pipeline step.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────┐
│        YOUR INFRASTRUCTURE        │
│                                   │
│   ┌───────────────────────────┐   │
│   │   VORTEX Assessment       │   │
│   │        Engine             │   │
│   └─────────────┬─────────────┘   │
│                 │                 │
│                 ▼                 │
│          WEB APPLICATION          │
│            TARGET                 │
└───────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;🕸️ Why Does VORTEX Understand SPA's?&lt;/p&gt;

&lt;p&gt;Conventional DAST scanners rely heavily on static HTML parsing. VORTEX incorporates an SPA-optimized crawler that performs the following actions:&lt;/p&gt;

&lt;p&gt;Renders and discovers dynamic routing structures across React, Vue, and Angular applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performs deep parsing of JavaScript bundles to hunt down:&lt;/li&gt;
&lt;li&gt;Exposed API keys&lt;/li&gt;
&lt;li&gt;Authentication tokens&lt;/li&gt;
&lt;li&gt;Hardcoded credentials&lt;/li&gt;
&lt;li&gt;Sensitive internal configuration secrets&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;🔬 Evaluation Flow&lt;/p&gt;

&lt;p&gt;A typical security assessment with VORTEX follows this streamlined execution sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               APLICACIÓN WEB
                     │
                     ▼
            ┌─────────────────┐
            │     VORTEX      │
            │     Crawler     │
            └────────┬────────┘
                     │
                     ▼
           SPA / Route Discovery
                     │
                     ▼
          JavaScript Analysis
                     │
                     ▼
          Security Assessment
                     │
                     ▼
            ┌─────────────────┐
            │    RESULTS      │
            └────────┬────────┘
                     │
            ┌────────┼────────┐
            ▼        ▼        ▼
          HTML      JSON     SARIF

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VORTEX fires targeted security tests (SQLi, XSS, Path Traversal, etc.) directly against the discovered surfaces. This rigorous JavaScript inspection uncovers severe flaws that standard black-box scanners routinely miss.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔬 Real-World Environment Demonstration
&lt;/h2&gt;


  
  Your browser does not support the video tag.





&lt;p&gt;⚡ A Native, Frictionless Binary&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VORTEX&lt;/strong&gt; is distributed as a self-contained, native Linux binary. This design delivers distinct performance advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero requirements for Python, pip, or fragile runtime dependencies.&lt;/li&gt;
&lt;li&gt;No Node.js engine required on the target execution host.&lt;/li&gt;
&lt;li&gt;Ultra-lightweight footprint (averaging under 6% CPU utilization and 62 MB of RAM during standard scans).&lt;/li&gt;
&lt;li&gt;This native portability allows you to spin up VORTEX inside staging servers, auditing workstations, or ephemeral Docker containers without complex setup overhead.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🔌 Native CI/CD Integration&lt;/p&gt;

&lt;p&gt;One of the greatest friction points in DevSecOps is integrating security testing without turning pipelines into delivery bottlenecks. VORTEX outputs native SARIF 2.1.0 reports, enabling strict security gatekeeping across popular developer platforms.&lt;/p&gt;

&lt;p&gt;GitHub Actions (.github/workflows/security.yml)&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="s"&gt;YAML&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VORTEX Security Assessment&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vortex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VORTEX Security Scan&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&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;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install VORTEX&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl -sSL [https://raw.githubusercontent.com/matarturo/vortex/main/install.sh](https://raw.githubusercontent.com/matarturo/vortex/main/install.sh) | sudo bash&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run VORTEX&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;vortex [https://my-app.com](https://my-app.com) --output /opt/vortex/reporte/reporte.sarif&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload SARIF&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;github/codeql-action/upload-sarif@v3&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;sarif_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/opt/vortex/reporte/reporte.sarif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitLab CI (.gitlab-ci.yml)&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="s"&gt;YAML&lt;/span&gt;
&lt;span class="na"&gt;vortex_security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl -sSL [https://raw.githubusercontent.com/matarturo/vortex/main/install.sh](https://raw.githubusercontent.com/matarturo/vortex/main/install.sh) | bash&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vortex [https://my-app.com](https://my-app.com) --output /opt/vortex/reporte/reporte.sarif&lt;/span&gt;

  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/opt/vortex/reporte/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Azure DevOps (azure-pipelines.yml)&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="s"&gt;YAML&lt;/span&gt;
&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;curl -sSL [https://raw.githubusercontent.com/matarturo/vortex/main/install.sh](https://raw.githubusercontent.com/matarturo/vortex/main/install.sh) | sudo bash&lt;/span&gt;
    &lt;span class="s"&gt;vortex [https://my-app.com](https://my-app.com) --output /opt/vortex/reporte/reporte.sarif&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;VORTEX&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Assessment'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishBuildArtifacts@v4&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pathToPublish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/opt/vortex/reporte'&lt;/span&gt;
    &lt;span class="na"&gt;artifactName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;VORTEX-Reports'&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;VORTEX&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Reports'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;⚡ Quick Installation&lt;/p&gt;

&lt;p&gt;Want to test the VORTEX Community Edition? No credit card is required to get started.&lt;/p&gt;

&lt;p&gt;Install on Ubuntu / Debian&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; install.sh &lt;span class="o"&gt;[&lt;/span&gt;https://raw.githubusercontent.com/matarturo/vortex/main/install.sh]&lt;span class="o"&gt;(&lt;/span&gt;https://raw.githubusercontent.com/matarturo/vortex/main/install.sh&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x install.sh
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Your First Local Assessment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vortex &lt;span class="nt"&gt;--local&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;PORT_NUMBER]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;🔗 Useful Links &amp;amp; Documentation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Official Website &amp;amp; Docs:&lt;/strong&gt; &lt;a href="https://zerodayslab.co/docs" rel="noopener noreferrer"&gt;zerodayslab.co/docs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/matarturo/vortex" rel="noopener noreferrer"&gt;github.com/matarturo/vortex&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Conclusion &amp;amp; Call to Action&lt;/p&gt;

&lt;p&gt;VORTEX was built to bridge the gap between fast-paced modern software development and offensive security requirements.&lt;/p&gt;

&lt;p&gt;Clone the repository, test out the Community edition against your local SPAs, and drop a star on the repo or share your feedback to help drive the project forward. See you in the code!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>cicd</category>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
