Skip to content
Open
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
81 changes: 81 additions & 0 deletions .github/workflows/sbom.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Generate SBOM
# Generates sbom.json using cyclonedx-dotnet and opens a PR if it has changed.
# Triggered when NuGet dependency files change on main, or manually.
# Internal documentation: go/sbom-scope
on:
workflow_dispatch: {}
push:
branches:
- main
paths:
- "src/**/*.csproj"
- "src/Directory.Build.props"
- "MongoDB.Driver.sbom.slnf"
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:
fetch-depth: 0
- name: Set up .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0"
- name: Stash existing SBOM
run: |
# Strip nondeterministic fields before diffing
jq 'del(.metadata.timestamp, .version)' sbom.json > ${{ runner.temp }}/sbom.existing.json
- name: Generate SBOM
run: bash evergreen/generate-sbom.sh
env:
GITHUB_USER: ${{ github.actor }}
GITHUB_APIKEY: ${{ secrets.GITHUB_TOKEN }}
- name: Download CycloneDX CLI
run: |
curl -fsSL -o ${{ runner.temp }}/cyclonedx \
"https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.32.0/cyclonedx-linux-x64"
echo "454879e6a4a405c8a13bff49b8982adcb0596f3019b26b0811c66e4d7f0783e1 ${{ runner.temp }}/cyclonedx" | sha256sum --check
chmod +x ${{ runner.temp }}/cyclonedx
- name: Validate SBOM
run: ${{ runner.temp }}/cyclonedx validate --input-file sbom.json --fail-on-errors
- name: Check for SBOM changes
id: sbom_diff
run: |
jq 'del(.metadata.timestamp, .version)' sbom.json > ${{ runner.temp }}/sbom.generated.json
RESULT=$(diff --brief ${{ runner.temp }}/sbom.existing.json ${{ runner.temp }}/sbom.generated.json || true)
echo "result=$RESULT" | tee -a $GITHUB_OUTPUT
- name: Open Pull Request if SBOM has changed
if: steps.sbom_diff.outputs.result
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
add-paths: sbom.json
branch: auto-update-sbom-${{ github.ref_name }}
commit-message: "chore: Update SBOM after dependency changes"
delete-branch: true
labels: sbom
title: "CSHARP-5626: Update SBOM [${{ github.ref_name }}]"
body: |
## Automated SBOM Update

This PR was automatically generated because a NuGet dependency change was merged
into `${{ github.ref_name }}`.

`sbom.json` has been regenerated to reflect the current NuGet package dependency
graph across all four MongoDB.Driver packages. The SBOM serial number is stable;
the `.version` field has been incremented to reflect this update.

Once merged, an Evergreen CI task will upload the updated SBOM to the internal
tracking system.

### Triggered by
- Commit: ${{ github.sha }}
- Workflow run: [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
11 changes: 11 additions & 0 deletions MongoDB.Driver.sbom.slnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"solution": {
"path": "CSharpDriver.sln",
"projects": [
"src/MongoDB.Bson/MongoDB.Bson.csproj",
"src/MongoDB.Driver/MongoDB.Driver.csproj",
"src/MongoDB.Driver.Authentication.AWS/MongoDB.Driver.Authentication.AWS.csproj",
"src/MongoDB.Driver.Encryption/MongoDB.Driver.Encryption.csproj"
]
}
}
2 changes: 2 additions & 0 deletions evergreen/download-augmented-sbom.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ docker run --platform="linux/amd64" --rm -v ${PWD}:/pwd \
--env-file ${PWD}/kondukto_credentials.env \
901841024863.dkr.ecr.us-east-1.amazonaws.com/release-infrastructure/silkbomb:2.0 \
augment --repo mongodb/mongo-csharp-driver --branch main --sbom-in /pwd/sbom.json --sbom-out /pwd/${SSDLC_PATH}/augmented-sbom.json

cp "${SSDLC_PATH}/augmented-sbom.json" ./vex.cdx.json
72 changes: 72 additions & 0 deletions evergreen/evergreen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,60 @@ functions:
params:
file: mo-expansion.yml

generate-sbom:
- command: shell.exec
params:
working_dir: "mongo-csharp-driver"
env:
GITHUB_USER: ${github_user}
GITHUB_APIKEY: ${github_apikey}
PACKAGE_VERSION: ${PACKAGE_VERSION}
script: |
${PREPARE_SHELL}
./evergreen/generate-sbom.sh
Comment on lines +225 to +235

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.

The generate-sbom function is defined here but I can't find anything that invokes it — no task uses func: generate-sbom, and the sbom variant only runs upload-sbom. Since SBOM generation now lives entirely in the .github/workflows/sbom.yml GitHub Actions workflow, is this Evergreen function leftover from the earlier design? If so, let's delete it; if it's intended for manual/future use, a comment saying so would help.


