faganronan via lists.openembedded.org schrieb am Do 23. Mai, 12:58 (GMT): > I'm brand new to the group, apologies if this is not the right spot. I've > been working a lot with SBOMs for embedded devices lately and been playing > around with the recipes for spdx and cve with limited success. The SPDX on > even a minimal image creates 500ish SPDX files and then it has to be with > the CPE data. I've had some good success with CycloneDX. Anyone else > running in to this?
Yes, I'm using CycloneDX with DependencyTrack, and wrote a converter of spdx to CycloneDX. The script also handles the upload to DependencyTrack, which is not that easy, because you have to wait for the end of the processing, before going on. A better approach would we a cyclonedx class, and I plan to create one, but didn't found the time to do. Here is the current version of the script: ``` #!/bin/sh # There will never again be support for SPDX by DependencyTrack # https://github.com/DependencyTrack/dependency-track/issues/1053 set -e -u -C no_upload=false recipe_filter=\* skip_sbom=false skip_vex=false while test $# -gt 0 do case "$1" in --no-upload) no_upload=true;; --skip-sbom) skip_sbom=true;; --skip-vex) skip_vex=true;; --recipe-filter) shift recipe_filter=${1:?Argument for --recipe-filter missing} ;; *) break;; esac shift done if ! $no_upload && test -z "${DT_API_KEY:-}" then echo 'Environment variable DT_API_KEY not set' >&2 exit 1 fi if test $# -ne 4 then echo "Usage: $0 [OPTIONS] HOST_URL PROJECT_NAME SPDX_TAR_ZST PROJECT_VERSION" >&2 echo >&2 echo 'OPTIONS:' >&2 echo ' --no-upload do not upload, but print to stdout' >&2 echo ' --recipe-filter FILTER only process recipes matched by wildcard FILTER' >&2 echo ' --skip-sbom do not generate the SBOM' >&2 echo ' --skip-vex do not generate the VEX' >&2 exit 1 fi dt_host_url=$1 dt_project=$2 spdx_tar=$3 dt_version=$4 api_req() ( path=${1:?API path} shift if out=$(curl --no-progress-meter --fail-with-body -H "X-Api-Key: $DT_API_KEY" \ "$@" "$dt_host_url/api/v1$path") then printf '%s' "$out" else rc=$? echo "curl $dt_host_url/api/v1$path failed with exit code $rc: $out" >&2 return $rc fi ) api_req_prj() { api_req "$@" -F "projectName=$dt_project" -F "projectVersion=$dt_version" } spdx_to_cydx_sbom() { # https://github.com/CycloneDX/specification/blob/1.5/schema/bom-1.5.schema.json # https://docs.dependencytrack.org/usage/cicd/ tar -x -f "$spdx_tar" --to-stdout --wildcards recipe-"$recipe_filter".spdx.json | \ jq --slurp 'map(.packages) |flatten |map(select(.name |startswith("packagegroup-") |not)) | { bomFormat: "CycloneDX", specVersion: "1.5", serialNumber: "urn:uuid:'"$(uuidgen)"'", version: 1, metadata: { timestamp: "'"$(date --iso-8601=seconds)"'", component: { name: "'"$dt_project"'", version: "'"$dt_version"'", type: "operating-system", }, }, components: map({ type: "application", name, "bom-ref": .name, supplier: { name: .supplier }, version: .versionInfo, description, licenses: [{ expression: .licenseDeclared, }], externalReferences: ([ { type: "vcs", url: .downloadLocation }, { type: "website", url: .homepage } ] |map(select(.url != null and .url != "NOASSERTION"))), } + ( .externalRefs[0].referenceLocator | if . != null then { cpe: . } else { } end )), }' } spdx_to_cydx_vex() { # https://github.com/CycloneDX/bom-examples/blob/master/VEX/vex.json tar -x -f "$spdx_tar" --to-stdout --wildcards recipe-"$recipe_filter".spdx.json | \ jq --slurp 'map(.packages) |flatten |map(select(.name |startswith("packagegroup-") |not)) |map(select(.sourceInfo != null)) | { bomFormat: "CycloneDX", specVersion: "1.5", serialNumber: "urn:uuid:'"$(uuidgen)"'", version: 1, metadata: { timestamp: "'"$(date --iso-8601=seconds)"'", component: { name: "'"$dt_project"'", version: "'"$dt_version"'", type: "operating-system", "bom-ref": "project", }, }, vulnerabilities: map(. as { name: $name, versionInfo: $version } | .sourceInfo |match("CVE-\\S+"; "g") | .string | { id: ., source : { name: "NVD", url: "https://nvd.nist.gov/" }, affects: [{ ref: "project", }], analysis: { state: "resolved", detail: ("Fixed by patch applied by Yocto recipe " + $name + " " + $version), }, }), }' } wait4process() ( kind=$1 token=$2 msg="Waiting for $kind getting processed..." while true do out=$(api_req "/bom/token/$token") case "$out" in '{"processing":false}') break ;; '{"processing":true}') printf '%s' "$msg" msg=. sleep 2 ;; *) echo "Unexpected return from /bom/token check: $out" >&2 exit 1 ;; esac done echo 'done.' ) if ! $skip_sbom then if $no_upload then spdx_to_cydx_sbom else out=$(spdx_to_cydx_sbom | api_req_prj '/bom' -F 'autoCreate=true' -F 'bom=@-') echo "SBOM upload response: $out" bom_token=$(echo "$out" | jq --raw-output .token) fi fi if ! $skip_vex then if ! $no_upload && test -n "${bom_token:-}" then wait4process SBOM "$bom_token" fi if $no_upload then spdx_to_cydx_vex else out=$(spdx_to_cydx_vex | api_req_prj '/vex' -F 'vex=@-') echo "VEX upload response: $out" wait4process VEX "$(echo "$out" |jq --raw-output .token)" out=$(api_req "/project/lookup?name=$dt_project&version=$dt_version") prj_uuid=$(echo "$out" |jq --raw-output .uuid) echo "Triggering refresh of metrics of project $prj_uuid after VEX upload" api_req "/metrics/project/$prj_uuid/refresh" echo "Visit $dt_host_url/projects/$prj_uuid for results" fi # TODO compare with https://autobuilder.yocto.io/pub/non-release/patchmetrics/cve-status-kirkstone.txt fi ``` Regards, Jörg --
signature.asc
Description: PGP signature
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#200053): https://lists.openembedded.org/g/openembedded-core/message/200053 Mute This Topic: https://lists.openembedded.org/mt/106269642/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
