The Safeguard CLI scan command reports findings but does not fail a build by itself, and the documentation says there is no severity threshold flag. You can add a gate in two lines: run the scan, then use jq on the vulnerability output file to exit non-zero when a critical match is present.
The pattern
The CLI documentation shows this GitHub Actions shape, with the scan writing into an output folder and jq reading the grype style file:
- name: Scan the working tree
run: safeguard scan --dir . --output out/
- name: Gate the build
run: |
jq -e '[.data.matches[].vulnerability.severity | ascii_downcase] | index("critical") | not' out/*/vuln.json
The jq command with the exit flag returns non-zero when the expression is false or null. Here the expression is true when no critical entry exists, so the step passes only when nothing critical is found.
The glob in the path implies a subfolder per scan under the output directory. Check your own output tree once so the glob matches exactly one file.
Handle the incomplete scan case
Before the jq step, make sure the scan itself completed. Exit code 2 means results were written but the vulnerability step did not run. In a plain shell step that is easy to miss, because GitHub Actions only fails on non-zero codes, and code 2 is non-zero, so the scan step would already fail. If you wrap the command in a script that swallows codes, add a check. See the exit codes post for the pattern.
Choosing the threshold
Blocking on critical only is a common starting point. A few notes before you pick:
- Start with critical, watch the failure rate for a week, then consider adding high.
- Decide up front what happens to a known, accepted finding. Without an exception path, teams end up deleting the gate.
- Remember that severity is a score, not exploitability. A critical in a function you never call still blocks the build under this check.
If you want severity combined with context such as reachability, that is what platform policies and gates are for. The CLI documentation says gates are defined and evaluated in the platform, not in the CLI.
Counting instead of failing
You may prefer a report first. This prints the number of matches per severity so you can watch trends before enforcing anything:
jq -r '.data.matches[].vulnerability.severity' out/*/vuln.json | sort | uniq -c
Run it in warn-only mode for a sprint, share the numbers, then switch to the failing gate.
Keep the artifacts
Upload the output folder as a build artifact whatever the result, so a developer can open the file that caused the failure. A gate that blocks a merge with no evidence attached creates support tickets.
You can see a pull request gate in action in the interactive demo.
Click through it at your own pace, or open it full screen.
Quick check
Does the CLI have a fail on severity flag for scan?
The scan documentation says no. Use a post-scan check such as jq, or enforce with platform policies and gates.
Why did my jq step say no such file?
The scan writes into a subfolder under the output directory. List the folder and adjust the path or glob.
Safeguard also provides a free scan at gold.safeguard.sh/scan for a quick comparison against your pipeline results.
Sources
Last reviewed: 19 October 2026.