For a DevOps lead, the question is not whether a scanner is clever. It is whether it is repeatable, quiet when it should be, and honest about its exit codes. Safeguard's CLI and runner have specific behaviors worth knowing before you commit them to a pipeline.
What it is
There are two ways Safeguard fits a pipeline, and the docs describe both.
- Connect the repository as an integration. The platform scans it and evaluates your policies and gates on the results. Nothing has to run in the pipeline.
- Run the CLI in the job.
safeguard scanandsafeguard detectrun on the build machine and write files you can keep as artifacts or upload to a code scanning tab.
The docs state that there is no published Safeguard GitHub Action, container image or pipeline plugin for the CLI yet. Do not write a workflow that assumes one.
How to use it
Install and pin
curl -fsSL https://cli.safeguard.sh/install | SAFEGUARD_VERSION=<version> bash
Pin the version so a release does not change a build. Releases are listed at cli.safeguard.sh/releases. The installer verifies checksums.
Mind the scanners
safeguard scan runs syft and grype. A fresh CI container has never opened the interactive terminal, so they are not there. Put both on the PATH, or point the TOOLS_DIR variable at a folder that holds them.
Secrets as SARIF in GitHub
The docs give this pattern for secrets and misconfigurations:
name: Safeguard
on: [push, pull_request]
jobs:
detect:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Install the Safeguard CLI
run: curl -fsSL https://cli.safeguard.sh/install | SAFEGUARD_VERSION=<version> bash
- name: Detect secrets and misconfigurations
run: safeguard detect --format sarif --output safeguard.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: safeguard.sarif
Understand exit codes
safeguard scan exits 0 when the scan ran, whatever it found. It exits 1 when the command failed and 2 when results were written but the vulnerability scan did not run. There is no severity threshold flag. If a pull request must fail on findings, build that as a policy gate in the platform, because gates are evaluated there, not by the CLI.
Runners on your own hosts
The runner is part of the CLI, not a separate product. It polls the platform for jobs assigned to its machine, runs each locally and uploads only the SBOM and findings. All traffic is outbound HTTPS, so nothing connects in to the runner. Headless commands:
safeguard runner start --workspace ./path/to/repo
safeguard runner status
safeguard runner stop
On a network that forbids held connections, pass --poll-wait 0. For scripts, safeguard exec "summarise the critical findings" runs one turn and exits. Its default permission mode is strict, so anything not pre approved by policy is denied. It refuses to run outside a git repository unless you pass --skip-git-repo-check.
Credentials
The CLI has no API key environment variable. A machine is authorized by signing in once, and safeguard login --no-browser prints a code for machines without a browser. For automation against the platform, the RBAC docs describe service tokens and workload identities that are OIDC federated from GitHub, GitLab and the major clouds, with no long lived secrets. Tokens are short lived by default.
Keep it healthy
safeguard doctor
safeguard update
safeguard self-update --check
If your tenant admin has switched off the CLI or runners as client types, these will not work for your people, so ask first.
Try it yourself
Add the secrets detection job above to a scratch repository with a pinned version. Commit a fake key on a branch and confirm the result appears in the code scanning tab. Then decide where in the platform the blocking gate should live.
Click through it at your own pace, or open it full screen.
Quick check
Why does a scan with critical findings still pass in CI?
The CLI reports findings and exits 0 when the scan ran. Enforcement is a policy gate in the platform.
What must be on a fresh CI runner before safeguard scan works?
syft and grype, on the PATH or in the folder named by TOOLS_DIR.
Does the runner accept inbound connections?
No. All traffic is outbound HTTPS opened by the runner.