Documentation

Continuous integration

How to adopt the scanner on a codebase that already exists, without breaking the build on day one.

The adoption path

The first run on a mature repository will find things. A scanner that breaks the build on day one gets deleted on day one, so adopt it in three steps.

  1. Report only. Run with --fail-on none. Nothing fails; the team sees the output.
  2. Baseline. Record today's findings and gate only on what is new.
  3. Raise the gate. Once the backlog is triaged, move to --fail-on high, then lower if you want.
# step 2: record what exists today
quantum-scan . --format json --out .quantum-baseline.json --source-only

# step 3: from now on, only new weaknesses fail
quantum-scan . --format json --out new.json \
  --baseline .quantum-baseline.json --fail-on high

Commit .quantum-baseline.json. Shrinking it is a visible, reviewable piece of work.

GitHub Actions

This uploads SARIF, so findings appear in the Security tab and as annotations on pull requests. security-events: write is required for the upload.

# .github/workflows/post-quantum.yml
name: Post-quantum readiness

on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4

      - name: Build the scanner
        run: |
          cmake -S quantum -B quantum/build -DCMAKE_BUILD_TYPE=Release
          cmake --build quantum/build -j

      - name: Scan
        run: |
          ./quantum/build/quantum-scan . \
            --format sarif --out quantum.sarif \
            --source-only --fail-on none

      - name: Upload to code scanning
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: quantum.sarif

To gate the build as well as report, add a second scan step in JSON with a threshold. Keep them separate: the SARIF upload should run even when the gate fails, so the alerts are visible.

      - name: Gate
        if: always()
        run: |
          ./quantum/build/quantum-scan . --format json --out quantum.json \
            --source-only --baseline .quantum-baseline.json --fail-on high

Publishing a CBOM with each release

Auditors ask for an inventory, not a scan log. Attaching one to every release means you always have a current answer.

      - name: Cryptographic inventory
        run: |
          ./quantum/build/quantum-scan . --format cbom \
            --out quantum-cbom.json --source-only

      - uses: actions/upload-artifact@v4
        with:
          name: quantum-cbom
          path: quantum-cbom.json

GitLab CI

post-quantum:
  image: ubuntu:24.04
  before_script:
    - apt-get update -qq && apt-get install -y -qq cmake g++
  script:
    - cmake -S quantum -B quantum/build -DCMAKE_BUILD_TYPE=Release
    - cmake --build quantum/build -j
    - ./quantum/build/quantum-scan . --format json --out quantum.json
      --source-only --fail-on high
  artifacts:
    when: always
    paths: [quantum.json]

Pre-commit hook

A full scan of the Functor repository takes 0.12 seconds, so it is cheap enough to run before every commit.

# .git/hooks/pre-commit
#!/bin/sh
quantum-scan . --format json --out /tmp/quantum.json \
  --source-only --baseline .quantum-baseline.json --fail-on high || {
  echo "New cryptographic weakness. Fix it, or add quantum:ignore[RULE-ID] with a reason."
  exit 1
}

Exit codes in a pipeline

CodeMeaningWhat CI should do
0Clean, or under the threshold.Continue.
1Usage error or scan failure.Fail loudly. This is a broken pipeline, not a clean repository.
2Threshold exceeded.Fail the job and show the report.

Do not write || true after the scan. It turns exit 1 and exit 2 into success, which means a misconfigured scanner reports the same as a clean codebase. If you need a soft run, use --fail-on none, which reports without gating.

Monorepos

Scan each component separately so a noisy package cannot hide a quiet one, and so each team owns its own baseline.

for pkg in services/*; do
  quantum-scan "$pkg" --format json --out "reports/$(basename "$pkg").json" \
    --source-only --baseline "$pkg/.quantum-baseline.json" --fail-on high || failed=1
done
exit "${failed:-0}"

If the first run is noisy

  • Use --source-only. Tests, fixtures and vendored code produce most first-run matches, and none of it is production cryptography.
  • Check the Likely Noise section before suppressing anything. Low-confidence matches are already excluded from the score.
  • Add paths to .quantumignore for generated code and third-party trees.
  • Suppress inline, with a rule id and a reason. // quantum:ignore[CRYPTO-HASH-BROKEN-001] cache key, not security. Every suppression is counted in the report, so this stays auditable.
  • Expect a large inventory. A wallet or an exchange should show pervasive migration exposure. That is the product working, not a problem.