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 display 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 . # ci preset: exits 1 on Critical/High
vulkro scan . --fail-on critical,high,medium # also fail the build on Medium severity
vulkro scan . --all-confidence # show every finding; still gates on severity

--min-confidence (default medium) controls which findings are displayed; --fail-on controls which severities make the build exit non-zero. Bare vulkro scan runs the ci preset, whose default policy is --fail-on critical,high.

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 is set.

Gate on new findings vs a baseline

Block the build only on new findings vs. main:

vulkro gate --base origin/main

vulkro gate scans the working tree and the base ref, then exits 1 only on findings present now but not in the base, so re-orderings, whitespace changes, and pre-existing tech debt never fail the build.

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