Skip to main content

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

.github/workflows/vulkro.yml
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 1 if there are unmitigated High/Critical findings, blocking the merge.

GitLab CI

.gitlab-ci.yml
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:

.pre-commit-config.yaml
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

VariableEffect
VULKRO_OFFLINE=1Skip the CVE bundle fetch in air-gapped CI runners.
VULKRO_DISABLE_INTERPROC_TAINT=1Temporarily skip cross-file taint propagation when an FP regression breaks a build.
VULKRO_GIT_BLAME=1Populate git_owner on every finding for ownership reports.

What's next