From 4178910eac8e4b13cda1383dceadbeb123597be4 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Feb 2026 07:09:54 +0000 Subject: [PATCH] refactor: streamline supply chain workflows by removing Syft and Grype installations and utilizing official Anchore actions for SBOM generation and vulnerability scanning --- .github/workflows/docker-build.yml | 2 - .github/workflows/supply-chain-pr.yml | 82 ++++++-------- .github/workflows/supply-chain-verify.yml | 132 ++++++++++------------ 3 files changed, 95 insertions(+), 121 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index b3edbd8c..4787fe33 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -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: diff --git a/.github/workflows/supply-chain-pr.yml b/.github/workflows/supply-chain-pr.yml index f6b1b0ee..9fcefc02 100644 --- a/.github/workflows/supply-chain-pr.yml +++ b/.github/workflows/supply-chain-pr.yml @@ -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 diff --git a/.github/workflows/supply-chain-verify.yml b/.github/workflows/supply-chain-verify.yml index be60bad3..29a342b3 100644 --- a/.github/workflows/supply-chain-verify.yml +++ b/.github/workflows/supply-chain-verify.yml @@ -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")