diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 2a7778d026..4a7ab2d10b 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -643,6 +643,14 @@ tasks: binary: bash args: [*task-runner, govulncheck] + - name: generate-sbom + tags: ["ssdlc"] + 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 27f3bd3147..62a1a625c7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -71,3 +71,10 @@ repos: language: system types: [go] entry: etc/check_license.sh + + - id: sbom-currency + name: sbom-currency + language: system + types: [json] + require_serial: true + entry: etc/generate-sbom.sh -c diff --git a/Taskfile.yml b/Taskfile.yml index a4c6f405bf..9f0074424d 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,7 +11,7 @@ tasks: ### Utility tasks. ### default: - deps: [build, check-license, check-fmt, check-modules, lint, test-short] + 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.cdx.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..149c64e914 100644 --- a/sbom.json +++ b/sbom.json @@ -1,11 +1,378 @@ { - "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:03582aab-e289-4eb8-9a85-eebdc4b8bf8a", + "version": 1, + "metadata": { + "timestamp": "2025-08-04T16:09:43-04:00", + "tools": [ + { + "vendor": "CycloneDX", + "name": "cyclonedx-gomod", + "version": "v1.9.0", + "hashes": [ + { + "alg": "MD5", + "content": "56b744e437c1ba4bb7f443b5846a21d2" + }, + { + "alg": "SHA-1", + "content": "10ec58662be68ba3dfba7b2488d6585632f0a5c0" + }, + { + "alg": "SHA-256", + "content": "0102cf3082192b20c8e8c99c12ad54808eb0dbeb6627ea8ea5e2df117732f134" + }, + { + "alg": "SHA-384", + "content": "1e2c8756c2d4facfd910c938f68b5635626413a56b2aeefe0c6b8e79182ec0974242c3a38fcee85ccb3e227aa4adfbce" + }, + { + "alg": "SHA-512", + "content": "afffbed84e1bbc5320974be54df7bf58165ef40c4ba6d87f02494bdf2b6f19a1e3ccdc873dcd993c00eab29920ee94d8c6ee5f3fb09d4915b33e276260f8661a" + } + ], + "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.2.3-0.20250605220432-a0f897bda6ce?type=module", + "type": "library", + "name": "go.mongodb.org/mongo-driver/v2", + "version": "v2.2.3-0.20250605220432-a0f897bda6ce", + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/go.mongodb.org/mongo-driver/v2@v2.2.3-0.20250605220432-a0f897bda6ce" + } + }, + "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.16.7?type=module", + "type": "library", + "name": "github.com/klauspost/compress", + "version": "v1.16.7", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "da693730f18dccacb112b030f186a885887af7ea5ae2c210482d1f3c608547d2" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/github.com/klauspost/compress@v1.16.7", + "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.1.2?type=module", + "type": "library", + "name": "github.com/xdg-go/scram", + "version": "v1.1.2", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "1475f92390788b884a455441085471ab589045e8fb58ede1841b897fe514c926" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:golang/github.com/xdg-go/scram@v1.1.2", + "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.2.3-0.20250605220432-a0f897bda6ce?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.16.7?type=module", + "pkg:golang/github.com/xdg-go/scram@v1.1.2?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.16.7?type=module" + }, + { + "ref": "pkg:golang/github.com/xdg-go/pbkdf2@v1.0.0?type=module" + }, + { + "ref": "pkg:golang/github.com/xdg-go/scram@v1.1.2?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" + ] + }, + { + "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" + ] + } + ] +}