Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/actions/sbom-update/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Generate SBOM
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto-opening a PR after another PR merges is just likely to be forgotten by our team. Could we instead add a PR check that blocks a PR from merging if there is a diff in the generated SBOM (and include tooling so that we can generate the SBOM locally)?

This does requires manual generation from us but this works better for our workflow.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per Slack conversation this replaced with automatic commit to a main branch.

description: Generates CycloneDX SBOM using cdxgen
inputs:
output-file:
description: "Output filename for the SBOM"
required: false
default: "sbom.json"
outputs:
HAS_CHANGES:
description: "Whether the SBOM has meaningful changes compared to the existing version"
value: ${{ steps.check_changes.outputs.HAS_CHANGES }}
runs:
using: composite
steps:
- name: Generate SBOM
id: generate-sbom
shell: bash
run: |
echo "Generating SBOM for 'node' project..."
npx @cyclonedx/cyclonedx-npm --package-lock-only --output-file sbom-new.json --output-format json --spec-version 1.5

- name: Validate SBOM presence
shell: bash
run: |
if [ ! -f "sbom-new.json" ]; then
echo "Error: SBOM file not found"
exit 1
fi
echo "SBOM file generated: sbom-new.json"

- name: Download CycloneDX CLI
shell: bash
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
shell: bash
run: /tmp/cyclonedx validate --input-file sbom-new.json --fail-on-errors

- name: Check for SBOM changes
id: check_changes
if: steps.generate-sbom.outcome == 'success'
shell: bash
env:
SBOM_FILE: ${{ inputs.output-file }}
run: |
JQ_NORMALIZER='del(.serialNumber) | del(.metadata.timestamp) | walk(if type == "object" and .timestamp then .timestamp = "TIMESTAMP_NORMALIZED" else . end)'

if [ -f "$SBOM_FILE" ]; then
echo "Comparing new SBOM with existing $SBOM_FILE..."

# First try cyclonedx diff for component-level comparison
DIFF_OUTPUT=$(/tmp/cyclonedx diff "$SBOM_FILE" sbom-new.json --component-versions 2>/dev/null || true)

if echo "$DIFF_OUTPUT" | grep -q "^None$"; then
echo "No component changes detected via cyclonedx diff"

# Double-check with jq normalization (excludes metadata like timestamps)
if diff -q \
<(cat "$SBOM_FILE" | jq -r "$JQ_NORMALIZER") \
<(cat sbom-new.json | jq -r "$JQ_NORMALIZER") > /dev/null 2>&1; then
echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT
echo "No meaningful changes detected in SBOM"
rm sbom-new.json
else
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
echo "Changes detected in SBOM (non-component changes)"
mv sbom-new.json "$SBOM_FILE"
fi
else
echo "Component changes detected:"
echo "$DIFF_OUTPUT"
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
mv sbom-new.json "$SBOM_FILE"
fi
else
echo "No existing $SBOM_FILE found, creating initial version"
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
mv sbom-new.json "$SBOM_FILE"
fi
continue-on-error: true

- name: Final SBOM validation
shell: bash
run: |
if [ ! -f "${{ inputs.output-file }}" ]; then
echo "Error: Final SBOM file not found at ${{ inputs.output-file }}"
exit 1
fi
echo "SBOM file validated: ${{ inputs.output-file }}"
19 changes: 19 additions & 0 deletions .github/actions/setup-sbom/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Setup PHP SBOM
description: Sets up environment for generating SBOM in PHP projects
inputs:
working-directory:
description: "The directory where composer.json is located"
required: false
default: "."

runs:
using: composite
steps:
- name: Setup Node.js (for cdxgen)
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
shell: bash
run: npm ci
19 changes: 19 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Setup Node SBOM
description: Sets up environment for generating SBOM in Node.js projects
inputs:
working-directory:
description: "The directory where package.json is located"
required: false
default: "."

runs:
using: composite
steps:
- name: Setup Node.js (for cdxgen)
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install cdxgen
shell: bash
run: npm install -g @cyclonedx/cdxgen
68 changes: 68 additions & 0 deletions .github/workflows/sbom.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Post-Merge SBOM Update
on:
push:
branches:
- main
paths:
- 'package.json'
- 'package-lock.json'
workflow_dispatch:

env:
SBOM_FILE: "sbom.json"

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@v5
with:
ref: ${{ github.ref }}
token: ${{ secrets.GITHUB_TOKEN }}

- name: Install Node and dependencies
uses: mongodb-labs/drivers-github-tools/node/setup@v3
with:
ignore_install_scripts: false

- name: Load version and package info
uses: mongodb-labs/drivers-github-tools/node/get_version_info@v3
with:
npm_package_name: mongodb

- name: Generate/Update package-lock.json
run: |
echo "Resolving dependencies and generating package-lock.json..."
npm install --package-lock-only
echo "package-lock.json generated with resolved versions"

- name: Setup SBOM environment
uses: ./.github/actions/setup-sbom

- name: Generate SBOM
id: generate_sbom
uses: ./.github/actions/sbom-update
with:
output-file: ${{ env.SBOM_FILE }}

- name: Commit SBOM changes
if: steps.generate_sbom.outputs.HAS_CHANGES == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add ${{ env.SBOM_FILE }}
git commit -m "chore(deps): Update SBOM after dependency changes"
git push
echo "SBOM updated and committed" >> $GITHUB_STEP_SUMMARY
continue-on-error: true