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.
- Report only. Run with
--fail-on none. Nothing fails; the team sees the output. - Baseline. Record today's findings and gate only on what is new.
- 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
| Code | Meaning | What CI should do |
|---|---|---|
0 | Clean, or under the threshold. | Continue. |
1 | Usage error or scan failure. | Fail loudly. This is a broken pipeline, not a clean repository. |
2 | Threshold 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
.quantumignorefor 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.