upload-sbom:
- command: expansions.update
params:
updates:
- key: DOCKER_CONFIG
value: "${workdir}/.docker"
- command: ec2.assume_role
display_name: Assume ECR readonly IAM role
params:
role_arn: arn:aws:iam::901841024863:role/ecr-role-evergreen-ro
- command: subprocess.exec
type: setup
display_name: Log in to ECR
params:
binary: bash
include_expansions_in_env:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_SESSION_TOKEN
- DOCKER_CONFIG
args:
- -c
- aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 901841024863.dkr.ecr.us-east-1.amazonaws.com
- command: ec2.assume_role
display_name: Assume Silkbomb IAM role
params:
role_arn: arn:aws:iam::901841024863:role/silkbomb
- command: subprocess.exec
type: test
display_name: Upload SBOM via Silkbomb
params:
binary: bash
working_dir: mongo-csharp-driver
include_expansions_in_env:
- branch_name
- DOCKER_CONFIG
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_SESSION_TOKEN
args:
- evergreen/upload-sbom.sh

download-and-promote-augmented-sbom-to-s3-bucket:
- command: ec2.assume_role
params:
Expand Down Expand Up @@ -1847,6 +1901,13 @@ tasks:
- func: build-apidocs
- func: upload-apidocs

- name: upload-sbom
tags: ["ssdlc"]
allowed_requesters: ["commit"]
exec_timeout_secs: 600
commands:
- func: upload-sbom

- name: generate-ssdlc-reports
commands:
- command: ec2.assume_role
Expand Down Expand Up @@ -2916,3 +2977,14 @@ buildvariants:
depends_on:
- name: push-packages-myget
variant: ".push-packages-myget"

- name: sbom
display_name: "SBOM"
allowed_requesters: ["commit"]
stepback: false
paths:
- sbom.json
run_on:
- ubuntu2004-small
tasks:
- name: upload-sbom
114 changes: 114 additions & 0 deletions evergreen/generate-sbom.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# Environment Variables:
# PACKAGE_VERSION (optional) - Version to use for SBOM generation
# Defaults to output of get-version.sh
# GITHUB_USER (optional) - GitHub username for license resolution
# GITHUB_APIKEY (optional) - GitHub API token for license resolution
set -eo pipefail

Comment thread
adelinowona marked this conversation as resolved.
# Accommodate Git Bash or MSYS2 on Windows
export MSYS_NO_PATHCONV=1

SERIAL_NUMBER="urn:uuid:a24dfa6a-26a8-44d8-94bd-a2488e01185b"
SBOM_SLNF="MongoDB.Driver.sbom.slnf"

echo -e "\n************************************************"

# Get package version if not set
if [[ -z "$PACKAGE_VERSION" ]]; then
PACKAGE_VERSION=$(bash ./evergreen/get-version.sh)
fi

echo "Package Version: ${PACKAGE_VERSION}"

echo "Restoring solution with version set to ${PACKAGE_VERSION}"
dotnet restore "${SBOM_SLNF}" /p:Version="${PACKAGE_VERSION}" /p:TreatWarningsAsErrors=false

# Install cyclonedx-dotnet
echo "Installing cyclonedx-dotnet"
dotnet tool install --global CycloneDX --version 6.2.0 --allow-downgrade

echo -e "\nGenerating SBOM"
echo "************************************************"

# Attempt GitHub license resolution only if GITHUB_USER and GITHUB_APIKEY are both non-empty
if [[ -n "${GITHUB_USER:-}" && -n "${GITHUB_APIKEY:-}" ]]; then
echo "GitHub license resolution enabled for user: ${GITHUB_USER}"
github_options=(--enable-github-licenses --github-username "${GITHUB_USER}" --github-token "${GITHUB_APIKEY}")
else
echo "GitHub license resolution disabled (GITHUB_USER or GITHUB_APIKEY not set)"
github_options=()
fi

# Packages with PrivateAssets="All" in Directory.Build.props that --exclude-dev does not
# cover reliably. Version specifiers are not required in cyclonedx-dotnet 6.2.0+.
EXCLUDE_FILTER="Microsoft.CodeAnalysis.FxCopAnalyzers,Microsoft.NETFramework.ReferenceAssemblies,Microsoft.SourceLink.GitHub"

dotnet-CycloneDX "${SBOM_SLNF}" \
--disable-package-restore \
--configuration Release \
--set-type library \
--set-nuget-purl \
--exclude-dev \
--set-name mongo-csharp-driver \
--set-version "${PACKAGE_VERSION}" \
--exclude-filter "${EXCLUDE_FILTER}" \
--spec-version 1.5 \
--filename sbom.cdx.json \
"${github_options[@]+"${github_options[@]}"}"

echo -e "\n================================="
echo "Resolving libmongocrypt version"
echo "================================="

LIBMONGOCRYPT_VERSION=$(dotnet msbuild src/MongoDB.Driver.Encryption/MongoDB.Driver.Encryption.csproj \
-getProperty:LibMongoCryptVersion 2>/dev/null)

if [[ -z "$LIBMONGOCRYPT_VERSION" ]]; then
echo "ERROR: Could not extract LibMongoCryptVersion from MongoDB.Driver.Encryption.csproj" >&2
exit 1
fi

echo "libmongocrypt version: ${LIBMONGOCRYPT_VERSION}"

