Skip to content
Closed
Changes from 1 commit
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
93 changes: 93 additions & 0 deletions .github/workflows/sbom.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Generate SBOM

# Runs on tag pushes matching the vX.Y.Z pattern.
# This generates and attaches the SPDX SBOM asset to the GitHub release.
# It is idempotent: if the SBOM asset already exists for the tag, it is
# overwritten with the fresh scan output.
on:
push:
tags:
- "v*"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are two workflows in this PR - sbom.yml and sbom-release.yml - that both trigger on 'v*' tag pushes, both generate a file named sbom.spdx.json, and both upload that same-named asset to the same GitHub release. On a real tag push they run concurrently and race to create/overwrite the single sbom.spdx.json asset (a second upload of an existing asset name fails with a 422, while the other deletes-then-reuploads). Ship exactly one workflow; delete the other. sbom.yml is the more complete one (deletes + re-uploads), so sbom-release.yml is likely redundant - but note sbom-release.yml cannot work at all anyway (see its upload step comment).


permissions:
contents: write
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
pull-requests: read

concurrency:
group: sbom-${{ github.ref }}
cancel-in-progress: false

jobs:
generate-sbom:
name: Generate and Attach SBOM
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout code
uses: actions/checkout@v4
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unpinned actions deviate from the repo convention. Every action in this repo is pinned to a commit SHA with a # vX.Y.Z comment (see ci.yml: actions/checkout@3d3c42... # v7.0.1, actions/setup-go@b7ad1dad... # v7.0.0), and the zizmor/scorecard CI enforces that. actions/checkout@v4 and actions/setup-go@v5 here use moving minor tags and will both be flagged. Pin both to a commit SHA with the version comment.

with:
persist-credentials: false

- name: Set up Go
uses: actions/setup-go@v5
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
with:
go-version-file: go.mod

- name: Install Syft (pinned version)
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/syft/v$SYFT_VERSION/install.sh | sh -s -- -b /usr/local/bin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SYFT_VERSION is never set in this step, so the install URL is broken. The env: SYFT_VERSION: v1.41.0 is declared only on the next step ("Generate SBOM", lines 43-44). Here it is unset, so the URL resolves to raw.githubusercontent.com/anchore/syft/v/install.sh, which 404s and the step fails. Move the env (or a hard-coded version) onto this Install step. Note also that even if it were set here, v$SYFT_VERSION with value v1.41.0 would expand to vv1.41.0 — drop the leading v from the value or from the URL template.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Supply-chain: curl-pipe-to-sh from a moving URL with no integrity check. This pipes install.sh fetched from raw.githubusercontent.com/anchore/syft/v$SYFT_VERSION/install.sh straight into sh, with no pinned version in this step and no checksum verification -- a supply-chain anti-pattern the repo zizmor/scorecard posture and SHA-pinning convention expects you to avoid. Prefer a SHA-pinned action (e.g. anchore/syft-action pinned to a commit hash for a fixed syft version) or download the exact-version release asset and verify its checksum. This also combines with the SYFT_VERSION scoping bug on the same line -- fix both together.


- name: Generate SBOM (SPDX format)
# Scan the project directory and output an SPDX SBOM file.
env:
SYFT_VERSION: 'v1.41.0'
run: |
syft . -o spdx-json=sbom.spdx.json

- name: Find release ID for the pushed tag
id: release
run: |
TAG_NAME="${GITHUB_REF#refs/tags/}"
# Query the GitHub API for a release with this tag name
RELEASE_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/$TAG_NAME" \
| jq -r '.id')

if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
echo "release_id=" >> "$GITHUB_OUTPUT"
else
echo "release_id=$RELEASE_ID" >> "$GITHUB_OUTPUT"
fi

- name: Check for existing SBOM asset and upload
if: steps.release.outputs.release_id != '' && steps.release.outputs.release_id != 'null'
run: |
RELEASE_ID="${{ steps.release.outputs.release_id }}"
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

# Check if an SBOM asset with this name already exists in the release
ASSET_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets" \
| jq -r ".[] | select(.name == \"sbom.spdx.json\") | .id")

if [ -n "$ASSET_ID" ] && [ "$ASSET_ID" != "null" ]; then
# Delete existing SBOM asset so we can re-upload the fresh scan
curl -s -X DELETE -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/$ASSET_ID" > /dev/null
echo "Deleted existing sbom.spdx.json asset"
fi

# Upload the SBOM as a release asset
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Content-Type: application/spdx+json" \
--data-binary @sbom.spdx.json \
"https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets" > /dev/null
echo "Uploaded sbom.spdx.json to release $RELEASE_ID"

- name: Notify if no release found for tag
if: steps.release.outputs.release_id == '' || steps.release.outputs.release_id == 'null'
run: |
echo "::notice::No GitHub Release found for tag ${GITHUB_REF#refs/tags/}. "
echo "The SBOM was generated at ./sbom.spdx.json but was not attached to a release."
echo "Ensure the tag was created by the release-please process "
"or manually create a release draft."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shell syntax error: the last line is a bare quoted string used as a command, so the step will fail with "or manually create a release draft.": command not found. The intent was clearly to continue the previous echo message. Fix by merging into a single echo:

echo "Ensure the tag was created by the release-please process or manually create a release draft."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shell syntax error: a bare quoted string on its own line. Line 93 is a stray "or manually create a release draft." used as a command, so this step will fail with command not found (and the preceding notice is split across two echoes). Merge it into the previous line:

echo "Ensure the tag was created by the release-please process or manually create a release draft."

Loading