refactor: streamline supply chain workflows by removing Syft and Grype installations and utilizing official Anchore actions for SBOM generation and vulnerability scanning

This commit is contained in:
GitHub Actions
2026-02-03 07:09:54 +00:00
parent de66689b79
commit 4178910eac
3 changed files with 95 additions and 121 deletions

View File

@@ -30,8 +30,6 @@ env:
GHCR_REGISTRY: ghcr.io
DOCKERHUB_REGISTRY: docker.io
IMAGE_NAME: wikid82/charon
SYFT_VERSION: v1.17.0@sha256:b3b6e6f7e8d9c0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4
GRYPE_VERSION: v0.107.0@sha256:a1a2a3a4a5a6a7a8a9b0b1b2b3b4b5b6b7b8b9c0c1c2c3c4c5c6c7c8c9d0d1
jobs:
build-and-push:

View File

@@ -19,10 +19,6 @@ concurrency:
group: supply-chain-pr-${{ github.event.workflow_run.head_branch || github.ref }}
cancel-in-progress: true
env:
SYFT_VERSION: v1.17.0
GRYPE_VERSION: v0.107.0
permissions:
contents: read
pull-requests: write
@@ -217,53 +213,46 @@ jobs:
echo "image_name=${IMAGE_NAME}" >> "$GITHUB_OUTPUT"
echo "✅ Loaded image: ${IMAGE_NAME}"
- name: Install Syft
if: steps.check-artifact.outputs.artifact_found == 'true'
run: |
echo "📦 Installing Syft ${SYFT_VERSION}..."
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | \
sh -s -- -b /usr/local/bin "${SYFT_VERSION}"
syft version
- name: Install Grype
if: steps.check-artifact.outputs.artifact_found == 'true'
run: |
echo "📦 Installing Grype ${GRYPE_VERSION}..."
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | \
sh -s -- -b /usr/local/bin "${GRYPE_VERSION}"
grype version
# Generate SBOM using official Anchore action (auto-updated by Renovate)
- name: Generate SBOM
if: steps.check-artifact.outputs.artifact_found == 'true'
uses: anchore/sbom-action@deef08a0db64bfad603422135db61477b16cef56 # v0.22.1
id: sbom
with:
image: ${{ steps.load-image.outputs.image_name }}
format: cyclonedx-json
output-file: sbom.cyclonedx.json
- name: Count SBOM components
if: steps.check-artifact.outputs.artifact_found == 'true'
id: sbom-count
run: |
IMAGE_NAME="${{ steps.load-image.outputs.image_name }}"
echo "📋 Generating SBOM for: ${IMAGE_NAME}"
syft "${IMAGE_NAME}" \
--output cyclonedx-json=sbom.cyclonedx.json \
--output table
# Count components
COMPONENT_COUNT=$(jq '.components | length' sbom.cyclonedx.json 2>/dev/null || echo "0")
echo "component_count=${COMPONENT_COUNT}" >> "$GITHUB_OUTPUT"
echo "✅ SBOM generated with ${COMPONENT_COUNT} components"
# Scan for vulnerabilities using official Anchore action (auto-updated by Renovate)
- name: Scan for vulnerabilities
if: steps.check-artifact.outputs.artifact_found == 'true'
uses: anchore/scan-action@8d2fce09422cd6037e577f4130e9b925e9a37175 # v7.3.1
id: grype-scan
with:
sbom: sbom.cyclonedx.json
fail-build: false
output-format: json
- name: Process vulnerability results
if: steps.check-artifact.outputs.artifact_found == 'true'
id: vuln-summary
run: |
echo "🔍 Scanning SBOM for vulnerabilities..."
# Run Grype against the SBOM
grype sbom:sbom.cyclonedx.json \
--output json \
--file grype-results.json || true
# Generate SARIF output for GitHub Security
grype sbom:sbom.cyclonedx.json \
--output sarif \
--file grype-results.sarif || true
# The scan-action outputs results.json and results.sarif
# Rename for consistency with downstream steps
if [[ -f results.json ]]; then
mv results.json grype-results.json
fi
if [[ -f results.sarif ]]; then
mv results.sarif grype-results.sarif
fi
# Count vulnerabilities by severity
if [[ -f grype-results.json ]]; then
@@ -295,8 +284,7 @@ jobs:
- name: Upload SARIF to GitHub Security
if: steps.check-artifact.outputs.artifact_found == 'true'
# github/codeql-action v4
uses: github/codeql-action/upload-sarif@ab5b0e3aabf4de044f07a63754c2110d3ef2df38
uses: github/codeql-action/upload-sarif@ab5b0e3aabf4de044f07a63754c2110d3ef2df38 # v4
continue-on-error: true
with:
sarif_file: grype-results.sarif
@@ -319,12 +307,12 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER="${{ steps.pr-number.outputs.pr_number }}"
COMPONENT_COUNT="${{ steps.sbom.outputs.component_count }}"
CRITICAL_COUNT="${{ steps.grype-scan.outputs.critical_count }}"
HIGH_COUNT="${{ steps.grype-scan.outputs.high_count }}"
MEDIUM_COUNT="${{ steps.grype-scan.outputs.medium_count }}"
LOW_COUNT="${{ steps.grype-scan.outputs.low_count }}"
TOTAL_COUNT="${{ steps.grype-scan.outputs.total_count }}"
COMPONENT_COUNT="${{ steps.sbom-count.outputs.component_count }}"
CRITICAL_COUNT="${{ steps.vuln-summary.outputs.critical_count }}"
HIGH_COUNT="${{ steps.vuln-summary.outputs.high_count }}"
MEDIUM_COUNT="${{ steps.vuln-summary.outputs.medium_count }}"
LOW_COUNT="${{ steps.vuln-summary.outputs.low_count }}"
TOTAL_COUNT="${{ steps.vuln-summary.outputs.total_count }}"
# Determine status emoji
if [[ "${CRITICAL_COUNT}" -gt 0 ]]; then

