CI/CD integration
Vulkro is designed for CI as a primary use case. This page covers the three most common setups; deeper config lives under Integrations.
Exit-code contract
vulkro scan exits 1 if any Critical or High finding survives the
--min-confidence filter. Otherwise 0. There are no partial-success codes,
so you can wire it into any CI runner that respects POSIX exit codes:
vulkro scan . # exits 1 on Critical/High (default --min-confidence high)
vulkro scan . --min-confidence medium # also blocks on Medium
vulkro scan . --all # never blocks; informational mode
GitHub Actions
name: vulkro
on:
push:
branches: [main]
pull_request:
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # required to upload SARIF
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # required for git-history secret audit
- name: Install vulkro
run: curl -fsSL https://dist.vulkro.com/install.sh | bash
- name: Run scan (SARIF)
run: vulkro scan . --format sarif > vulkro.sarif
- name: Upload to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: vulkro.sarif
- name: Block PR on new Critical/High
run: vulkro scan . --min-confidence high
The two vulkro scan calls are intentional:
- The first run produces SARIF for GitHub Code Scanning (always succeeds, so the upload always happens).
- The second run is the gate - exits
1if there are unmitigated High/Critical findings, blocking the merge.
GitLab CI
vulkro:
stage: test
image: ubuntu:24.04
script:
- curl -fsSL https://dist.vulkro.com/install.sh | bash
- vulkro scan . --format junit > vulkro-junit.xml
- vulkro scan . --min-confidence high
artifacts:
when: always
reports:
junit: vulkro-junit.xml
paths:
- vulkro-junit.xml
expire_in: 30 days
JUnit output renders directly in GitLab's MR test report - every finding becomes a "test failure" with the file/line surfaced.
pre-commit
For fast local feedback:
repos:
- repo: local
hooks:
- id: vulkro-fast
name: vulkro (fast pass, source only)
entry: vulkro scan . --min-confidence high --scope src
language: system
pass_filenames: false
stages: [pre-commit]
--scope src skips Dockerfile / lockfile / template / IaC findings to keep
the hook fast. For a deeper local run, swap in vulkro scan . --min-confidence medium.
PR-comment integration
--format gh-pr produces a markdown comment intended for gh pr comment:
vulkro scan . --format gh-pr > comment.md
gh pr comment "$PR_NUMBER" --body-file comment.md
The comment summarises severity counts, lists Critical/High findings with
deep links to source lines, and notes diff vs. baseline if --baseline-ref
is set.
Diff against a baseline
Block the build only on new findings vs. main:
git checkout origin/main
vulkro scan . --save --label baseline-main
git checkout HEAD
vulkro diff baseline-main --fail-on added-critical,added-high
vulkro diff matches findings by stable finding_key, so re-orderings,
whitespace changes, and refactors don't generate false positives.
See also: Baselines explained - covers the
.vulkro-baseline.json ratchet file, the UI's baseline scan, and the
Export-for-CI bridge.
Environment variables you'll want
| Variable | Effect |
|---|---|
VULKRO_OFFLINE=1 | Skip the CVE bundle fetch in air-gapped CI runners. |
VULKRO_DISABLE_INTERPROC_TAINT=1 | Temporarily skip cross-file taint propagation when an FP regression breaks a build. |
VULKRO_GIT_BLAME=1 | Populate git_owner on every finding for ownership reports. |
What's next
- GitHub Actions deep-dive -> - caching, matrix, monorepo strategies.
- Output formats -> - CycloneDX SBOM, RoPA for GDPR Art. 30, exec HTML.
- Compliance gating -> - block PRs on PCI-DSS / SOC 2 / HIPAA control failures.