diff --git a/.evergreen/config.yml b/.evergreen/config.yml index f57ffb1e75..52db932796 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -630,6 +630,14 @@ tasks: binary: bash args: [*task-runner, govulncheck] + - name: generate-sbom + tags: ["ssdlc", "static-analysis"] + commands: + - command: subprocess.exec + params: + binary: bash + args: [*task-runner, generate-sbom] + - name: pull-request-helpers allowed_requesters: ["patch", "github_pr"] commands: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e2422188f..cb34e7a31c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -74,3 +74,10 @@ repos: language: system types: [go] entry: etc/check_license.sh + + - id: sbom-currency + name: sbom-currency + language: system + files: ^(go\.mod)$ + require_serial: true + entry: etc/generate-sbom.sh -c diff --git a/Taskfile.yml b/Taskfile.yml index 5fd8210bda..fa489e810b 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -10,8 +10,8 @@ dotenv: ['.test.env'] tasks: ### Utility tasks. ### - default: - deps: [build, check-license, check-fmt, check-modules, lint, test-short] + default: + deps: [build, check-license, check-fmt, check-modules, lint, test-short, generate-sbom] add-license: bash etc/check_license.sh -a @@ -87,6 +87,17 @@ tasks: govulncheck: bash etc/govulncheck.sh + generate-sbom: + desc: Generate a CycloneDX SBOM + summary: | + Generate a CycloneDX SBOM with the cyclonedx-gomod 'mod' subcommand + The SBOM includes the aggregate of modules required by packages in the mongo-go-driver library, excluding examples, tests and test packages. + Task will run only when go.mod is newer than sbom.json. + method: timestamp + sources: [go.mod] + generates: [sbom.json] + cmd: bash etc/generate-sbom.sh + update-notices: bash etc/generate_notices.pl > THIRD-PARTY-NOTICES ### Local testing tasks. ### diff --git a/etc/generate-sbom.sh b/etc/generate-sbom.sh new file mode 100755 index 0000000000..ef13f378c2 --- /dev/null +++ b/etc/generate-sbom.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -e + +CHECK_CURRENCY="false" + +# Options are: +# -c : check currency of staged sbom.json versus go.mod. +while getopts "c" opt; do + case $opt in + c) + CHECK_CURRENCY="true" + ;; + *) + echo "usage: $0 [-c]" >&2 + echo " -c : (optional) check currency of staged sbom.json versus go.mod." >&2 + exit 1 + ;; + esac +done +#shift $((OPTIND - 1)) + +if ! $CHECK_CURRENCY; then + # The cyclonedx-gomod 'mod' subcommand is used to generate a CycloneDX SBOM with GOWORK=off to exclude example/test code. + # TODO: Add libmongocrypt as an optional component via a merge once the libmongocrypt SBOM is updated with newer automation + + ## The pipe to jq is a temporary workaround until this issue is resolved: https://github.com/CycloneDX/cyclonedx-gomod/issues/662. + ## When resolved, bump version and replace with commented line below. + # GOWORK=off go run github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@[UPDATED VERSION] mod -type library -licenses -assert-licenses -output-version 1.5 -json -output sbom.json . + GOWORK=off go run github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@v1.9.0 mod -type library -licenses -assert-licenses -output-version 1.5 -json . | jq '.metadata.component.purl |= split("?")[0]' | jq '.components[].purl |= split("?")[0]' > sbom.json +elif [[ $(git diff --name-only --cached go.mod) && ! $(git diff --name-only --cached sbom.json) ]]; then + echo "'go.mod' has changed. 'sbom.json' must be re-generated (run 'task generate-sbom' or 'etc/generate-sbom.sh') and staged." && exit 1 +fi diff --git a/sbom.json b/sbom.json index 3f3df0c7cc..dccf370247 100644 --- a/sbom.json +++ b/sbom.json @@ -1,11 +1,379 @@ { - "metadata": { - "timestamp": "2024-05-02T17:36:29.429171+00:00" - }, - "components": [], - "serialNumber": "urn:uuid:06a59521-ad52-420b-aee6-7d9ed15e1fd9", - "version": 1, - "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", - "bomFormat": "CycloneDX", - "specVersion": "1.5" - } \ No newline at end of file + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "urn:uuid:e425de21-e2e7-462f-9e9e-8717ba68514e", + "version": 1, + "metadata": { + "timestamp": "2025-12-04T18:42:32-08:00", + "tools": [ + { + "vendor": "CycloneDX", + "name": "cyclonedx-gomod", + "version": "v1.9.0", + "hashes": [ + { + "alg": "MD5", + "content": "a87d4648b958d7e04c61971d2df2b020" + }, + { + "alg": "SHA-1", + "content": "b742b751dba5c90d13996a46302a7605142f5e54" + }, + { + "alg": "SHA-256", + "content": "2e1cba54adc4999b6e023f937d9e7d809a49d3f1fe928e44915d7930e26787e7" + }, + { + "alg": "SHA-384", + "content": "75c49bc565c88d862b55d6c0bef098b1a7d4d9b12ed248157079385931652b7e8df0d8e93f8355f075c3cbadb9039f45" + }, + { + "alg": "SHA-512", + "content": "c543586507ca06db1d728cc1daaf09f6ce18e6ba0b6d77f80c05b87c5bb4a9c5065ddc51637efe69dea6c0f6f9e9a8fd3bb5cb873369b9d414d6eb97b4fe2023" + } + ], + "externalReferences": [ + { + "url": "https://github.com/CycloneDX/cyclonedx-gomod", + "type": "vcs" + }, + { + "url": "https://cyclonedx.org", + "type": "website" + } + ] + } + ], + "component": { + "bom-ref": "pkg:golang/go.mongodb.org/mongo-driver/v2@v2.4.1-0.20251028034235-9bd07db1ed8f?type=module", + "type": "library", + "name": "go.mongodb.org/mongo-driver/v2", + "version": "v2.4.1-0.20251028034235-9bd07db1ed8f", + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/go.mongodb.org/mongo-driver/v2@v2.4.1-0.20251028034235-9bd07db1ed8f" + } + }, + "components": [ + { + "bom-ref": "pkg:golang/github.com/davecgh/go-spew@v1.1.1?type=module", + "type": "library", + "name": "github.com/davecgh/go-spew", + "version": "v1.1.1", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "be3f63feed5baa7bc211f24ec1486d94e011aacdfeae41d8635de36164d4f7b7" + } + ], + "licenses": [ + { + "license": { + "id": "0BSD" + } + } + ], + "purl": "pkg:golang/github.com/davecgh/go-spew@v1.1.1", + "externalReferences": [ + { + "url": "https://github.com/davecgh/go-spew", + "type": "vcs" + } + ] + }, + { + "bom-ref": "pkg:golang/github.com/golang/snappy@v1.0.0?type=module", + "type": "library", + "name": "github.com/golang/snappy", + "version": "v1.0.0", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "3b2eb4ec65571eced1b5b820b4f067af64660c0ac8b0079f0f0beb756bd1846b" + } + ], + "licenses": [ + { + "license": { + "id": "BSD-3-Clause" + } + } + ], + "purl": "pkg:golang/github.com/golang/snappy@v1.0.0", + "externalReferences": [ + { + "url": "https://github.com/golang/snappy", + "type": "vcs" + } + ] + }, + { + "bom-ref": "pkg:golang/github.com/klauspost/compress@v1.17.6?type=module", + "type": "library", + "name": "github.com/klauspost/compress", + "version": "v1.17.6", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "eb47aad84fe395fc105edbd911e0546000ecf81c0a056511218f868f67911a32" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/github.com/klauspost/compress@v1.17.6", + "externalReferences": [ + { + "url": "https://github.com/klauspost/compress", + "type": "vcs" + } + ] + }, + { + "bom-ref": "pkg:golang/github.com/xdg-go/pbkdf2@v1.0.0?type=module", + "type": "library", + "name": "github.com/xdg-go/pbkdf2", + "version": "v1.0.0", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "4aeec33eee3cc173300b76ececc08d1becf816173212ecf9765bdc85bab40747" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/github.com/xdg-go/pbkdf2@v1.0.0", + "externalReferences": [ + { + "url": "https://github.com/xdg-go/pbkdf2", + "type": "vcs" + } + ] + }, + { + "bom-ref": "pkg:golang/github.com/xdg-go/scram@v1.2.0?type=module", + "type": "library", + "name": "github.com/xdg-go/scram", + "version": "v1.2.0", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "6d8285d801301b9aea77506e993e20027bf053f33d9c1a76a534b1799c3b5afb" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/github.com/xdg-go/scram@v1.2.0", + "externalReferences": [ + { + "url": "https://github.com/xdg-go/scram", + "type": "vcs" + } + ] + }, + { + "bom-ref": "pkg:golang/github.com/xdg-go/stringprep@v1.0.4?type=module", + "type": "library", + "name": "github.com/xdg-go/stringprep", + "version": "v1.0.4", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "5cb23f360dced40b73ab4a01b374d69bee5956092ad9aa9d96f3fd26da19e9cf" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/github.com/xdg-go/stringprep@v1.0.4", + "externalReferences": [ + { + "url": "https://github.com/xdg-go/stringprep", + "type": "vcs" + } + ] + }, + { + "bom-ref": "pkg:golang/github.com/youmark/pkcs8@v0.0.0-20240726163527-a2c0da244d78?type=module", + "type": "library", + "name": "github.com/youmark/pkcs8", + "version": "v0.0.0-20240726163527-a2c0da244d78", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "8a5415d61cf38aef8b2ccdf3513274b6b473b5fc208ea2a705636d49191b9b03" + } + ], + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "purl": "pkg:golang/github.com/youmark/pkcs8@v0.0.0-20240726163527-a2c0da244d78", + "externalReferences": [ + { + "url": "https://github.com/youmark/pkcs8", + "type": "vcs" + } + ] + }, + { + "bom-ref": "pkg:golang/golang.org/x/crypto@v0.33.0?type=module", + "type": "library", + "name": "golang.org/x/crypto", + "version": "v0.33.0", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "20e04fb24922e8bcac8b4968f6a42f6f1890f85bec082fd858e79c087022c6eb" + } + ], + "licenses": [ + { + "license": { + "id": "BSD-Source-Code" + } + } + ], + "purl": "pkg:golang/golang.org/x/crypto@v0.33.0" + }, + { + "bom-ref": "pkg:golang/golang.org/x/sync@v0.11.0?type=module", + "type": "library", + "name": "golang.org/x/sync", + "version": "v0.11.0", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "186cfcf9740fe05bd34eb8d93f334a4cc16d4971fcd110331bee608453e02bdc" + } + ], + "licenses": [ + { + "license": { + "id": "BSD-Source-Code" + } + } + ], + "purl": "pkg:golang/golang.org/x/sync@v0.11.0" + }, + { + "bom-ref": "pkg:golang/golang.org/x/text@v0.22.0?type=module", + "type": "library", + "name": "golang.org/x/text", + "version": "v0.22.0", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "6e87eaee6dff1c016f6c5e758f3dd0f702e0de392f48fba266efe90f55f082d3" + } + ], + "licenses": [ + { + "license": { + "id": "BSD-Source-Code" + } + } + ], + "purl": "pkg:golang/golang.org/x/text@v0.22.0" + } + ], + "dependencies": [ + { + "ref": "pkg:golang/go.mongodb.org/mongo-driver/v2@v2.4.1-0.20251028034235-9bd07db1ed8f?type=module", + "dependsOn": [ + "pkg:golang/github.com/davecgh/go-spew@v1.1.1?type=module", + "pkg:golang/github.com/golang/snappy@v1.0.0?type=module", + "pkg:golang/github.com/klauspost/compress@v1.17.6?type=module", + "pkg:golang/github.com/xdg-go/scram@v1.2.0?type=module", + "pkg:golang/github.com/xdg-go/stringprep@v1.0.4?type=module", + "pkg:golang/github.com/youmark/pkcs8@v0.0.0-20240726163527-a2c0da244d78?type=module", + "pkg:golang/golang.org/x/crypto@v0.33.0?type=module", + "pkg:golang/golang.org/x/sync@v0.11.0?type=module" + ] + }, + { + "ref": "pkg:golang/github.com/davecgh/go-spew@v1.1.1?type=module" + }, + { + "ref": "pkg:golang/github.com/golang/snappy@v1.0.0?type=module" + }, + { + "ref": "pkg:golang/github.com/klauspost/compress@v1.17.6?type=module" + }, + { + "ref": "pkg:golang/github.com/xdg-go/pbkdf2@v1.0.0?type=module" + }, + { + "ref": "pkg:golang/github.com/xdg-go/scram@v1.2.0?type=module", + "dependsOn": [ + "pkg:golang/github.com/xdg-go/pbkdf2@v1.0.0?type=module", + "pkg:golang/github.com/xdg-go/stringprep@v1.0.4?type=module", + "pkg:golang/golang.org/x/text@v0.22.0?type=module" + ] + }, + { + "ref": "pkg:golang/github.com/xdg-go/stringprep@v1.0.4?type=module", + "dependsOn": [ + "pkg:golang/golang.org/x/text@v0.22.0?type=module" + ] + }, + { + "ref": "pkg:golang/github.com/youmark/pkcs8@v0.0.0-20240726163527-a2c0da244d78?type=module", + "dependsOn": [ + "pkg:golang/golang.org/x/crypto@v0.33.0?type=module" + ] + }, + { + "ref": "pkg:golang/golang.org/x/crypto@v0.33.0?type=module", + "dependsOn": [ + "pkg:golang/golang.org/x/text@v0.22.0?type=module" + ] + }, + { + "ref": "pkg:golang/golang.org/x/sync@v0.11.0?type=module" + }, + { + "ref": "pkg:golang/golang.org/x/text@v0.22.0?type=module", + "dependsOn": [ + "pkg:golang/golang.org/x/sync@v0.11.0?type=module" + ] + } + ] +}