View File

@@ -57,14 +57,6 @@ jobs:
echo " Event: ${{ github.event.workflow_run.event }}"
echo " PR Count: ${{ toJson(github.event.workflow_run.pull_requests) }}"
- name: Install Verification Tools
run: |
# Install Syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Install Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
- name: Determine Image Tag
id: tag
run: |
@@ -119,40 +111,30 @@ jobs:
echo "exists=false" >> $GITHUB_OUTPUT
fi
# Generate SBOM using official Anchore action (auto-updated by Renovate)
- name: Generate and Verify SBOM
if: steps.image-check.outputs.exists == 'true'
uses: anchore/sbom-action@deef08a0db64bfad603422135db61477b16cef56 # v0.22.1
with:
image: ghcr.io/${{ github.repository_owner }}/charon:${{ steps.tag.outputs.tag }}
format: cyclonedx-json
output-file: sbom-verify.cyclonedx.json
- name: Verify SBOM Completeness
if: steps.image-check.outputs.exists == 'true'
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/charon:${{ steps.tag.outputs.tag }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Verifying SBOM for ${IMAGE}..."
echo "Verifying SBOM completeness..."
echo ""
# Log Syft version for debugging
echo "Syft version:"
syft version
echo ""
# Count components
COMPONENT_COUNT=$(jq '.components | length' sbom-verify.cyclonedx.json 2>/dev/null || echo "0")
# Generate fresh SBOM in CycloneDX format (aligned with docker-build.yml)
echo "Generating SBOM in CycloneDX JSON format..."
if ! syft ${IMAGE} -o cyclonedx-json > sbom-generated.json; then
echo "❌ Failed to generate SBOM"
echo ""
echo "Debug information:"
echo "Image: ${IMAGE}"
echo "Syft exit code: $?"
exit 1 # Fail on real errors, not silent exit
fi
echo "SBOM components: ${COMPONENT_COUNT}"
# Check SBOM content
GENERATED_COUNT=$(jq '.components | length' sbom-generated.json 2>/dev/null || echo "0")
echo "Generated SBOM components: ${GENERATED_COUNT}"
if [[ ${GENERATED_COUNT} -eq 0 ]]; then
if [[ ${COMPONENT_COUNT} -eq 0 ]]; then
echo "⚠️ SBOM contains no components - may indicate an issue"
else
echo "✅ SBOM contains ${GENERATED_COUNT} components"
echo "✅ SBOM contains ${COMPONENT_COUNT} components"
fi
- name: Upload SBOM Artifact
@@ -160,7 +142,7 @@ jobs:
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: sbom-${{ steps.tag.outputs.tag }}
path: sbom-generated.json
path: sbom-verify.cyclonedx.json
retention-days: 30
- name: Validate SBOM File
@@ -178,32 +160,32 @@ jobs:
fi
# Check file exists
if [[ ! -f sbom-generated.json ]]; then
if [[ ! -f sbom-verify.cyclonedx.json ]]; then
echo "❌ SBOM file does not exist"
echo "valid=false" >> $GITHUB_OUTPUT
exit 0
fi
# Check file is non-empty
if [[ ! -s sbom-generated.json ]]; then
if [[ ! -s sbom-verify.cyclonedx.json ]]; then
echo "❌ SBOM file is empty"
echo "valid=false" >> $GITHUB_OUTPUT
exit 0
fi
# Validate JSON structure
if ! jq empty sbom-generated.json 2>/dev/null; then
if ! jq empty sbom-verify.cyclonedx.json 2>/dev/null; then
echo "❌ SBOM file contains invalid JSON"
echo "SBOM content:"
cat sbom-generated.json
cat sbom-verify.cyclonedx.json
echo "valid=false" >> $GITHUB_OUTPUT
exit 0
fi
# Validate CycloneDX structure
BOMFORMAT=$(jq -r '.bomFormat // "missing"' sbom-generated.json)
SPECVERSION=$(jq -r '.specVersion // "missing"' sbom-generated.json)
COMPONENTS=$(jq '.components // [] | length' sbom-generated.json)
BOMFORMAT=$(jq -r '.bomFormat // "missing"' sbom-verify.cyclonedx.json)
SPECVERSION=$(jq -r '.specVersion // "missing"' sbom-verify.cyclonedx.json)
COMPONENTS=$(jq '.components // [] | length' sbom-verify.cyclonedx.json)
echo "SBOM Format: ${BOMFORMAT}"
echo "Spec Version: ${SPECVERSION}"
@@ -224,42 +206,48 @@ jobs:
echo "valid=true" >> $GITHUB_OUTPUT
fi
- name: Scan for Vulnerabilities
if: steps.validate-sbom.outputs.valid == 'true'
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/charon:${{ steps.tag.outputs.tag }}
run: |
echo "Scanning for vulnerabilities with Grype..."
echo "SBOM format: CycloneDX JSON"
echo "SBOM size: $(wc -c < sbom-generated.json) bytes"
echo "SBOM Format: ${BOMFORMAT}"
echo "Spec Version: ${SPECVERSION}"
echo "Components: ${COMPONENTS}"
echo ""
# Update Grype vulnerability database
echo "Updating Grype vulnerability database..."
grype db update
echo ""
# Run Grype with explicit path and better error handling
if ! grype sbom:./sbom-generated.json --output json --file vuln-scan.json; then
echo ""
echo "❌ Grype scan failed"
echo ""
echo "Debug information:"
echo "Grype version:"
grype version
echo ""
echo "SBOM preview (first 1000 characters):"
head -c 1000 sbom-generated.json
echo ""
exit 1 # Fail the step to surface the issue
if [[ "${BOMFORMAT}" != "CycloneDX" ]]; then
echo "❌ Invalid bomFormat: expected 'CycloneDX', got '${BOMFORMAT}'"
echo "valid=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "✅ Grype scan completed successfully"
echo ""
if [[ "${COMPONENTS}" == "0" ]]; then
echo "⚠️ SBOM has no components - may indicate incomplete scan"
echo "valid=partial" >> $GITHUB_OUTPUT
else
echo "✅ SBOM is valid with ${COMPONENTS} components"
echo "valid=true" >> $GITHUB_OUTPUT
fi
# Display human-readable results
echo "Vulnerability summary:"
grype sbom:./sbom-generated.json --output table || true
# Scan for vulnerabilities using official Anchore action (auto-updated by Renovate)
- name: Scan for Vulnerabilities
if: steps.validate-sbom.outputs.valid == 'true'
uses: anchore/scan-action@8d2fce09422cd6037e577f4130e9b925e9a37175 # v7.3.1
id: scan
with:
sbom: sbom-verify.cyclonedx.json
fail-build: false
output-format: json
- name: Process Vulnerability Results
if: steps.validate-sbom.outputs.valid == 'true'
run: |
echo "Processing vulnerability results..."
# The scan-action outputs results.json and results.sarif
# Rename for consistency
if [[ -f results.json ]]; then
mv results.json vuln-scan.json
fi
if [[ -f results.sarif ]]; then
mv results.sarif vuln-scan.sarif
fi
# Parse and categorize results
CRITICAL=$(jq '[.matches[] | select(.vulnerability.severity == "Critical")] | length' vuln-scan.json 2>/dev/null || echo "0")