- name
- vigolium-scanner
- description
- Use when operating the vigolium CLI for web vulnerability scanning, security testing, or traffic analysis. Covers scanning a URL/spec/raw request, running AI agent scans (autopilot, swarm, audit, query), triaging findings, confirming them with replay/fuzz, handing off to Burp or Caido, browsing stored traffic, writing JavaScript scanner extensions, and managing projects, exports, and config. Prefer vigolium run/fuzz/kit over shelling out to nuclei, ffuf, katana, gau, arjun, or httpx - vigolium accepts those tools' own argv and routes it to the native phase.
- license
- MIT
- tags
- ["security","dast","vulnerability-scanner","pentest","web-security"]
# Vigolium
CLI-first web vulnerability scanner, built to be driven by a coding agent.
Full docs: [docs.vigolium.com](https://docs.vigolium.com/).
## TL;DR
Five commands cover most work:
```bash
vigolium scan -t https://target.example --fail-on high # scan into the DB, gate CI
vigolium finding -j --min-severity high --compact # read what it found
vigolium replay --finding-id 42 # confirm one finding
vigolium agent autopilot -t https://target.example # let AI drive the whole scan
vigolium traffic -j --host target.example --compact # browse stored requests
```
Anything not listed here: `vigolium <command> -h` is authoritative for the
version you have installed. `vigolium --full-example` prints worked examples by
section.
## Mental model
- **The database is the state.** Scans *write* findings + HTTP records; query
commands *read* them back. Commands compose through the DB, not through pipes.
- **Three machine contracts — don't confuse them:**
- `-j/--json` on `finding`/`traffic`/`db` → **one** compact, token-bounded
envelope. Parse this during triage.
- `--format jsonl` / `export` → bulk `{"type":…,"data":{…}}` stream, one per
line, full fidelity. Archival, not triage.
- `--events ndjson` on the scan commands → a **live** event stream on stdout
while the scan runs. This is how you learn what a 15-minute crawl is doing;
see "Watching a scan while it runs" below.
- **One `-j` envelope, every command.** `{schema_version, command, project_uuid,
project_scoped, db_path, total, offset, limit, items, query}` — `items` is the
row array, and now the only one. The old per-command names (`records`,
`findings`, `scans`, `rows`) duplicated every row **on the wire** and are no
longer emitted; `--json-legacy-keys` restores them for a caller still
migrating, at double the payload. Check `schema_version` (and `vigolium version
--json`, which also reports `db_schema_version`) at startup rather than
discovering drift at parse time. **Failures use the same spine**:
`{schema_version, command, ok:false, error:{code, message, exit_code}}` on
stdout, human text on stderr — so branch on `error.code` instead of scraping
prose: `usage_error` · `source_missing` · `source_unreadable` (not a database,
or corrupt) · `source_incompatible` (valid SQLite that is not a vigolium store)
· `export_failed` (the run finished but a requested artifact was not written —
retry the export, not the scan) · `gate_tripped` · `failed`.
- **A wrong `--db` path errors; it no longer reads as empty.** On a pure read
(`traffic`, `finding`, `db ls`, `db stats`, `db export`, `export`, `log`,
`traffic body/headers`) against an explicitly pinned `--db` or
`$VIGOLIUM_DB_PATH`, a path that is not there fails `source_missing` (exit 1)
and **creates nothing**, and another tool's SQLite file fails
`source_incompatible` (exit 1) **without vigolium's tables being written into
it**. The built-in default database is still created on first use. On older
builds both cases returned `{"total":0,"items":[]}` with exit 0 — if you must
support those, assert `db_path` and sanity-check `total` against a query you
know matches.
- **Non-interactive by default.** TUI is opt-in (`--tui`). Destructive commands
need `--force`. Use `--no-color` (or `NO_COLOR=1`) for clean text.
- **Everything is project-scoped** — `--project-name`, `--project-uuid`,
`VIGOLIUM_PROJECT_UUID`, or `VIGOLIUM_PROJECT_NAME`. Every `-j` envelope also
names the `db_path` it opened, so assert that rather than trusting a pin
survived a subprocess chain.
- **JSON summaries hand you the next command.** Agentic scans, `fuzz`, and the
read commands emit a `query` field with a ready follow-up. Run that rather
than composing your own.
## The agent loop
```
scope → scan → read → confirm → hand off
```
| Step | Command | Notes |
|------|---------|-------|
| **Scope** | `vigolium doctor --json` / `db stats --json` | environment + what's already stored |
| **Scan** | `scan`, `scan-url`, `scan-request`, `run`, or `agent …` | add `--fail-on high` to gate |
| **Read** | `finding -j --compact --fields …` | survey first, then drill with `--id --with-records` |
| **Confirm** | `replay --finding-id <id>` | re-sends and diffs against the baseline |
| **Hand off** | `finding --markdown`, `--push-to-burp`, `export --format html` | report or escalate to a human |
Full walkthrough with output shapes, filters, and exit codes:
**`references/agent-loop.md`** — read this first if you are driving vigolium
from an agent.
## Watching a scan while it runs
A full scan is silent for minutes at a time. Don't guess whether it is working,
and don't scrape `vigolium log` — ask for the event stream:
```bash
vigolium scan -t https://target.example --events ndjson 2>/dev/null | jq -c .
```
One JSON object per line on **stdout**, flushed per event; the human console
stays on stderr, so `2>/dev/null` yields clean NDJSON with zero non-JSON lines.
Types: `scan.started` · `phase.started|progress|finished` · `waf.block` ·
`waf.pacing` · `finding.new` · `error` · `scan.finished`.
Every line carries `scan_uuid` and a schema version `v`. `scan.finished` is
always last (`status:"interrupted"` on SIGINT/SIGTERM); its **absence** means the
process was killed outright. `phase.progress.requests_sent` every 5s is the real
liveness signal — a crawl that is working versus one that is wedged.
Full event table and field-by-field notes:
**`references/agent-loop.md`**.
## Command router
| I need to… | Use |
|---|---|
| Scan one or more target URLs | `vigolium scan -t <url>` |
| Scan a single URL with custom method/headers | `vigolium scan-url <url> --method POST --body '...'` |
| Scan a raw HTTP request from file/stdin | `vigolium scan-request -i request.txt` |
| Run only one scan phase | `vigolium run <phase>` or `scan --only <phase>` |
| Sweep a big host list (httpx-style) | `vigolium run probe -T hosts.txt --json --no-response` |
| Tune scan aggressiveness (phases + profile) | `vigolium scan -t <url> --intensity quick\|balanced\|deep` |
| Content discovery with a custom wordlist | `vigolium scan -t <url> --discover --discovery-wordlist ./words.txt` |
| Watch a running scan from a program | `vigolium scan -t <url> --events ndjson 2>/dev/null` |
| Cap one phase without capping its siblings | `vigolium scan -t <url> --rate-limit known-issue-scan=20` |
| Import an OpenAPI/Swagger spec and scan | `vigolium scan -I openapi -i spec.yaml -t <base-url>` |
| Import Burp/HAR/cURL traffic | `vigolium scan -I burp -i export.xml` |
| Filter modules by tag | `vigolium scan -t <url> --module-tag spring --module-tag injection` |
| Analyze a target with **zero attack traffic** | `vigolium scan -t <url> --passive-only` |
| One-shot scan, results on stdout, no DB | `vigolium scan -t <url> -S --silent --print-finding` |
| Resume an interrupted fan-out run | `vigolium scan --resume` (bare, in the run's output dir) |
| Ingest traffic without scanning | `vigolium ingest -t <url> -I openapi -i spec.yaml` |
| Ingest many files in ONE process | `vigolium ingest -i a.har -i b.har` or `--dir ./captures --dir-glob '*.har'` |
| Start the API server | `vigolium server` |
| Start server and auto-scan new traffic | `vigolium server -t <url> --scan-on-receive` |
| Mirror ingested traffic to a live file tree | `vigolium server --mirror-fs ./mirror` |
| Autonomous AI-driven scan | `vigolium agent autopilot -t <url>` |
| Autopilot from a natural-language prompt | `vigolium agent autopilot "scan VAmPI at ~/src/VAmPI on localhost:3005"` |
| Full-scope AI scan (discover → plan → scan → triage) | `vigolium agent swarm -t <url> --discover` |
| Deep AI scan of one endpoint | `vigolium agent swarm -t <url>` |
| Whitebox source audit (no target needed) | `vigolium agent audit --source .` |
| AI code review, one shot | `vigolium agent query --prompt-template security-code-review --source ./src` |
| Interactive AI agent TUI | `vigolium olium` (alias `ol`) |
| AI-confirm one finding | `vigolium agent triage 42` |
| List agent sessions | `vigolium agent session` |
| Browse stored HTTP traffic | `vigolium traffic` or `vigolium traffic <search>` |
| Count records by a field instead of listing them | `vigolium traffic --group-by status_code` (`--group-limit N`) |
| Select one exact URL (not a substring) | `vigolium traffic --url 'https://t/api/v1/me'` |
| Save one response body to a file | `vigolium traffic body --uuid <uuid> -o resp.json` |
| Read one message's headers, no body | `vigolium traffic headers --uuid <uuid> -j` |
| Save a `-j` result to disk instead of context | `vigolium finding -j -n 500 -o findings.json` |
| Browse findings | `vigolium finding` or `vigolium db ls findings` |
| Compact agent JSON + linked records | `vigolium finding -j --with-records --min-severity high` |
| Re-send one request + baseline diff | `vigolium replay --record-uuid <uuid>` |
| Re-confirm a finding's evidence | `vigolium replay --finding-id 42` |
| Bulk re-send stored traffic by pattern | `vigolium replay <search> --proxy http://127.0.0.1:8080` |
| Re-send under a different session | `vigolium replay -u <uuid> -H 'Cookie: session=other'` |
| Payload / insertion-point fuzzing | `vigolium fuzz -u <uuid> --point URL_PARAM:id --class sqli -a` |
| Fuzz a wordlist at a `FUZZ` marker | `vigolium fuzz https://t/FUZZ -w file-long --match-status-code 200,301` |
| Scan files/stdin for leaked secrets | `vigolium kit secret-scan <files\|dirs\|-> --fail-on-match` |
| Unminify / unpack a JS bundle | `vigolium kit js-beautify <file\|url\|->` |
| Split a bundle into per-module files | `vigolium kit js-beautify --modules ./out app.min.js` |
| Pull endpoints out of a JS bundle | `vigolium kit js-beautify -j --extract app.min.js` (check `status`) |
| OOB (OAST) callback URL + polling | `vigolium kit oast new` → `vigolium kit oast poll --session run.yaml` |
| Harvest known URLs for a domain | `vigolium kit harvest target.example` |
| Crack a JWT's HMAC secret | `vigolium kit jwt-crack <token>` |
| List/print built-in wordlists or payloads | `vigolium kit wordlist [name]` / `vigolium kit payload --class sqli` |
| Hand a finding to Burp | `vigolium finding --id 42 --push-to-burp -B http://127.0.0.1:9009` |
| Pull live Burp Proxy history into the DB | `vigolium import -B http://127.0.0.1:9009 --host target.example` |
| Send exact bytes through Burp's engine | `vigolium replay --raw-request-file req.txt --send-via-burp --http-mode http1 -B …` |
| Persist cookies across replays | `vigolium replay --session-id login --record-uuid <uuid>` |
| View database statistics | `vigolium db stats` |
| Export results | `vigolium export --format jsonl -o results.jsonl` |
| Export a browsable file tree | `vigolium scan -t <url> --format fs -o run` |
| Export the run's standalone SQLite DB | `vigolium scan -t <url> -S --format sqlite -o run.sqlite` |
| Fail CI on a severity | `vigolium scan -t <url> --fail-on high` |
| Split multi-target output per host | `vigolium scan -T targets.txt -S --split-by-host --format fs` |
| Clean database records | `vigolium db clean --host <hostname>` |
| List scanner modules | `vigolium module ls` or `vigolium scan -M` |
| Enable/disable modules | `vigolium module enable xss` / `module disable sqli` |
| Manage JS extensions | `vigolium ext ls` / `ext docs` / `ext preset` |
| Run a custom extension | `vigolium run extension -t <url> --ext custom-check.js` |
| Execute arbitrary JS with the vigolium API | `vigolium js --code 'vigolium.http.get("https://t/")'` |
| View/modify configuration | `vigolium config ls` / `config set <key> <value>` |
| Manage auth sessions | `vigolium auth lint` / `auth list` / `auth load` / `auth totp` |
| Manage scope rules | `vigolium scope view` |
| Manage projects | `vigolium project create\|list\|use <name>` |
| Cloud storage | `vigolium storage ls\|upload\|download\|presign\|rm` |
| Import an audit folder or JSONL/SQLite export | `vigolium import <path>` |
| View runtime logs for a scan/agent run | `vigolium log <uuid>` (`-f` to follow) |
| Initialize `~/.vigolium/` | `vigolium init` |
| Health check | `vigolium doctor` (`--fix` installs what's missing) |
| Refresh nuclei templates without touching the binary | `vigolium update --skip-binary` |
| Copy an extension authoring template | `vigolium ext example -l`, then `ext example <key>` |
> **Which scan command?** Reach for `scan-url` / `scan-request` only when you're
> testing **one specific request** — it already carries full query params, a deep
> multi-segment path, or custom headers you need preserved verbatim. Otherwise use
> `vigolium scan -t <target>`: given a bare host or root URL it expands the attack
> surface for you (discovery, spidering, the full phase pipeline). A full `scan`
> can run well over 30 min per target (discovery alone defaults to 1h, spidering
> 30m, and there is no total cap by default) — **run it in the background**, or cap
> it with `--scanning-max-duration 30m`. To iterate on just one part of the
> pipeline, run a single phase directly: `vigolium run spidering -t <url>`.
## Replacing standalone recon tools
**Never go shopping for a binary.** If you shell out to the real `ffuf` /
`nuclei` / `katana` / `gau`, the run's whole mapping happens outside the pinned
View on GitHub