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


-- 

Attachment: 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]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to