> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vigolium.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scanning a Single-Page Application (SPA)

> How Vigolium handles SPAs built with React, Angular, or Vue using browser-based spidering.

## 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:

```bash theme={null}
vigolium scan -t https://spa.example.com
```

For deeper coverage, use the `deep` strategy which increases spidering depth and interaction aggressiveness:

```bash theme={null}
vigolium scan -t https://spa.example.com --strategy deep
```

If you only want the spidering phase (e.g., for reconnaissance), isolate it with `--only`:

```bash theme={null}
vigolium scan -t https://spa.example.com --only spidering
```

## 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.

> [!NOTE]
>
> **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:

```bash theme={null}
vigolium scan -t https://example.com --only spidering --dry-run
```

## Tuning Spidering

Spidering behavior is configurable via `vigolium-configs.yaml` or CLI flags:

```yaml theme={null}
spidering:
  max_depth: 5          # Maximum navigation depth from the entry point
  max_pages: 100        # Maximum number of pages to visit
  max_duration: 300     # Timeout in seconds for the spidering phase
  tabs: 4               # Crawl several tabs concurrently in one browser (v0.5.3)
  route_seeds: true     # Seed the crawl with client routes found in JavaScript
  require_auth: false   # A failed login is fatal for that target
  interaction:
    click_buttons: true  # Click discovered buttons
    fill_forms: true     # Auto-fill and submit forms
    scroll: true         # Scroll pages to trigger lazy loading
```

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

> [!WARNING]
>
> **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:
>
> ```yaml theme={null}
> spidering:
>   interaction:
>     self_register: true      # complete a public signup form and continue as that account
>     upload_files: true
>     download_files: true
>     accept_confirm: true
> ```
>
> Generated identities use `example.com` (`spidering.identity_email_domain`), so an escaped signup cannot mail a real address.

> [!NOTE]
>
> **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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.