Skip to content

Commit bc0a7fc

Browse files
committed
NODE-7025: Making SBOM validation more robust
1 parent a603292 commit bc0a7fc

File tree

2 files changed

+79
-37
lines changed

2 files changed

+79
-37
lines changed

.github/actions/sbom-update/action.yml

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,87 @@ inputs:
55
description: "Output filename for the SBOM"
66
required: false
77
default: "sbom.json"
8-
8+
outputs:
9+
HAS_CHANGES:
10+
description: "Whether the SBOM has meaningful changes compared to the existing version"
11+
value: ${{ steps.check_changes.outputs.HAS_CHANGES }}
912
runs:
1013
using: composite
1114
steps:
1215
- name: Generate SBOM
16+
id: generate-sbom
1317
shell: bash
14-
working-directory: ${{ inputs.working-directory }}
1518
run: |
1619
echo "Generating SBOM for 'node' project..."
17-
npx @cyclonedx/cyclonedx-npm --package-lock-only --omit dev --output-file sbom.json --output-format json --spec-version 1.5
20+
npx @cyclonedx/cyclonedx-npm --package-lock-only --output-file sbom-new.json --output-format json --spec-version 1.5
21+
22+
- name: Validate SBOM presence
23+
shell: bash
24+
run: |
25+
if [ ! -f "sbom-new.json" ]; then
26+
echo "Error: SBOM file not found"
27+
exit 1
28+
fi
29+
echo "SBOM file generated: sbom-new.json"
30+
31+
- name: Download CycloneDX CLI
32+
shell: bash
33+
run: |
34+
curl -L -s -o /tmp/cyclonedx "https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.29.1/cyclonedx-linux-x64"
35+
chmod +x /tmp/cyclonedx
1836
1937
- name: Validate SBOM
38+
shell: bash
39+
run: /tmp/cyclonedx validate --input-file sbom-new.json --fail-on-errors
40+
41+
- name: Check for SBOM changes
42+
id: check_changes
43+
if: steps.generate-sbom.outcome == 'success'
44+
shell: bash
45+
env:
46+
SBOM_FILE: ${{ inputs.output-file }}
47+
run: |
48+
JQ_NORMALIZER='del(.serialNumber) | del(.metadata.timestamp) | walk(if type == "object" and .timestamp then .timestamp = "TIMESTAMP_NORMALIZED" else . end)'
49+
50+
if [ -f "$SBOM_FILE" ]; then
51+
echo "Comparing new SBOM with existing $SBOM_FILE..."
52+
53+
# First try cyclonedx diff for component-level comparison
54+
DIFF_OUTPUT=$(/tmp/cyclonedx diff "$SBOM_FILE" sbom-new.json --component-versions 2>/dev/null || true)
55+
56+
if echo "$DIFF_OUTPUT" | grep -q "^None$"; then
57+
echo "No component changes detected via cyclonedx diff"
58+
59+
# Double-check with jq normalization (excludes metadata like timestamps)
60+
if diff -q \
61+
<(cat "$SBOM_FILE" | jq -r "$JQ_NORMALIZER") \
62+
<(cat sbom-new.json | jq -r "$JQ_NORMALIZER") > /dev/null 2>&1; then
63+
echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT
64+
echo "No meaningful changes detected in SBOM"
65+
rm sbom-new.json
66+
else
67+
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
68+
echo "Changes detected in SBOM (non-component changes)"
69+
mv sbom-new.json "$SBOM_FILE"
70+
fi
71+
else
72+
echo "Component changes detected:"
73+
echo "$DIFF_OUTPUT"
74+
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
75+
mv sbom-new.json "$SBOM_FILE"
76+
fi
77+
else
78+
echo "No existing $SBOM_FILE found, creating initial version"
79+
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
80+
mv sbom-new.json "$SBOM_FILE"
81+
fi
82+
continue-on-error: true
83+
84+
- name: Final SBOM validation
2085
shell: bash
2186
run: |
2287
if [ ! -f "${{ inputs.output-file }}" ]; then
23-
echo "Error: SBOM file not found"
88+
echo "Error: Final SBOM file not found at ${{ inputs.output-file }}"
2489
exit 1
2590
fi
26-
2791
echo "SBOM file validated: ${{ inputs.output-file }}"

.github/workflows/sbom.yml

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
name: Post-Merge SBOM Update
2-
32
on:
43
push:
54
branches:
@@ -8,8 +7,10 @@ on:
87
- 'package.json'
98
- 'package-lock.json'
109
workflow_dispatch:
10+
1111
env:
1212
SBOM_FILE: "sbom.json"
13+
1314
permissions:
1415
contents: write
1516
pull-requests: write
@@ -24,10 +25,10 @@ jobs:
2425
cancel-in-progress: false
2526

2627
steps:
27-
- name: Checkout repository (Base Branch)
28+
- name: Checkout repository
2829
uses: actions/checkout@v5
2930
with:
30-
ref: ${{ github.event.pull_request.base.ref || github.ref }}
31+
ref: ${{ github.ref }}
3132
token: ${{ secrets.GITHUB_TOKEN }}
3233

3334
- name: Install Node and dependencies
@@ -50,41 +51,18 @@ jobs:
5051
uses: ./.github/actions/setup-sbom
5152

5253
- name: Generate SBOM
54+
id: generate_sbom
5355
uses: ./.github/actions/sbom-update
5456
with:
55-
output-file: ${SBOM_FILE}
56-
57-
- name: Check for Changes in sbom.json
58-
id: git_status
59-
run: |
60-
# Filter to remove/normalize serialNumber and timestamp fields
61-
JQ_NORMALIZER='del(.serialNumber) | del(.metadata.timestamp) | walk(if type == "object" and .timestamp then .timestamp = "TIMESTAMP_NORMALIZED" else . end)'
62-
63-
# Check if the base file exists in Git (to prevent errors on first commit)
64-
if ! git show HEAD:$SBOM_FILE > /dev/null 2>&1; then
65-
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
66-
exit 0
67-
fi
68-
69-
# Compare the normalized committed version vs. the normalized current version
70-
if diff -q \
71-
<(git show HEAD:$SBOM_FILE | jq -r "$JQ_NORMALIZER") \
72-
<(cat $SBOM_FILE | jq -r "$JQ_NORMALIZER"); then
73-
74-
echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT
75-
echo "No changes detected in sbom.json"
76-
else
77-
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT
78-
echo "Changes detected in sbom.json"
79-
fi
57+
output-file: ${{ env.SBOM_FILE }}
8058

81-
- name: "Commit SBOM changes"
82-
if: steps.sbom_status.outputs.HAS_CHANGES == 'true'
59+
- name: Commit SBOM changes
60+
if: steps.generate_sbom.outputs.HAS_CHANGES == 'true'
8361
run: |
8462
git config user.name "github-actions[bot]"
8563
git config user.email "github-actions[bot]@users.noreply.github.com"
8664
git add ${{ env.SBOM_FILE }}
8765
git commit -m "chore(deps): Update SBOM after dependency changes"
8866
git push
89-
echo "📦 SBOM updated and committed" >> $GITHUB_STEP_SUMMARY
90-
continue-on-error: true
67+
echo "SBOM updated and committed" >> $GITHUB_STEP_SUMMARY
68+
continue-on-error: true

0 commit comments

Comments
 (0)