Sub-second security gates that do not punish the team.
Adding a SAST step to CI is easy. Adding one that does not take 20 minutes per run, flag 500 false positives at the team's shared Slack channel, or require every contributor to sign up for a third-party account is harder.
Vulkro is designed for the gate. Single binary install, scan times measured per file in milliseconds, deterministic exit codes, and the right output formats for whichever CI you use.
The gate contract
vulkro scan --fail-on critical,high --format sarif --output vulkro.sarif
Three exit codes, every subcommand, by contract:
| Exit | Meaning |
|---|---|
0 | Scan completed, no findings at or above the --fail-on threshold |
1 | Scan completed, findings reported (or license-block) |
2 | Error: bad arguments, IO failure, internal crash |
This is in the --help text of every subcommand. Your CI's
if exit_code in (0, 1) logic is the right shape; exit 2 is
a "something broke, page someone" signal.
Baseline + ratchet for the retrofit
A repo that has been running without SAST for years will not turn green on day one. Two options:
Baseline once, ratchet forever. Commit the current state
into .vulkro-baseline.json. Subsequent scans with
--ratchet (auto-discovers the baseline file) only fail on
findings the PR introduces. Existing debt is tracked separately
and burned down on its own cadence.
Compare against a base ref. vulkro scan --gate-vs origin/main
diffs against a ref and fails only on NEW findings the working
branch introduces. Useful for PRs where the baseline approach
would be too coarse.
Either way the security team gets the visibility they want without the team getting a "burn down 4,000 findings" assignment they will never finish.
Output formats per CI
| CI / Tool | Recommended format | Flag |
|---|---|---|
| GitHub Code Scanning | SARIF | --format sarif |
| GitHub PR inline comments | gh-pr | --format gh-pr |
| GitLab Security Dashboard | SARIF | --format sarif |
| Any dashboard with JUnit | JUnit XML | --format junit |
| Any dashboard with CSV | CSV | --format csv |
| Stream to a downstream tool | NDJSON | --format ndjson |
| Human-readable terminal | Default table | (no flag needed) |
SARIF is the workhorse: it works in GitHub Code Scanning, GitLab, Azure DevOps, BitBucket, and most other dashboards that have implemented the spec.
GitHub Actions in 12 lines
name: vulkro
on:
pull_request:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # full history so --gate-vs works
- run: curl -fsSL https://install.vulkro.com | sh
- run: vulkro scan --ratchet --format sarif --output vulkro.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: vulkro.sarif }
if: always() ensures the SARIF uploads even when Vulkro
returns exit 1 (findings present), so the Code Scanning tab
gets the data and the build still fails the way you want it to.
GitLab CI in 8 lines
vulkro:
image: ubuntu:24.04
before_script:
- curl -fsSL https://install.vulkro.com | sh
script:
- vulkro scan --ratchet --format sarif --output vulkro.sarif
artifacts:
reports:
sast: vulkro.sarif
GitLab's sast report type ingests SARIF natively.
Pre-commit hook
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: vulkro
name: vulkro scan
entry: vulkro scan --fail-on critical
language: system
pass_filenames: false
types_or: [python, javascript, typescript, go]
pass_filenames: false because vulkro takes a single project
root (not a list of files) and cross-file taint flow matters,
so scanning the project is the right primitive. types_or
scopes the hook to only fire when files in the supported
languages change. Vulkro's ci preset is fast enough that a
full scan in a pre-commit hook is practical (sub-5s on most
projects under 100k LoC).
Performance
| Scenario | Typical scan time |
|---|---|
| Pre-commit (single file change) | < 200ms |
| PR diff (10 files) | < 1s |
| Full project (10k LoC) | 3-5s |
| Full project (100k LoC) | 20-40s |
| Full project (1M LoC) | 2-4min |
Sub-second on the common cases is the target. The default tree- sitter parsing pipeline is fast enough for most codebases; deeper inter-procedural taint adds time but only on the rules that need it.
Honest comparison
vs. cloud SAST products (Snyk Code, Semgrep Cloud, Checkmarx): Vulkro does not require an account, does not upload code, and does not auto-renew. Performance is generally faster because there is no upload step. The trade-off: no shared dashboard or cross-team triage UI.
vs. CodeQL via GitHub Actions: CodeQL is excellent and free for public repos. Vulkro complements it (different rule set, faster scans, easier multi-language story); the two coexist in the same SARIF pipeline. For private repos GHAS pricing kicks in; Vulkro Pro at $19 is the alternative.
vs. Semgrep CE: Vulkro Free has substantially higher recall on the public benchmark (42 vs 12 of 55 bugs caught). Both are license-friendly for CI use.
Read the full benchmark methodology · CLI reference · Output formats