GitHub Actions
Integrate ScanRook into your GitHub Actions CI pipeline to scan container images on every push or pull request. Gate deployments on critical CVEs and upload reports as build artifacts.
Complete workflow
Copy this workflow to .github/workflows/scanrook.yml in your repository.
name: ScanRook Vulnerability Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
security-events: write
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install ScanRook
run: curl -fsSL https://scanrook.sh/install | bash
- name: Build container image
run: |
docker build -t myapp:ci .
docker save myapp:ci -o myapp.tar
- name: Pre-warm vulnerability cache
run: scanrook db update --source all
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
- name: Scan container image
run: |
scanrook scan \
--file ./myapp.tar \
--mode deep \
--format json \
--out report.json
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
- name: Upload scan report
if: always()
uses: actions/upload-artifact@v4
with:
name: scanrook-report
path: report.json
- name: Fail on critical or high CVEs
run: |
CRITICAL=$(jq '.summary.critical // 0' report.json)
HIGH=$(jq '.summary.high // 0' report.json)
echo "Critical: $CRITICAL, High: $HIGH"
if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ]; then
echo "::error::Found $CRITICAL critical and $HIGH high severity vulnerabilities"
jq '.findings[] | select(.severity == "CRITICAL" or .severity == "HIGH") | {cve, package: .package.name, version: .package.version, severity, confidence}' report.json
exit 1
fiStep breakdown
What each step does and how to customize it.
Install ScanRook
The shell installer auto-detects the platform and downloads the latest release binary. The binary is placed in /usr/local/bin and is immediately available for subsequent steps.
Build and save image
ScanRook scans saved Docker images (tar files), not running containers. Use docker save to export the image after building.
Pre-warm cache
Optional but recommended. Pre-warming the vulnerability cache before scanning reduces API calls and speeds up the scan. Set NVD_API_KEY as a repository secret for higher NVD rate limits.
Scan and report
The scan produces a JSON report. The workflow uploads it as a build artifact so it is available for download on every run.
Fail on critical/high CVEs
The final step reads the summary from the JSON report and fails the workflow if any critical or high severity findings are present. Adjust the threshold by changing the jq filter.
SBOM diff gate
Optional: compare SBOMs across PRs and enforce a policy on package changes.
name: SBOM Diff Gate
on:
pull_request:
branches: [main]
jobs:
sbom-diff:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install ScanRook
run: curl -fsSL https://scanrook.sh/install | bash
- name: Build and save image
run: |
docker build -t myapp:pr .
docker save myapp:pr -o myapp.tar
- name: Generate current SBOM
run: |
scanrook container --tar ./myapp.tar --sbom --format json --out current-report.json
- name: Download baseline SBOM
uses: actions/download-artifact@v4
with:
name: baseline-sbom
path: .
continue-on-error: true
- name: Diff SBOMs
if: hashFiles('baseline-sbom.json') != ''
run: |
scanrook sbom diff \
--baseline ./baseline-sbom.json \
--current ./current-report.json \
--json --out diff.json
- name: Check policy
if: hashFiles('diff.json') != ''
run: |
scanrook sbom policy \
--policy ./scanrook-policy.yaml \
--diff ./diff.json \
--report ./current-report.jsonThis workflow compares the current SBOM against a baseline stored as a build artifact from the last main branch build. The scanrook sbom policy step exits with code 1 if the policy file is violated, failing the PR check.
Tips
Best practices for CI integration.
- Store
NVD_API_KEYas a repository or organization secret, not in the workflow file. - Use
--mode deepfor the most thorough scan. Use--mode lightfor faster feedback on PRs. - Cache the
~/.scanrook/cache/directory between runs usingactions/cacheto speed up repeated scans. - Filter findings by confidence tier in your gate step: only fail on
ConfirmedInstalledfindings to avoid blocking on heuristic-only matches. - Use
if: always()on the upload step so reports are saved even when the scan or gate step fails.