Deployment: Invicti Platform on-demand
Package: Ultimate
Bundle: API Security
Invicti source scan
Invicti Source Scan generates OpenAPI 3.0 specifications from your application source code and uploads them to Invicti Platform for DAST scanning. It statically analyzes your codebase to discover API endpoints across multiple programming languages — including endpoints that are undocumented, missing from your existing specs, or added since your last spec update.
Use it when:
- Your team doesn't maintain an OpenAPI spec, or the spec is incomplete.
- You want to catch shadow APIs and undocumented endpoints before they reach production.
- You need to keep your API inventory in sync with every code change automatically.
This document explains how to set up and run Invicti Source Scan, upload the generated specification to Invicti Platform, and integrate it into your CI/CD pipeline.
How it works
Invicti Source Scan runs as a Docker container in your CI/CD pipeline or locally:
- Scans source code for API endpoint definitions.
- Generates an OpenAPI 3.0 specification from discovered endpoints.
- Optionally enriches the spec with AI-generated descriptions and examples.
- Uploads the specification to Inventory > API catalog for DAST scanning.
Supported languages
The scanner supports the following languages and their popular web frameworks:
Python, JavaScript, TypeScript, C#/.NET, Java, Go, Ruby, PHP, Rust, Kotlin, Swift, Scala, Crystal, Elixir
It also parses existing OpenAPI/Swagger specs and GraphQL schemas.
Prerequisites
- Docker installed on your machine or CI/CD runner.
- Invicti Platform credentials: Platform URL, API user email, and API token. You can find the API token under Settings > API tokens.
- Source code accessible as a local directory or Git repository.
- Registry access: Log in to the Invicti container registry before pulling the image:
docker login registry.invicti.com
When prompted, enter your Invicti account email as the username and your license key as the password.
Step 1: Run the scan
Mount your source code at /code (read-only) and an output directory at /output:
docker run --rm \
-v "/path/to/your/source/code":/code:ro \
-v "$(pwd)/output":/output \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
The generated OpenAPI spec is saved as output/openapi.json. A typical scan takes between 30 seconds and 2 minutes depending on codebase size and scan profile.
[TODO: Add screenshot of Docker terminal output showing a successful scan with endpoint count]
Example output snippet (openapi.json)
{
"openapi": "3.0.3",
"info": {
"title": "Discovered API - my-service",
"version": "1.0.0"
},
"paths": {
"/api/users/{id}": {
"get": {
"summary": "Get user by ID",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": { "type": "integer" }
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"email": { "type": "string" }
}
}
}
}
}
}
}
}
}
}
Scan options
| Variable | Default | Description |
|---|---|---|
SCANNER_PROFILE | standard | quick (deterministic pattern matching only — fastest), standard (adds taint analysis and dataflow graphs), deep (all engines including code property graphs and LLM verification) |
SCANNER_PARALLEL | false | Enable parallel scanning for large codebases |
SCANNER_WORKERS | 4 | Number of parallel workers. Only applies when SCANNER_PARALLEL is set to true |
SCANNER_SERVICE_NAME | - | Service name prefix in the OpenAPI spec title |
SCANNER_INCREMENTAL | false | Only scan files changed since the last run (faster for CI/CD) |
SCANNER_FAIL_ON_CRITICAL | false | Exit with code 2 if the scan quality gate fails (useful for CI/CD pipelines) |
AI-enriched specification (optional)
Enable LLM-powered enrichment to improve the quality of the generated OpenAPI specification. This helps DAST scanners test your API more effectively by providing:
- Parameter definitions with types, validation rules, and examples — so the DAST scanner can send correctly formatted requests
- Request/response schemas with property descriptions and required fields — so the DAST scanner can craft valid payloads and detect unexpected responses
- Authentication schemes detected from decorators and middleware (Bearer, API key, OAuth2) — so the DAST scanner can authenticate before testing protected endpoints
Without enrichment, the scanner still generates a complete OpenAPI spec using deterministic analysis — enrichment refines and extends it.
Enrichment options
Set SCANNER_AI_ENRICH=true and provide an API key. If you're using Anthropic Claude, just set the API key — it's the default provider. Set LLM_PROVIDER only if you want to use a different provider.
| Variable | Default | Description |
|---|---|---|
SCANNER_AI_ENRICH | false | Set to true to enable AI enrichment |
LLM_PROVIDER | anthropic | LLM provider. Set only if not using Anthropic: openai, gemini, or bedrock |
ANTHROPIC_API_KEY | - | Anthropic Claude API key (default provider) |
OPENAI_API_KEY | - | OpenAI API key (requires LLM_PROVIDER=openai) |
GOOGLE_API_KEY | - | Google Gemini API key (requires LLM_PROVIDER=gemini) |
AWS_ACCESS_KEY_ID | - | AWS Bedrock access key (requires LLM_PROVIDER=bedrock) |
AWS_SECRET_ACCESS_KEY | - | AWS Bedrock secret key |
SCANNER_MAX_TOKENS | unlimited | Total token budget. When reached, enrichment stops gracefully |
docker run --rm \
-v "/path/to/your/source/code":/code:ro \
-v "$(pwd)/output":/output \
-e SCANNER_AI_ENRICH=true \
-e ANTHROPIC_API_KEY=your-api-key \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
AI enrichment is optional and does not affect the core scan. If a provider is unavailable or the token budget is exhausted, the scanner falls back to the deterministic-only spec automatically.
Cost and performance
AI enrichment consumes tokens from your LLM provider account. The scanner minimizes costs with a hybrid approach:
- Deterministic extraction first — Parameters, status codes, types, and descriptions are extracted directly from source code without any LLM calls.
- LLM only where needed — The LLM is called only for endpoints where deterministic confidence is below 70%.
- Batch processing — Endpoints are grouped (default: 20 per batch) to reduce API calls. A codebase with 100 endpoints typically requires ~8 LLM calls instead of 140.
- Caching — Results are cached per endpoint using a content hash. If the code hasn't changed, cached results are reused with no LLM calls.
As a rough estimate, enriching 100 endpoints costs approximately $0.10–$0.50 in LLM tokens (varies by provider and model). Subsequent scans with caching enabled cost near zero for unchanged endpoints.
Use SCANNER_MAX_TOKENS to set a hard budget — the scanner stops enrichment gracefully when the limit is reached and outputs the spec with whatever enrichment was completed.
Mount the cache directory as a volume to reuse enrichment results across pipeline runs and avoid repeated LLM costs:
docker run --rm \
-v "/path/to/your/source/code":/code:ro \
-v "$(pwd)/output":/output \
-v "$(pwd)/.enrichment-cache":/.cache/enrichment \
-e SCANNER_AI_ENRICH=true \
-e ANTHROPIC_API_KEY=your-api-key \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
Advanced enrichment configuration
Token budget and rate limiting
| Variable | Default | Description |
|---|---|---|
SCANNER_MAX_TOKENS | unlimited | Total token budget. When reached, the current batch completes and remaining batches are skipped |
SCANNER_RPM | 60 | Maximum LLM requests per minute |
SCANNER_TPM | 100000 | Maximum tokens per minute |
Caching
| Variable | Default | Description |
|---|---|---|
ENRICHMENT_CACHE_DIR | /.cache/enrichment | Cache database directory. Mount a volume to persist across runs |
ENRICHMENT_CACHE_TTL | 604800 (7 days) | Cache entry time-to-live in seconds |
SCANNER_NO_CACHE | false | Set to true to skip cache and force fresh analysis |
Batch processing and hybrid analysis
| Variable | Default | Description |
|---|---|---|
ENABLE_BATCHING | true | Group endpoints into batches to reduce API calls |
OPENAPI_BATCH_SIZE | 20 | Number of endpoints per enrichment batch |
DETERMINISTIC_CONFIDENCE_THRESHOLD | 0.7 | Use LLM only when deterministic confidence is below this value |
LLM_MODEL | provider default | Override the default model for your provider |
Example with advanced options
docker run --rm \
-v "/path/to/your/source/code":/code:ro \
-v "$(pwd)/output":/output \
-v "$(pwd)/.enrichment-cache":/.cache/enrichment \
-e SCANNER_AI_ENRICH=true \
-e ANTHROPIC_API_KEY=your-api-key \
-e SCANNER_MAX_TOKENS=500000 \
-e SCANNER_RPM=30 \
-e OPENAPI_BATCH_SIZE=10 \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
Step 2: Upload to Invicti Platform
Add the sync environment variables to automatically upload the specification:
docker run --rm \
-v "/path/to/your/source/code":/code:ro \
-v "$(pwd)/output":/output \
-e INVICTI_SYNC=true \
-e INVICTI_URL=https://platform.invicti.com \
-e INVICTI_USER=your-api-user@company.com \
-e INVICTI_TOKEN=your-api-token \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
| Variable | Required | Description |
|---|---|---|
INVICTI_SYNC | Yes | Set to true to enable upload |
INVICTI_URL | Yes | Invicti Platform base URL |
INVICTI_USER | Yes | API user email |
INVICTI_TOKEN | Yes | API token |
DRY_RUN | No | Set to true to validate without uploading |
Step 3: View and scan imported APIs
- Select Discovery from the left-side menu, then go to Inventory > API catalog.
- Locate the uploaded specification and link it to a target.
- Start a DAST scan to test the discovered endpoints.
[TODO: Add screenshot of Inventory > API Catalog page showing an uploaded source scan spec]
[TODO: Add screenshot showing discovered endpoints linked to a target]
Run Invicti Source Scan in your CI/CD pipeline to automatically update your API inventory on every push.
Output formats
The primary output is the OpenAPI 3.0 specification (output/openapi.json) that gets uploaded to API hub. The scanner also generates a detailed JSON result file with endpoint metadata and statistics.
For CI/CD pipeline visibility, you can optionally enable additional report formats:
| Variable | Output file | Description |
|---|---|---|
| (default) | output/openapi.json | OpenAPI 3.0 specification — uploaded to API hub |
| (default) | output/result.json | Full scan results with endpoint metadata and statistics |
SCANNER_SARIF_FILE | output/results.sarif | SARIF 2.1 — for GitHub or other code analysis dashboards |
SCANNER_JUNIT_FILE | output/results.junit.xml | JUnit XML — for CI/CD test result viewers (GitLab, Jenkins, Azure DevOps) |
All output files are written to the /output volume mount.
Tracking API changes in CI/CD
To see what API changes occurred in a given build before uploading to API hub, provide the previous OpenAPI spec. The scanner includes a diff in the JSON output (changes field) showing added, removed, and modified endpoints — useful for pipeline logs and code review.
docker run --rm \
-v "/path/to/your/source/code":/code:ro \
-v "$(pwd)/output":/output \
-v "$(pwd)/previous-openapi.json":/previous/openapi.json:ro \
-e PREVIOUS_SPEC=/previous/openapi.json \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
CI/CD integration
GitHub Actions
name: API Discovery Scan
on:
push:
branches: [main]
jobs:
source-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Invicti Source Scan
run: |
docker run --rm \
-v "${{ github.workspace }}":/code:ro \
-v "${{ github.workspace }}/output":/output \
-e INVICTI_SYNC=true \
-e INVICTI_URL=${{ secrets.INVICTI_URL }} \
-e INVICTI_USER=${{ secrets.INVICTI_USER }} \
-e INVICTI_TOKEN=${{ secrets.INVICTI_TOKEN }} \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
GitLab CI
source-scan:
stage: test
image: docker:latest
services:
- docker:dind
script:
- docker run --rm
-v "$CI_PROJECT_DIR":/code:ro
-v "$CI_PROJECT_DIR/output":/output
-e INVICTI_SYNC=true
-e INVICTI_URL=$INVICTI_URL
-e INVICTI_USER=$INVICTI_USER
-e INVICTI_TOKEN=$INVICTI_TOKEN
registry.invicti.com/invicti-platform/invicti-source-scan:latest
artifacts:
paths:
- output/
Troubleshooting
Show all troubleshooting topics
No endpoints found
- Verify the correct directory is mounted at
/code. Check with:docker run --rm -v "/path":/code registry.invicti.com/invicti-platform/invicti-source-scan:latest ls /code - Confirm your project uses a supported language.
- For large route files, increase the file size limit:
-e SCANNER_MAX_FILE_SIZE=10
Scan is slow
- Use
SCANNER_PROFILE=quickfor faster deterministic-only scanning. - Enable parallel mode:
-e SCANNER_PARALLEL=true -e SCANNER_WORKERS=8 - Use incremental scanning in CI/CD:
-e SCANNER_INCREMENTAL=true
Upload to Invicti fails
- Verify
INVICTI_URLdoesn't have a trailing slash. - Confirm the API token is valid and not expired.
- Test with dry run first:
-e DRY_RUN=true - Check the container logs for detailed error messages.
Path contains spaces (Windows)
Wrap the volume mount path in quotes:
docker run --rm \
-v "C:/Users/My Name/project":/code:ro \
-v "$(pwd)/output":/output \
registry.invicti.com/invicti-platform/invicti-source-scan:latest
Need help?
Invicti Support team is ready to provide you with technical help. Go to Help Center