diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml new file mode 100644 index 0000000000..187211c8b5 --- /dev/null +++ b/.github/workflows/sbom.yml @@ -0,0 +1,130 @@ +name: Generate SBOM + +# This workflow uses cyclonedx/cdxgen and publishes an sbom.json artifact. +# It runs on manual trigger or when package files change on master branch, +# and creates a PR with the updated SBOM. +# Internal documentation: go/sbom-scope + +on: + workflow_dispatch: {} + push: + branches: ['master'] + paths: + - 'Gemfile' + - 'Gemfile.lock' + - 'mongo.gemspec' + +permissions: + contents: write + pull-requests: write + +jobs: + sbom: + name: Generate SBOM and Create PR + runs-on: ubuntu-latest + concurrency: + group: sbom-${{ github.ref }} + cancel-in-progress: false + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.2" + + - name: Install dependencies + run: bundle install --standalone + + - name: Generate SBOM + run: | + # Generate SBOM with license fetching and required-only flag + FETCH_LICENSE=true npx -y -p "@cyclonedx/cdxgen@11.0.0" \ + cdxgen \ + --type ruby \ + --spec-version 1.5 \ + --required-only \ + --output sbom.json + + # Post-process SBOM: remove incompatible fields and fix licenses + jq ' + # Remove incompatible fields for silkbomb compatibility + del(.metadata.lifecycles) | + walk(if type == "object" then del(.evidence) else . end) | + + # Fix missing licenses + .components |= map( + if .name == "yard-solargraph" and + (.licenses == null or .licenses == []) then + . + {licenses: [{license: { + id: "MIT", + url: "https://opensource.org/licenses/MIT" + }}]} + else + . + end + ) + ' sbom.json > sbom.tmp.json && mv sbom.tmp.json sbom.json + + - name: Download CycloneDX CLI + run: | + curl -L -s -o /tmp/cyclonedx \ + "https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.29.1/cyclonedx-linux-x64" + chmod +x /tmp/cyclonedx + + - name: Validate SBOM + run: | + /tmp/cyclonedx validate --input-file sbom.json \ + --fail-on-errors + + - name: Cleanup vendor directory + if: always() + run: | + # Remove vendor directory if it was created during bundle install + rm -rf vendor/ + + - name: Upload SBOM artifact + uses: actions/upload-artifact@v4 + with: + name: sbom + path: sbom.json + if-no-files-found: error + + - name: Create Pull Request + # peter-evans/create-pull-request v7.0.6 + uses: peter-evans/create-pull-request@b4733b9419fd47bbfa1807b15627e17cd70b5b22 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: 'chore: Update SBOM after dependency changes' + branch: auto-update-sbom-${{ github.run_id }} + delete-branch: true + title: 'chore: Update SBOM' + add-paths: | + sbom.json + body: | + ## Automated SBOM Update + + This PR was automatically generated because dependency + manifest files changed. + + ### Changes + - Updated `sbom.json` to reflect current dependencies + + ### Verification + The SBOM was generated using @cyclonedx/cdxgen v11.0.0 with + the ruby type. + + ### Triggered by + - Commit: ${{ github.sha }} + - Workflow run: ${{ github.run_id }} + + --- + _This PR was created automatically by the [SBOM workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})_ + labels: | + sbom + automated + dependencies