Skip to content

Security scan (CVE) #211

Security scan (CVE)

Security scan (CVE) #211

Workflow file for this run

name: Security scan (CVE)
on:
# Re-scan on every push so a fresh CVE in the dep graph surfaces on
# the next merge, not at the next release.
push:
branches: [main]
pull_request:
branches: [main]
# Re-scan daily — the CVE database advances even when the image
# doesn't, so a bug disclosed today can affect a tag we shipped weeks
# ago. Re-running picks that up.
schedule:
- cron: '17 6 * * *'
# Manual trigger for ad-hoc audits.
workflow_dispatch:
permissions:
contents: write
security-events: write
env:
IMAGE_REF: ghcr.io/cairn-geocoder/cairn:latest
jobs:
trivy-image:
name: Trivy — container image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image (local tag for scan)
run: |
docker build -t cairn:scan .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: cairn:scan
format: sarif
output: trivy-image.sarif
severity: CRITICAL,HIGH,MEDIUM
ignore-unfixed: false
exit-code: 0
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-image.sarif
category: trivy-image
- name: Generate human-readable summary
if: always()
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: cairn:scan
format: table
output: trivy-summary.txt
severity: CRITICAL,HIGH,MEDIUM
- name: Generate compact JSON for the homepage
if: always()
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: cairn:scan
format: json
output: trivy-image.json
severity: CRITICAL,HIGH,MEDIUM
- name: Slim Trivy JSON into security/cves.json
if: always()
run: |
set -euo pipefail
mkdir -p security
# Reduce Trivy's verbose output to a compact, page-friendly shape.
# Keep id / severity / package / installed / fixed_in / title.
jq -n \
--arg image "${IMAGE_REF}" \
--arg scanned "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--slurpfile raw trivy-image.json \
'{
scanned_at: $scanned,
image: $image,
summary: ([
$raw[0].Results[]?.Vulnerabilities[]?
] | reduce .[] as $v (
{critical:0, high:0, medium:0, low:0, unknown:0};
if $v.Severity == "CRITICAL" then .critical += 1
elif $v.Severity == "HIGH" then .high += 1
elif $v.Severity == "MEDIUM" then .medium += 1
elif $v.Severity == "LOW" then .low += 1
else .unknown += 1 end
)),
vulnerabilities: [
$raw[0].Results[]?.Vulnerabilities[]? |
{
id: .VulnerabilityID,
severity: .Severity,
package: .PkgName,
installed_version: .InstalledVersion,
fixed_version: (.FixedVersion // ""),
title: (.Title // (.Description // "" | .[0:120])),
url: (.PrimaryURL // "")
}
] | unique_by([.id, .package])
| sort_by(
if .severity == "CRITICAL" then 0
elif .severity == "HIGH" then 1
elif .severity == "MEDIUM" then 2
elif .severity == "LOW" then 3
else 4 end
)
}' > security/cves.json
echo "security/cves.json size:"
wc -c security/cves.json
- name: Commit security/cves.json if changed
if: always() && github.event_name != 'pull_request'
run: |
set -euo pipefail
if git diff --quiet security/cves.json 2>/dev/null; then
echo "no change"
exit 0
fi
git config user.name "cairn-security-bot"
git config user.email "cairn-security-bot@users.noreply.github.com"
# Race-safe push. The scan output lives in a tmp file so we
# can rebase on top of fresh main without a merge conflict
# against a concurrent run that touched the same file.
# Each iteration re-applies our scan on top of whatever
# main is right now.
NEW=$(mktemp)
cp security/cves.json "$NEW"
for try in 1 2 3 4 5; do
git fetch --quiet origin main
git reset --hard origin/main
cp "$NEW" security/cves.json
if git diff --quiet security/cves.json; then
echo "main already has identical content"
exit 0
fi
git add security/cves.json
git commit -m "chore(security): refresh cves.json [skip ci]"
if git push origin HEAD:main; then
echo "pushed on attempt $try"
exit 0
fi
echo "push rejected on attempt $try; retrying"
sleep 2
done
echo "could not push after 5 attempts" >&2
exit 1
- name: Attach summary to job log
if: always()
run: |
{
echo '### Trivy image scan (`'"${IMAGE_REF}"'`)'
echo
echo '```'
cat trivy-summary.txt
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
trivy-fs:
name: Trivy — workspace filesystem
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy on the source tree (Cargo.lock + Dockerfile)
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: fs
format: sarif
output: trivy-fs.sarif
severity: CRITICAL,HIGH,MEDIUM
ignore-unfixed: false
exit-code: 0
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-fs.sarif
category: trivy-fs