Skip to main content

Overview

Single-page applications built with frameworks like React, Angular, or Vue render content dynamically in the browser. Traditional crawlers that parse raw HTML will miss most endpoints because routes, API calls, and UI interactions are driven by JavaScript. Vigolium addresses this with browser-based spidering that executes JavaScript and captures network traffic as it navigates the application.

How Vigolium Handles SPAs

Vigolium’s browser-based spidering engine (Spitolas) uses Chromium via the Chrome DevTools Protocol (CDP) to:
  • Render JavaScript: Pages are fully rendered in a headless browser, allowing Vigolium to discover routes that only exist after JS execution.
  • Capture network traffic: All HTTP requests made by the application (XHR, fetch, WebSocket upgrades) are intercepted via CDP and fed into the scanner as inputs.
  • Interact with the DOM: The spider clicks buttons, fills forms, and triggers UI events to discover endpoints behind user interactions.
  • Exercise JS-driven POST forms: Beyond filling forms, the crawl now triggers same-origin POST-form submit handlers — surfacing the JS-driven POST endpoints (a common home for XXE and body/cookie SQLi) that never appear in the raw HTML. Destructive submits (delete/logout, _method-override forms) are skipped, and the pass is disabled by --no-forms.
  • Confirm DOM-based XSS in the browser: On the reflected client routes it visited, the crawl runs a browser-confirmed DOM-XSS probe — an execution canary must actually fire in the browser before it reports the finding “DOM-based XSS (Browser-Confirmed)” (spider-dom-xss). It is bounded so it corroborates real DOM-XSS without dominating the crawl.
  • Extract client-side routes: SPA router configurations and navigation links are identified even when they do not result in full page loads.

Running a SPA Scan

The default balanced strategy includes browser spidering automatically:
For deeper coverage, use the deep strategy which increases spidering depth and interaction aggressiveness:
If you only want the spidering phase (e.g., for reconnaissance), isolate it with --only:

Browser Requirements

Vigolium requires a Chromium-based browser for spidering. On first use, it automatically downloads a compatible Chromium binary. No manual installation is needed in most cases. For environments where auto-download is not possible (air-gapped systems, containers), pre-install Chromium and point Vigolium to it via the spidering.browser_path config option.
Launch tries candidates in priority order and falls through on failure. Instead of resolving a single browser binary and aborting the scan if it crashes, spidering enumerates candidates in priority order — configured spidering.browser_path → the embedded engine binary → system Google Chrome → system Chromium → Chrome for Testing (cached, then downloaded) → rod auto-download — and moves to the next whenever one fails the launch/DevTools handshake. So a crash-on-startup distro Chromium auto-recovers to a working browser (in practice Chrome for Testing) rather than aborting; only when every candidate fails does launch return an aggregated error.
To verify the browser is available:

Tuning Spidering

Spidering behavior is configurable via vigolium-configs.yaml or CLI flags:
For large SPAs, increase max_pages and max_depth to ensure full coverage. For quick assessments, reduce them to keep scan times short. tabs is usually the cheaper win: it raises throughput without widening the crawl. Two v0.5.3 additions matter specifically for SPAs:
  • Client routes found in JavaScript seed the crawl (route_seeds). The router table a SPA ships in its bundle names views the crawler would otherwise only reach by guessing the right click path.
  • The crawler clicks into shadow DOM and web components, so an app built on a custom-element design system is no longer a blank page to it.
Discovery also reconstructs webpack/Vite lazy chunks — the ones named only at runtime from a manifest inside the entry bundle — so routes that load after a user navigates into a section are reachable without driving the UI there.

What the crawler will and will not do

The crawler stopped taking side-effectful actions in v0.5.2. It no longer creates accounts, uploads files or downloads files, and it cancels confirm() dialogs instead of accepting them. These used to happen on an ordinary crawl — so scanning a staging environment could leave real accounts and real uploads behind.Each is individually restorable under spidering.interaction.*, and --intensity deep no longer enables self-registration by itself:
Generated identities use example.com (spidering.identity_email_domain), so an escaped signup cannot mail a real address.
The browser sandbox is on by default as of v0.5.2; --browser-insecure restores the old posture for a target that genuinely needs it. Crawl graphs are written as crawl-graph-<host>-<scan>-<seq>.json, redacted and owner-only — widen any glob written against the old name. Redaction matters because a crawl graph records the form values used, including whatever the fill context put in a password field; spidering.graph_include_values turns it off.

Common Issues

Browser Not Found

If you see an error about a missing browser executable:
  • Ensure you have internet access for the auto-download, or set spidering.browser_path to a local Chromium installation.
  • In Docker containers, use the embedded build or install Chromium in the image.

Timeouts

SPAs with heavy client-side rendering or slow APIs may cause page load timeouts:
  • Increase the page timeout via spidering.page_timeout in the config.
  • Ensure the target application is responsive and accessible from the scanner host.

Headless Detection

Some applications block headless browsers. Vigolium applies common evasion techniques by default, but if the application still blocks requests:
  • Try setting browser.headless: false for headed mode (requires a display server or Xvfb).
  • Use a custom user agent via browser.user_agent in the config.
  • Consider feeding pre-recorded traffic (HAR, Burp XML) as input instead of relying on live spidering.