LIBMONGOCRYPT_PURL="pkg:github/mongodb/libmongocrypt@${LIBMONGOCRYPT_VERSION}"
ENCRYPTION_PURL="pkg:nuget/MongoDB.Driver.Encryption@${PACKAGE_VERSION}"

tmp=$(mktemp)
jq \
--arg purl "$LIBMONGOCRYPT_PURL" \
--arg ver "$LIBMONGOCRYPT_VERSION" \
--arg encpurl "$ENCRYPTION_PURL" \
'.components += [{
"type": "library",
"bom-ref": $purl,
"name": "libmongocrypt",
"version": $ver,
"purl": $purl
}] |
.dependencies += [{
"ref": $encpurl,
"dependsOn": [$purl]
}]' sbom.cdx.json > "$tmp" && mv "$tmp" sbom.cdx.json

echo -e "\n================================="
echo "Updating sbom.json with version tracking"
echo "================================="

CURRENT_VERSION=$(jq -r '.version // 0' sbom.json 2>/dev/null || echo 0)
NEW_CONTENT=$(jq -S 'del(.version, .metadata.timestamp)' sbom.cdx.json)
OLD_CONTENT=$(jq -S 'del(.version, .metadata.timestamp)' sbom.json 2>/dev/null || echo '{}')

if [ "$NEW_CONTENT" = "$OLD_CONTENT" ]; then
NEW_VERSION=$CURRENT_VERSION
echo "SBOM content unchanged, keeping version ${NEW_VERSION}"
else
NEW_VERSION=$((CURRENT_VERSION + 1))
echo "SBOM content changed, incrementing version to ${NEW_VERSION}"
fi

jq --argjson v "$NEW_VERSION" --arg serial "$SERIAL_NUMBER" \
'.version = $v | .serialNumber = $serial' sbom.cdx.json > sbom.json
rm sbom.cdx.json

echo "Generated sbom.json (version ${NEW_VERSION})"
41 changes: 41 additions & 0 deletions evergreen/upload-sbom.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -eo pipefail

: "${branch_name:?}"
: "${AWS_ACCESS_KEY_ID:?}"
: "${AWS_SECRET_ACCESS_KEY:?}"
: "${AWS_SESSION_TOKEN:?}"

silkbomb="901841024863.dkr.ecr.us-east-1.amazonaws.com/release-infrastructure/silkbomb:2.0"
docker pull "${silkbomb}"

silkbomb_augment_flags=(
--repo mongodb/mongo-csharp-driver
--branch "${branch_name}"
--sbom-in /pwd/sbom.json
--sbom-out /pwd/augmented.sbom.json.new
--no-update-sbom-version
)

docker run --rm -v "$(pwd):/pwd" \
--user "$(id -u):$(id -g)" \
--env 'AWS_ACCESS_KEY_ID' --env 'AWS_SECRET_ACCESS_KEY' --env 'AWS_SESSION_TOKEN' \
"${silkbomb}" augment "${silkbomb_augment_flags[@]}"
Comment thread
jasonhills-mongodb marked this conversation as resolved.
Comment on lines +12 to +23

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.

This runs the same silkbomb augment command as our existing download-augmented-sbom.sh, but the upload-sbom function assumes the silkbomb role and forwards only AWS creds + branch_name + DOCKER_CONFIG — it never runs fetch-kondukto-token.sh and never passes --env-file kondukto_credentials.env / KONDUKTO_TOKEN.

The download path goes through kondukto_role_arn → fetch-kondukto-token.sh → --env-file for the identical augment call, which suggests augment needs the Kondukto token to reach Kondukto. Does the silkbomb IAM role somehow cover Kondukto auth (e.g. silkbomb self-fetches the token from Secrets Manager), or is the fetch-kondukto-token.sh + --env-file step missing here?


old_json=$(mktemp)
new_json=$(mktemp)
diff_txt=$(mktemp)
trap 'rm -f "$old_json" "$new_json" "$diff_txt"' EXIT

if [ -f ./augmented.sbom.json ]; then
jq -S 'del(.metadata.timestamp)' ./augmented.sbom.json > "$old_json"
else
echo '{}' > "$old_json"
fi
jq -S 'del(.metadata.timestamp)' ./augmented.sbom.json.new > "$new_json"
Comment on lines +30 to +35

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.

The "significant changes" check diffs augmented.sbom.json.new against augmented.sbom.json, but nothing in the upload-sbom task ever creates or downloads augmented.sbom.json (the script only writes the .new file, and the task runs func: upload-sbom alone). So old_json always falls into the echo '{}' branch and the diff reports significant changes on every run, making the guard effectively constant-true. Was the intent to download the previously-augmented SBOM as the baseline first (analogous to download-augmented-sbom.sh), or to persist .new back as the baseline after upload?


if ! diff -sty --left-column -W 200 "$old_json" "$new_json" > "$diff_txt"; then
declare status
status='{"status":"failed", "type":"test", "should_continue":true, "desc":"detected significant changes in Augmented SBOM"}'
curl -sS -d "${status}" -H "Content-Type: application/json" -X POST http://localhost:2285/task_status || true
fi
1 change: 0 additions & 1 deletion purls.txt

This file was deleted.

Loading