{"id":51521731,"url":"https://github.com/praetorian-inc/vespasian","last_synced_at":"2026-07-08T16:30:41.320Z","repository":{"id":351884379,"uuid":"1144876824","full_name":"praetorian-inc/vespasian","owner":"praetorian-inc","description":"API discovery tool that maps attack surfaces from captured traffic and generates specs for REST, GraphQL, SOAP, and WebSocket APIs","archived":false,"fork":false,"pushed_at":"2026-07-07T15:57:22.000Z","size":17700,"stargazers_count":106,"open_issues_count":11,"forks_count":6,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-07T17:05:26.201Z","etag":null,"topics":["api-discovery","api-enumeration","api-security","api-specification","application-security","attack-surface","burp-suite","capability","golang","graphql","headless-browser","openapi","openapi-generator","penetration-testing","security-tools","soap","traffic-analysis","wsdl"],"latest_commit_sha":null,"homepage":"https://github.com/praetorian-inc/vespasian","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/praetorian-inc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-29T06:32:29.000Z","updated_at":"2026-07-07T15:57:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/praetorian-inc/vespasian","commit_stats":null,"previous_names":["praetorian-inc/vespasian"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/praetorian-inc/vespasian","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/praetorian-inc%2Fvespasian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/praetorian-inc%2Fvespasian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/praetorian-inc%2Fvespasian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/praetorian-inc%2Fvespasian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/praetorian-inc","download_url":"https://codeload.github.com/praetorian-inc/vespasian/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/praetorian-inc%2Fvespasian/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35271852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-discovery","api-enumeration","api-security","api-specification","application-security","attack-surface","burp-suite","capability","golang","graphql","headless-browser","openapi","openapi-generator","penetration-testing","security-tools","soap","traffic-analysis","wsdl"],"created_at":"2026-07-08T16:30:40.790Z","updated_at":"2026-07-08T16:30:41.311Z","avatar_url":"https://github.com/praetorian-inc.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/praetorian-inc/vespasian\"\u003e\n    \u003cimg src=\"docs/images/banner.png\" alt=\"Vespasian — API discovery and specification generation from live HTTP traffic\" width=\"100%\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eDiscover API endpoints from real HTTP traffic. Generate OpenAPI, GraphQL SDL, and WSDL specs automatically.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/praetorian-inc/vespasian/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/praetorian-inc/vespasian/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/praetorian-inc/vespasian\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/praetorian-inc/vespasian\" alt=\"Go Report Card\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\" alt=\"License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n# Vespasian: API Discovery and Specification Generation Tool\n\n**Vespasian discovers API endpoints by observing real HTTP traffic and generates API specification files from those observations.** It captures traffic through headless browser crawling or imports it from existing sources (Burp Suite XML exports, HAR files, and mitmproxy dumps), then classifies requests, probes discovered endpoints, and outputs specifications in the native format for each API type: OpenAPI 3.0 for REST, GraphQL SDL for GraphQL, and WSDL for SOAP services.\n\nBuilt for penetration testers and security engineers who need to map the API attack surface of web applications, single-page apps, and microservices when the API documentation is not available.\n\n## Why Vespasian?\n\nModern applications make API calls dynamically. Single-page applications construct requests at runtime via JavaScript. Mobile apps call APIs through native HTTP clients. Real-time features communicate over WebSocket connections. Static analysis and source code review miss these runtime behaviors entirely.\n\nExisting approaches to API discovery have limitations:\n\n- **Checking known paths** (`/swagger.json`, `/openapi.yaml`) only finds APIs that are explicitly documented\n- **Static analysis** cannot observe requests that are constructed dynamically at runtime\n- **Manual proxy capture** is time-consuming and produces raw traffic without structured specifications\n\nVespasian takes a different approach: it observes actual network traffic at the wire level, then uses classification heuristics and active probing to produce structured API specifications automatically. Because this is inherently probabilistic, Vespasian discovers only the endpoints present in the captured traffic, but it reliably maps the API surface that an application actually exposes during use.\n\n## Key Features\n\n| Feature | Description |\n|---------|-------------|\n| **REST API Discovery** | Classifies REST endpoints via content-type, path patterns, and response structure; outputs OpenAPI 3.0 |\n| **GraphQL API Discovery** | Detects GraphQL endpoints, runs tiered introspection queries, and generates GraphQL SDL schemas |\n| **WSDL/SOAP Discovery** | Identifies SOAP services via SOAPAction headers and envelope detection; fetches and parses WSDL documents |\n| **API Type Auto-Detection** | Automatically determines API type (REST, GraphQL, WSDL) from captured traffic without manual selection |\n| **Browser Crawling** | Two backends: headless mode drives Chrome via [go-rod](https://github.com/go-rod/rod) for full JavaScript/SPA support; non-headless mode uses a stdlib net/http engine (DFS, 150 rps, scope+SSRF redirect guard) for lightweight crawls |\n| **SPA Bundle Extraction** | Post-crawl pass that scans JavaScript bundles for API path strings and probes them with raw HTTP, recovering endpoints the headless browser could not exercise |\n| **Static Form Extraction** | Statically parses `\u003cform\u003e` elements in captured HTML responses — including login, search, and admin forms — to surface submission endpoints and parameters that dynamic crawling may never trigger |\n| **Traffic Import** | Import existing captures from Burp Suite XML, HAR 1.2 files, and mitmproxy dumps |\n| **Active Probing** | OPTIONS discovery, JSON schema inference, WSDL document fetching, and GraphQL introspection |\n| **Path Normalization** | `/users/42` and `/users/87` become `/users/{id}` with known literal preservation (`/me`, `/self`) |\n| **SSRF Protection** | Blocks crawling and probing of private and loopback addresses by default. Pass `--dangerous-allow-private` to test internal targets (localhost, 127.0.0.1, RFC1918, link-local); the flag is required when the seed URL is itself a private host. |\n| **JS Bundle Static Analysis** | Statically analyses captured JavaScript bundles to recover API endpoints, path parameters, and request-body fields missed by dynamic crawling. Enabled by default via `--analyze-js`; sourcemap recovery is controlled by `--fetch-sourcemaps` (default: `true` for `scan`/`crawl`, `false` for `generate`). |\n| **Proxy Support** | Route headless browser traffic through Burp Suite or other intercepting proxies |\n| **Two-Stage Pipeline** | Capture once, generate many: separate capture and generation steps for maximum flexibility |\n\n## How It Works\n\nVespasian uses a two-stage pipeline that separates traffic capture from specification generation:\n\n```mermaid\nflowchart LR\n    subgraph Capture\n        A[\"Crawler\u003cbr/\u003eheadless go-rod or net/http\"] --\u003e C[\"capture.json\u003cbr/\u003eObservedRequest array\"]\n        B[\"Traffic Importers\u003cbr/\u003eBurp Suite XML, HAR, mitmproxy\"] --\u003e C\n    end\n    subgraph Generate\n        C --\u003e S[\"Static Analyzer\u003cbr/\u003eHTML form extraction\"]\n        S --\u003e D[\"Classifier\u003cbr/\u003eREST, GraphQL, WSDL\"]\n        D --\u003e E[\"Prober\u003cbr/\u003eOPTIONS, schema, WSDL, introspection\"]\n        E --\u003e F[\"Spec Generator\u003cbr/\u003eOpenAPI 3.0, GraphQL SDL, WSDL\"]\n    end\n```\n\n**Why two stages:**\n\n- **Capture once, generate many.** Run different generators against the same capture without re-scanning.\n- **Debuggable.** The capture file is inspectable JSON, isolating capture bugs from generation bugs.\n- **Composable.** Import traffic from any source (browser crawls, proxy captures, mobile testing).\n- **Offline analysis.** Generate specifications without network access, useful during limited engagement windows.\n\n\u003e **Breaking change:** The `query_params` field of `capture.json` is now `map[string][]string` (multi-value). Capture files generated by previous versions must be regenerated.\n\n### SPA Bundle Extraction\n\nMany single-page applications bundle their API paths as string literals inside\nJavaScript files (`/api/v2/users`, `` `/api/items/${id}` ``,\n`\"identity/\" + \"api/auth/login\"`). The headless browser only exercises the\npaths the user actually clicks on; the rest stay hidden. After the crawl,\nVespasian re-scans every captured JavaScript bundle for API-path patterns\nand probes the discovered URLs with raw HTTP requests. Wrong combinations\n(typically caused by the regex matching unrelated string literals) come back\n404 and are dropped.\n\nThe extractor also reconstructs paths built by runtime string concatenation —\nboth `String.prototype.concat` (`\"/api/posts/\".concat(id, \"/comment\")`) and the\n`+` operator (`\"/api/users/\" + uid + \"/profile\"`). Operands that are not string\nliterals (identifiers, function calls, expressions) are replaced with a numeric\nplaceholder, so `\"/api/posts/\".concat(id, \"/comment\")` becomes the probeable\npath `/api/posts/0/comment`, which the OpenAPI generator then parameterizes to\n`/api/posts/{postId}/comment`.\n\nBy default this step:\n\n- only probes URLs whose origin matches the scan target — cross-origin URLs\n  embedded in the bundle are skipped to avoid using Vespasian as a request\n  reflector;\n- never forwards `--header` values (Authorization, Cookie, ...) to\n  cross-origin destinations;\n- enforces SSRF protection on every URL unless `--dangerous-allow-private`\n  is set;\n- caps probe attempts at 500 per scan and total wall-clock time at 10\n  minutes for the JS-replay step.\n\nIf a discovered SPA bundle is over 1 MB (the default crawl truncation\nlimit), Vespasian re-fetches it with a 10 MB cap before scanning so paths\nembedded after the truncation point are still recovered.\n\n## How to Install Vespasian\n\n### Install from Source (Go)\n\n```bash\ngo install github.com/praetorian-inc/vespasian/cmd/vespasian@latest\n```\n\n### Download Pre-Built Binary\n\nDownload the latest binary for your platform from the [Releases](https://github.com/praetorian-inc/vespasian/releases) page.\n\n### Build from Source\n\n```bash\ngit clone https://github.com/praetorian-inc/vespasian.git\ncd vespasian\nmake build\n```\n\n## How to Discover APIs with Vespasian\n\n### Quick Start: Scan a Web Application\n\n```bash\n# Crawl and generate an API spec in one step (auto-detects API type)\nvespasian scan https://app.example.com -o api.yaml\n\n# With authentication\nvespasian scan https://app.example.com -H \"Authorization: Bearer \u003ctoken\u003e\" -o api.yaml\n\n# Specify the API type explicitly\nvespasian scan https://app.example.com --api-type graphql -o schema.graphql\n```\n\n### Two-Stage Workflow\n\n```bash\n# Stage 1: Capture traffic via headless browser\nvespasian crawl https://app.example.com -o capture.json\n\n# Stage 1 (alternative): Import traffic from Burp Suite\nvespasian import burp traffic.xml -o capture.json\n\n# Stage 1 (alternative): Import traffic from HAR archive\nvespasian import har recording.har -o capture.json\n\n# Stage 1 (alternative): Import traffic from mitmproxy\nvespasian import mitmproxy flows -o capture.json\n\n# Stage 2: Generate OpenAPI spec for REST\nvespasian generate rest capture.json -o api.yaml\n\n# Stage 2: Generate GraphQL SDL schema\nvespasian generate graphql capture.json -o schema.graphql\n\n# Stage 2: Generate WSDL from SOAP traffic\nvespasian generate wsdl capture.json -o service.wsdl\n```\n\n### Common Options\n\n```bash\n# Route crawl traffic through Burp Suite\nvespasian scan https://app.example.com --proxy http://127.0.0.1:8080 -o api.yaml\n\n# Scan a local/private target (bypasses SSRF protection)\nvespasian scan http://localhost:3000 --dangerous-allow-private -o api.yaml\n\n# Verbose output to see discovered requests in real-time\nvespasian scan https://app.example.com -v -o api.yaml\n\n# Suppress the startup banner\nvespasian --no-banner scan https://app.example.com -o api.yaml\n```\n\n#### Path normalization \u0026 `--merge-slugs`\n\nIDs (numeric, UUID, hashes like `/users/42`) are always normalized to parameters regardless of flags. Observation-based \"slug\" merging is opt-in via `--merge-slugs` (off by default):\n\n- Feature routes (default): `/feature/login`, `/feature/export` stay distinct.\n- Blog/CMS content (use `--merge-slugs`): `/posts/hello-world`, `/posts/my-trip` → `/posts/{postSlug}`\n\nOptionally, when using `--merge-slugs`, you can also configure `--slug-threshold` (default 2) to set how many distinct values must appear at a position before merging paths into a slug; higher is more conservative.\n\n## Use Cases\n\n### Penetration Testing without API Documentation\n\nDuring authorized security assessments, clients often cannot provide API documentation. Vespasian crawls the target application with a headless browser, captures every API call the frontend makes, and produces specifications that describe the discovered endpoints, parameters, and response schemas.\n\n### Generating API Specs from Existing Proxy Captures\n\nPentesters already capture traffic in Burp Suite and mitmproxy during manual testing. Rather than re-crawling, Vespasian can import that traffic and generate specifications from work already done. This is especially useful for mobile application testing, where no browser crawl can observe the API calls.\n\n### Mapping API Attack Surface for Web Applications\n\nFor attack surface management, Vespasian identifies which API endpoints a web application exposes by executing its JavaScript and intercepting all outbound requests. The resulting specification can feed into further security testing tools that accept OpenAPI, GraphQL SDL, or WSDL input.\n\n### Feeding into Hadrian for Authorization Testing\n\nGenerate an API specification with Vespasian, then pass it directly to [Hadrian](https://github.com/praetorian-inc/hadrian) for automated OWASP API Top 10 authorization testing. This creates a complete discover-then-test workflow.\n\n## API Type Support\n\nVespasian classifies and generates specifications for three API types:\n\n| API Type | Classification Signals | Output Format | Probing |\n|----------|----------------------|---------------|---------|\n| **REST** | JSON/XML content-type, `/api/` `/v1/` path patterns, HTTP methods | OpenAPI 3.0 (YAML/JSON) | OPTIONS discovery, JSON, urlencoded, and multipart request-body inference |\n| **GraphQL** | `/graphql` path, query structure in POST body, `data`/`errors` response keys | GraphQL SDL | Tiered introspection queries (3 tiers for WAF bypass) |\n| **WSDL/SOAP** | SOAPAction header, SOAP envelope in body, `?wsdl` URL parameter | WSDL XML | Active `?wsdl` document fetching |\n\n### REST Classification Heuristics\n\n1. **Content-type**: responses with `application/json` or `application/xml`\n2. **Static asset exclusion**: drops `.js`, `.css`, `.png`, `.woff`, `/static/`, `/assets/`\n3. **Path heuristics**: `/api/`, `/v1/`, `/v2/`, `/v3/`, `/rest/`, `/rpc/` paths boost confidence\n4. **HTTP method**: POST/PUT/PATCH/DELETE to non-page URLs\n5. **Response structure**: JSON object or array bodies (not HTML)\n\n### GraphQL Classification Heuristics\n\n1. **Path matching**: `/graphql` path (0.70 confidence)\n2. **Query structure**: GraphQL query syntax in POST body (0.85 confidence)\n3. **Response structure**: `data`/`errors` keys in response (0.80 confidence)\n4. **Combined signals**: path + body together (0.95 confidence)\n\n### GraphQL Introspection\n\nVespasian uses a tiered introspection strategy to handle WAF-protected GraphQL servers:\n\n- **Tier 1**: Full introspection with descriptions, deprecation, and directives\n- **Tier 2**: Minimal-complete query without descriptions, deprecation info, or directives\n- **Tier 3**: Minimal last-resort query with the smallest payload\n- **Fallback**: Traffic-based inference from observed queries and mutations when introspection is disabled\n\n## CLI Reference\n\n### `vespasian scan`\n\nConvenience command that crawls a target and generates a specification in one step.\n\n```\nvespasian scan \u003curl\u003e [flags]\n  --api-type         API type: auto, rest, graphql, wsdl (default: auto)\n  -H, --header       Auth headers to inject (repeatable)\n  -o, --output       Output spec file (default: stdout)\n  --depth            Max crawl depth (default: 3)\n  --max-pages        Max pages to visit (default: 100)\n  --timeout          Maximum duration for the entire scan (default: 10m)\n  --scope            same-origin or same-domain (default: same-origin)\n  --headless         Headless Chrome mode (default: true); --headless=false uses the stdlib net/http engine\n  --proxy            Proxy URL for headless browser (e.g., http://127.0.0.1:8080)\n  --confidence       Min classification confidence (default: 0.5)\n  --probe            Enable active probing (default: true)\n  --deduplicate      Deduplicate endpoints before probing (default: true)\n  --dangerous-allow-private  Disable SSRF protection for crawling and probes,\n                     allowing private/localhost targets (localhost, 127.0.0.1,\n                     RFC1918, link-local). Required when the seed URL is a\n                     private host, otherwise the crawl exits with an error and\n                     captures nothing. WARNING: Do not use on production\n                     systems.\n  --no-request-id    Disable auto X-Vespasian-Request-Id header\n  -v, --verbose      Show requests in real-time\n```\n\n### `vespasian crawl`\n\nCaptures HTTP traffic from the target application. By default it drives a headless Chrome browser (go-rod) for full JavaScript/SPA support; with `--headless=false` it uses a dependency-free stdlib net/http engine (no Chrome required).\n\n```\nvespasian crawl \u003curl\u003e [flags]\n  -H, --header       Auth headers to inject (repeatable)\n  -o, --output       Capture output file (default: stdout)\n  --depth            Max crawl depth (default: 3)\n  --max-pages        Max pages to visit (default: 100)\n  --timeout          Maximum duration for the entire crawl (default: 10m)\n  --scope            same-origin or same-domain (default: same-origin)\n  --headless         Headless Chrome mode (default: true); --headless=false uses the stdlib net/http engine\n  --proxy            Proxy URL for headless browser (e.g., http://127.0.0.1:8080)\n  --dangerous-allow-private  Disable SSRF protection for crawling, allowing\n                     private/localhost targets (localhost, 127.0.0.1, RFC1918,\n                     link-local). Required when the seed URL is a private\n                     host, otherwise the crawl exits with an error and\n                     captures nothing. WARNING: Do not use on production\n                     systems.\n  --no-request-id    Disable auto X-Vespasian-Request-Id header\n  -v, --verbose      Show requests in real-time\n```\n\n### `vespasian import`\n\nConverts traffic captures from external tools and formats into the Vespasian capture format.\n\n```\nvespasian import \u003cformat\u003e \u003cfile\u003e [flags]\n  Formats: burp, har, mitmproxy\n  -o, --output       Capture output file (default: stdout)\n  -v, --verbose      Show imported requests\n```\n\n### `vespasian generate`\n\nProduces an API specification from a capture file.\n\n```\nvespasian generate \u003capi-type\u003e \u003ccapture-file\u003e [flags]\n  API types: rest, graphql, wsdl\n  -o, --output       Output file (default: stdout)\n  --confidence       Min classification confidence (default: 0.5)\n  --probe            Enable active probing (default: true)\n  --deduplicate      Deduplicate endpoints before probing (default: true)\n  --dangerous-allow-private  Disable SSRF protection on the probe path\n                     (OPTIONS/schema/WSDL-fetch/GraphQL introspection) for\n                     private/localhost targets. WARNING: Do not use on\n                     production systems.\n  -v, --verbose      Show discovered endpoints\n```\n\n## Architecture\n\n### Pipeline Components\n\n| Component | Purpose | Supported Types |\n|-----------|---------|-----------------|\n| **Crawler** | Two backends: go-rod headless Chrome (JavaScript/SPA support) and stdlib net/http (lightweight, DFS, 150 rps, SSRF guard) | Protocol-agnostic |\n| **Importers** | Convert Burp Suite XML, HAR, and mitmproxy traffic to capture format | All three formats |\n| **Classifier** | Separates API calls from static assets using heuristics | REST, GraphQL, WSDL |\n| **Prober** | Enriches endpoints via active requests | OPTIONS, JSON schema, WSDL fetch, GraphQL introspection |\n| **Generator** | Produces specification files from classified and probed traffic | OpenAPI 3.0, GraphQL SDL, WSDL |\n\n### Package Layout\n\n```\ncmd/vespasian/          CLI entry point\ninternal/pipeline/      Shared crawl/classify/probe/generate orchestration (CLI + SDK)\npkg/sdk/                capability-sdk Capability adapter (used by Guard hosts)\npkg/crawl/              Crawler (headless go-rod + net/http backends) + capture format\npkg/importer/           Traffic importers (Burp, HAR, mitmproxy)\npkg/analyze/            Static HTML form extraction from captured response bodies\npkg/classify/           API classification (REST, GraphQL, WSDL)\npkg/probe/              Endpoint probing (OPTIONS, schema, WSDL, GraphQL introspection)\npkg/generate/\n  ├── rest/             OpenAPI 3.0 generation, path normalization, schema inference\n  ├── graphql/          GraphQL SDL generation, introspection, traffic inference\n  └── wsdl/             WSDL generation, SOAP operation extraction\n```\n\nFor a deeper reference on the crawler — interface, backends, options, SSRF model, and how to add a new backend — see [docs/crawler.md](docs/crawler.md).\n\n## Frequently Asked Questions\n\n### What types of APIs can Vespasian discover?\n\nVespasian discovers **REST APIs** (generating OpenAPI 3.0 specs), **GraphQL APIs** (generating SDL schemas via introspection or traffic inference), and **SOAP/WSDL services** (generating WSDL documents). It automatically detects the API type from captured traffic, or you can specify it explicitly with `--api-type`.\n\n### How is Vespasian different from running a web crawler?\n\nStandard web crawlers follow HTML links and index pages. Vespasian intercepts **all HTTP traffic** from a headless browser, including XHR/fetch API calls, WebSocket upgrades, and dynamically constructed requests that don't appear in HTML. It then classifies those requests by API type and generates structured specifications, not just URL lists.\n\n### Does Vespasian find undocumented APIs?\n\nVespasian discovers any API endpoint that the application calls during the crawl. If the frontend calls `/api/internal/debug` at runtime, Vespasian will capture and document it, even if it doesn't appear in any published API documentation.\n\n### Can I use Vespasian with traffic I've already captured?\n\nYes. If you've already captured traffic using Burp Suite, browser dev tools (HAR), or mitmproxy, use `vespasian import` to convert it to the capture format, then `vespasian generate` to produce specifications. No re-crawling needed.\n\n### Does Vespasian handle GraphQL servers that disable introspection?\n\nYes. Vespasian uses a tiered introspection strategy. If the full introspection query is blocked, it tries progressively simpler queries. If all introspection is disabled, it falls back to inferring the schema from observed queries and mutations in the captured traffic.\n\n### Is it safe to run against production?\n\nVespasian's crawl stage drives a browser and follows links, which is read-only. The probing stage sends OPTIONS requests, fetches `?wsdl` documents, and runs GraphQL introspection queries, all of which are read-only operations. However, always coordinate with the target owner and prefer staging environments during security assessments.\n\n### Security limitations of --analyze-js on untrusted bundles\n\nWhen analyzing JavaScript bundles served by an attacker-controlled application, `--analyze-js` carries a bounded resource-exhaustion risk. The underlying tree-sitter/jsluice parser is not context-cancellable: a bundle crafted to hang the parser will keep the per-bundle goroutine alive until the parser returns or the process exits. Per-bundle timeouts (default 5 s, configurable) bound the wait per bundle, but a genuine parser deadlock leaks that goroutine for the lifetime of the process. In the worst case the number of leaked goroutines is the worker concurrency × (1 + N) where N is the number of sourcesContent entries in any recovered sourcemap. For long-running processes or automated pipelines that analyze untrusted targets, the recommended mitigation is process isolation: run vespasian with a wall-clock timeout (`--timeout`) per target so leaked goroutines are bounded by the process lifetime.\n\n## Development\n\n### Prerequisites\n\n- [Go 1.24+](https://go.dev/dl/)\n- [golangci-lint](https://golangci-lint.run/welcome/install/)\n\n### Build and Test\n\n```bash\ngit clone https://github.com/praetorian-inc/vespasian.git\ncd vespasian\nmake build       # Build the binary to bin/vespasian\nmake test        # Run tests with race detection\nmake lint        # Run golangci-lint (gocritic, misspell, revive)\nmake check       # Run all checks (fmt, vet, lint, test)\n```\n\n```bash\nmake coverage    # Generate coverage report\nmake deps        # Download and tidy modules\nmake clean       # Remove build artifacts\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/my-feature`)\n3. Commit your changes (`git commit -am 'Add my feature'`)\n4. Push to the branch (`git push origin feature/my-feature`)\n5. Open a Pull Request\n\nPlease ensure all CI checks pass before requesting review.\n\n## License\n\nThis project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details.\n\n## About Praetorian\n\n[Praetorian](https://www.praetorian.com/) is a cybersecurity company that helps organizations secure their most critical assets through offensive security services and the [Praetorian Guard](https://www.praetorian.com/guard) attack surface management platform.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpraetorian-inc%2Fvespasian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpraetorian-inc%2Fvespasian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpraetorian-inc%2Fvespasian/lists"}