Skip to main content

Your first scan

This page walks you through:

  1. Pointing vulkro scan at a project.
  2. Reading the output.
  3. Filtering it down to actionable signal.

Run it

cd path/to/your/project
vulkro scan .

What happens:

  • Vulkro walks the working tree honouring .gitignore (same library ripgrep / fd use).
  • It detects your project's language and framework - Python, Node.js, or Go; Express, Fastify, Next.js, FastAPI, Django, gin, etc.
  • It statically extracts every HTTP route, builds a module call graph, and runs the security pipeline (OWASP, secrets, CVEs, taint, reachability, privacy).
  • It prints a colourised summary table to stdout.

Wall-clock for a mid-sized monorepo is typically 30-90s. Larger repos (Cal.com, ~2,200 modules) take ~45s on an M1.

Read the output

A typical run looks like this:

Detected: TypeScript | Next.js (App Router)
224 endpoints | 2,230 modules
Taint pass | Reachability | CVE match | Secrets | IaC

CRITICAL 33 HIGH 355 MED 1369 LOW 1063

API1 BrokenObjectLevelAuth 115 findings
API8 SecurityMisconfiguration 2292 findings
SECRETS 436 hardcoded | 19 in git history
DEPS 23 CVEs (4 KEV-listed, 7 reachable)

Completed in 42s | exit 1 (Critical/High present)

Each section is separately addressable:

  • Severity counts are the headline. Critical and High block the build by default.
  • OWASP categories show where your code spends its risk budget.
  • SECRETS combines source-tree and git-history findings; the desktop console (vulkro serve) splits them.
  • DEPS tags the most dangerous CVEs. KEV-listed (CISA "actively exploited") and reachable (the vulnerable symbol is actually called from your code) are escalated.

Get the details

Default output is a summary. To see file paths and line numbers:

vulkro scan . --verbose

For machine-readable output:

vulkro scan . --format json | jq .findings[0]
vulkro scan . --format sarif > vulkro.sarif
vulkro scan . --format junit > vulkro-junit.xml

See the full output formats reference for the rest: CycloneDX 1.5, SPDX 2.3, GDPR Art. 30 RoPA (markdown + HTML), executive HTML, PDF (via wkhtmltopdf), GitHub-PR-comment, CSV.

Filter the noise

The default scan uses --min-confidence medium - Medium and High findings only. Three knobs adjust the threshold:

# Default: medium + high. The typical day-to-day mode.
vulkro scan .

# Audit mode - surfaces every heuristic / pattern-only finding,
# including Low confidence ones on test/example code.
vulkro scan . --all-confidence

# CI-gate mode - High confidence only. Precision 0.76, recall 0.57
# on the public vuln corpus. Production-recommended for blocking gates.
vulkro scan . --min-confidence high

# Hide non-source files (Dockerfile, lockfiles, *.tf, *.md, templates,
# docker-compose). Keeps `.env*` and k8s manifests.
vulkro scan . --scope src

Confidence model -> explains the calibration table that decides which detector categories get downgraded.

Suppress a specific finding

Add a comment in the source instead of editing a config file:

# vulkro-disable-next-line BrokenAuthentication
@router.get("/health")
def health(): ...
// vulkro-disable-next-line BrokenAuthentication
app.get("/internal/ping", noAuth);

/* vulkro-disable-next-line API1 */
db.query(`SELECT * FROM ${table}`);

vulkro-disable-file at the top of a file silences all findings for it. Suppression counts are reported in every scan summary so you can audit them.

Save the run

vulkro scan . --save

Adds the run to local scan history at ~/.vulkro/scans.db so you can:

  • Compare against a previous baseline (vulkro diff <ref>).
  • Track risk-debt and MTTR trends over time in the desktop console.
  • Mark a scan as baseline to gate PRs against it.

What's next