mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 23:26:39 -08:00
feat(report): add support for SPDX (#2059)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
@@ -198,6 +198,7 @@ Failures: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)
|
|||||||
- container image, local filesystem and remote git repository
|
- container image, local filesystem and remote git repository
|
||||||
- Supply chain security (SBOM support)
|
- Supply chain security (SBOM support)
|
||||||
- Support CycloneDX
|
- Support CycloneDX
|
||||||
|
- Support SPDX
|
||||||
|
|
||||||
# Integrations
|
# Integrations
|
||||||
- [GitHub Actions][action]
|
- [GitHub Actions][action]
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ See [Integrations][integrations] for details.
|
|||||||
- remote git repository
|
- remote git repository
|
||||||
- [SBOM][sbom] (Software Bill of Materials) support
|
- [SBOM][sbom] (Software Bill of Materials) support
|
||||||
- CycloneDX
|
- CycloneDX
|
||||||
|
- SPDX
|
||||||
|
|
||||||
Please see [LICENSE][license] for Trivy licensing information.
|
Please see [LICENSE][license] for Trivy licensing information.
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,20 @@ NAME:
|
|||||||
USAGE:
|
USAGE:
|
||||||
trivy sbom [command options] ARTIFACT
|
trivy sbom [command options] ARTIFACT
|
||||||
|
|
||||||
|
DESCRIPTION:
|
||||||
|
ARTIFACT can be a container image, file path/directory, git repository or container image archive. See examples.
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
--output value, -o value output file name [$TRIVY_OUTPUT]
|
--output value, -o value output file name [$TRIVY_OUTPUT]
|
||||||
--clear-cache, -c clear image caches without scanning (default: false) [$TRIVY_CLEAR_CACHE]
|
--clear-cache, -c clear image caches without scanning (default: false) [$TRIVY_CLEAR_CACHE]
|
||||||
--ignorefile value specify .trivyignore file (default: ".trivyignore") [$TRIVY_IGNOREFILE]
|
--ignorefile value specify .trivyignore file (default: ".trivyignore") [$TRIVY_IGNOREFILE]
|
||||||
--timeout value timeout (default: 5m0s) [$TRIVY_TIMEOUT]
|
--timeout value timeout (default: 5m0s) [$TRIVY_TIMEOUT]
|
||||||
--severity value, -s value severities of vulnerabilities to be displayed (comma separated) (default: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") [$TRIVY_SEVERITY]
|
--severity value, -s value severities of vulnerabilities to be displayed (comma separated) (default: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") [$TRIVY_SEVERITY]
|
||||||
|
--offline-scan do not issue API requests to identify dependencies (default: false) [$TRIVY_OFFLINE_SCAN]
|
||||||
|
--db-repository value OCI repository to retrieve trivy-db from (default: "ghcr.io/aquasecurity/trivy-db") [$TRIVY_DB_REPOSITORY]
|
||||||
|
--skip-files value specify the file paths to skip traversal (accepts multiple inputs) [$TRIVY_SKIP_FILES]
|
||||||
|
--skip-dirs value specify the directories where the traversal is skipped (accepts multiple inputs) [$TRIVY_SKIP_DIRS]
|
||||||
--artifact-type value, --type value input artifact type (image, fs, repo, archive) (default: "image") [$TRIVY_ARTIFACT_TYPE]
|
--artifact-type value, --type value input artifact type (image, fs, repo, archive) (default: "image") [$TRIVY_ARTIFACT_TYPE]
|
||||||
--sbom-format value, --format value SBOM format (cyclonedx) (default: "cyclonedx") [$TRIVY_SBOM_FORMAT]
|
--sbom-format value, --format value SBOM format (cyclonedx, spdx, spdx-json) (default: "cyclonedx") [$TRIVY_SBOM_FORMAT]
|
||||||
--help, -h show help (default: false)
|
--help, -h show help (default: false)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
# SBOM
|
# SBOM
|
||||||
|
|
||||||
Trivy currently supports the following SBOM formats.
|
Trivy currently supports the following SBOM formats.
|
||||||
|
|
||||||
- [CycloneDX][cyclonedx]
|
- [CycloneDX][cyclonedx]
|
||||||
|
- [SPDX][spdx]
|
||||||
|
|
||||||
To generate SBOM, you can use the `--format` option for each subcommand such as `image` and `fs`.
|
To generate SBOM, you can use the `--format` option for each subcommand such as `image` and `fs`.
|
||||||
|
|
||||||
@@ -188,4 +190,5 @@ $ trivy sbom --artifact-type repo github.com/aquasecurity/trivy-ci-test
|
|||||||
$ trivy sbom --artifact-type archive alpine.tar
|
$ trivy sbom --artifact-type archive alpine.tar
|
||||||
```
|
```
|
||||||
|
|
||||||
[cyclonedx]: cyclonedx.md
|
[cyclonedx]: cyclonedx.md
|
||||||
|
[spdx]: spdx.md
|
||||||
|
|||||||
297
docs/docs/sbom/spdx.md
Normal file
297
docs/docs/sbom/spdx.md
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
# SPDX
|
||||||
|
|
||||||
|
Trivy generates reports in the [SPDX][spdx] format.
|
||||||
|
|
||||||
|
You can use the regular subcommands (like `image`, `fs` and `rootfs`) and specify `spdx` with the `--format` option.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ trivy image --format spdx --output result.spdx alpine:3.15
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Result</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cat result.spdx
|
||||||
|
SPDXVersion: SPDX-2.2
|
||||||
|
DataLicense: CC0-1.0
|
||||||
|
SPDXID: SPDXRef-DOCUMENT
|
||||||
|
DocumentName: alpine:3.15
|
||||||
|
DocumentNamespace: http://aquasecurity.github.io/trivy/container_image/alpine:3.15-bebf6b19-a94c-4e2c-af44-065f63923f48
|
||||||
|
Creator: Organization: aquasecurity
|
||||||
|
Creator: Tool: trivy
|
||||||
|
Created: 2022-04-28T07:32:57.142806Z
|
||||||
|
|
||||||
|
##### Package: zlib
|
||||||
|
|
||||||
|
PackageName: zlib
|
||||||
|
SPDXID: SPDXRef-12bc938ac028a5e1
|
||||||
|
PackageVersion: 1.2.12-r0
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: Zlib
|
||||||
|
PackageLicenseDeclared: Zlib
|
||||||
|
|
||||||
|
##### Package: apk-tools
|
||||||
|
|
||||||
|
PackageName: apk-tools
|
||||||
|
SPDXID: SPDXRef-26c274652190d87f
|
||||||
|
PackageVersion: 2.12.7-r3
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: GPL-2.0-only
|
||||||
|
PackageLicenseDeclared: GPL-2.0-only
|
||||||
|
|
||||||
|
##### Package: libretls
|
||||||
|
|
||||||
|
PackageName: libretls
|
||||||
|
SPDXID: SPDXRef-2b021966d19a8211
|
||||||
|
PackageVersion: 3.3.4-r3
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: ISC AND (BSD-3-Clause OR MIT)
|
||||||
|
PackageLicenseDeclared: ISC AND (BSD-3-Clause OR MIT)
|
||||||
|
|
||||||
|
##### Package: busybox
|
||||||
|
|
||||||
|
PackageName: busybox
|
||||||
|
SPDXID: SPDXRef-317ce3476703f20d
|
||||||
|
PackageVersion: 1.34.1-r5
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: GPL-2.0-only
|
||||||
|
PackageLicenseDeclared: GPL-2.0-only
|
||||||
|
|
||||||
|
##### Package: libcrypto1.1
|
||||||
|
|
||||||
|
PackageName: libcrypto1.1
|
||||||
|
SPDXID: SPDXRef-34f407fb4dbd67f4
|
||||||
|
PackageVersion: 1.1.1n-r0
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: OpenSSL
|
||||||
|
PackageLicenseDeclared: OpenSSL
|
||||||
|
|
||||||
|
##### Package: libc-utils
|
||||||
|
|
||||||
|
PackageName: libc-utils
|
||||||
|
SPDXID: SPDXRef-4bbc1cb449d54083
|
||||||
|
PackageVersion: 0.7.2-r3
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: BSD-2-Clause AND BSD-3-Clause
|
||||||
|
PackageLicenseDeclared: BSD-2-Clause AND BSD-3-Clause
|
||||||
|
|
||||||
|
##### Package: alpine-keys
|
||||||
|
|
||||||
|
PackageName: alpine-keys
|
||||||
|
SPDXID: SPDXRef-a3bdd174be1456b6
|
||||||
|
PackageVersion: 2.4-r1
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: MIT
|
||||||
|
PackageLicenseDeclared: MIT
|
||||||
|
|
||||||
|
##### Package: ca-certificates-bundle
|
||||||
|
|
||||||
|
PackageName: ca-certificates-bundle
|
||||||
|
SPDXID: SPDXRef-ac6472ba26fb991c
|
||||||
|
PackageVersion: 20211220-r0
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: MPL-2.0 AND MIT
|
||||||
|
PackageLicenseDeclared: MPL-2.0 AND MIT
|
||||||
|
|
||||||
|
##### Package: libssl1.1
|
||||||
|
|
||||||
|
PackageName: libssl1.1
|
||||||
|
SPDXID: SPDXRef-b2d1b1d70fe90f7d
|
||||||
|
PackageVersion: 1.1.1n-r0
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: OpenSSL
|
||||||
|
PackageLicenseDeclared: OpenSSL
|
||||||
|
|
||||||
|
##### Package: scanelf
|
||||||
|
|
||||||
|
PackageName: scanelf
|
||||||
|
SPDXID: SPDXRef-c617077ba6649520
|
||||||
|
PackageVersion: 1.3.3-r0
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: GPL-2.0-only
|
||||||
|
PackageLicenseDeclared: GPL-2.0-only
|
||||||
|
|
||||||
|
##### Package: musl
|
||||||
|
|
||||||
|
PackageName: musl
|
||||||
|
SPDXID: SPDXRef-ca80b810029cde0e
|
||||||
|
PackageVersion: 1.2.2-r7
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: MIT
|
||||||
|
PackageLicenseDeclared: MIT
|
||||||
|
|
||||||
|
##### Package: alpine-baselayout
|
||||||
|
|
||||||
|
PackageName: alpine-baselayout
|
||||||
|
SPDXID: SPDXRef-d782e64751ba9faa
|
||||||
|
PackageVersion: 3.2.0-r18
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: GPL-2.0-only
|
||||||
|
PackageLicenseDeclared: GPL-2.0-only
|
||||||
|
|
||||||
|
##### Package: musl-utils
|
||||||
|
|
||||||
|
PackageName: musl-utils
|
||||||
|
SPDXID: SPDXRef-e5e8a237f6162e22
|
||||||
|
PackageVersion: 1.2.2-r7
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: MIT BSD GPL2+
|
||||||
|
PackageLicenseDeclared: MIT BSD GPL2+
|
||||||
|
|
||||||
|
##### Package: ssl_client
|
||||||
|
|
||||||
|
PackageName: ssl_client
|
||||||
|
SPDXID: SPDXRef-fdf0ce84f6337be4
|
||||||
|
PackageVersion: 1.34.1-r5
|
||||||
|
FilesAnalyzed: false
|
||||||
|
PackageLicenseConcluded: GPL-2.0-only
|
||||||
|
PackageLicenseDeclared: GPL-2.0-only
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
SPDX-JSON format is also supported by using `spdx-json` with the `--format` option.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ trivy image --format spdx-json --output result.spdx.json alpine:3.15
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Result</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cat result.spdx.json | jq .
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2022-04-28T08:16:55.328255Z",
|
||||||
|
"creators": [
|
||||||
|
"Tool: trivy",
|
||||||
|
"Organization: aquasecurity"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"documentNamespace": "http://aquasecurity.github.io/trivy/container_image/alpine:3.15-d9549e3a-a4c5-4ee3-8bde-8c78d451fbe7",
|
||||||
|
"name": "alpine:3.15",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-12bc938ac028a5e1",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "Zlib",
|
||||||
|
"licenseDeclared": "Zlib",
|
||||||
|
"name": "zlib",
|
||||||
|
"versionInfo": "1.2.12-r0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-26c274652190d87f",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "GPL-2.0-only",
|
||||||
|
"licenseDeclared": "GPL-2.0-only",
|
||||||
|
"name": "apk-tools",
|
||||||
|
"versionInfo": "2.12.7-r3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-2b021966d19a8211",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "ISC AND (BSD-3-Clause OR MIT)",
|
||||||
|
"licenseDeclared": "ISC AND (BSD-3-Clause OR MIT)",
|
||||||
|
"name": "libretls",
|
||||||
|
"versionInfo": "3.3.4-r3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-317ce3476703f20d",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "GPL-2.0-only",
|
||||||
|
"licenseDeclared": "GPL-2.0-only",
|
||||||
|
"name": "busybox",
|
||||||
|
"versionInfo": "1.34.1-r5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-34f407fb4dbd67f4",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "OpenSSL",
|
||||||
|
"licenseDeclared": "OpenSSL",
|
||||||
|
"name": "libcrypto1.1",
|
||||||
|
"versionInfo": "1.1.1n-r0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-4bbc1cb449d54083",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "BSD-2-Clause AND BSD-3-Clause",
|
||||||
|
"licenseDeclared": "BSD-2-Clause AND BSD-3-Clause",
|
||||||
|
"name": "libc-utils",
|
||||||
|
"versionInfo": "0.7.2-r3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-a3bdd174be1456b6",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "MIT",
|
||||||
|
"licenseDeclared": "MIT",
|
||||||
|
"name": "alpine-keys",
|
||||||
|
"versionInfo": "2.4-r1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-ac6472ba26fb991c",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "MPL-2.0 AND MIT",
|
||||||
|
"licenseDeclared": "MPL-2.0 AND MIT",
|
||||||
|
"name": "ca-certificates-bundle",
|
||||||
|
"versionInfo": "20211220-r0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-b2d1b1d70fe90f7d",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "OpenSSL",
|
||||||
|
"licenseDeclared": "OpenSSL",
|
||||||
|
"name": "libssl1.1",
|
||||||
|
"versionInfo": "1.1.1n-r0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-c617077ba6649520",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "GPL-2.0-only",
|
||||||
|
"licenseDeclared": "GPL-2.0-only",
|
||||||
|
"name": "scanelf",
|
||||||
|
"versionInfo": "1.3.3-r0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-ca80b810029cde0e",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "MIT",
|
||||||
|
"licenseDeclared": "MIT",
|
||||||
|
"name": "musl",
|
||||||
|
"versionInfo": "1.2.2-r7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-d782e64751ba9faa",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "GPL-2.0-only",
|
||||||
|
"licenseDeclared": "GPL-2.0-only",
|
||||||
|
"name": "alpine-baselayout",
|
||||||
|
"versionInfo": "3.2.0-r18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-e5e8a237f6162e22",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "MIT BSD GPL2+",
|
||||||
|
"licenseDeclared": "MIT BSD GPL2+",
|
||||||
|
"name": "musl-utils",
|
||||||
|
"versionInfo": "1.2.2-r7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-fdf0ce84f6337be4",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "GPL-2.0-only",
|
||||||
|
"licenseDeclared": "GPL-2.0-only",
|
||||||
|
"name": "ssl_client",
|
||||||
|
"versionInfo": "1.34.1-r5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spdxVersion": "SPDX-2.2"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[spdx]: https://spdx.dev/wp-content/uploads/sites/41/2020/08/SPDX-specification-2-2.pdf
|
||||||
3
go.mod
3
go.mod
@@ -155,6 +155,7 @@ require (
|
|||||||
github.com/sergi/go-diff v1.1.0 // indirect
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
|
github.com/spdx/tools-golang v0.3.0
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/stretchr/objx v0.3.0 // indirect
|
github.com/stretchr/objx v0.3.0 // indirect
|
||||||
github.com/tmccombs/hcl2json v0.3.4 // indirect
|
github.com/tmccombs/hcl2json v0.3.4 // indirect
|
||||||
@@ -198,5 +199,7 @@ require (
|
|||||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
|
|
||||||
// To resolve CVE-2022-23648
|
// To resolve CVE-2022-23648
|
||||||
replace github.com/containerd/containerd v1.5.9 => github.com/containerd/containerd v1.5.10
|
replace github.com/containerd/containerd v1.5.9 => github.com/containerd/containerd v1.5.10
|
||||||
|
|||||||
5
go.sum
5
go.sum
@@ -1183,6 +1183,8 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
|
|||||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||||
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
|
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
@@ -1468,6 +1470,9 @@ github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34c
|
|||||||
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
|
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
|
||||||
|
github.com/spdx/tools-golang v0.3.0 h1:rtm+DHk3aAt74Fh0Wgucb4pCxjXV8SqHCPEb2iBd30k=
|
||||||
|
github.com/spdx/tools-golang v0.3.0/go.mod h1:RO4Y3IFROJnz+43JKm1YOrbtgQNljW4gAPpA/sY2eqo=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
|
|||||||
231
mkdocs.yml
231
mkdocs.yml
@@ -7,125 +7,126 @@ repo_url: https://github.com/aquasecurity/trivy
|
|||||||
edit_uri: ""
|
edit_uri: ""
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- HOME: index.md
|
- HOME: index.md
|
||||||
- Getting started:
|
- Getting started:
|
||||||
- Overview: getting-started/overview.md
|
- Overview: getting-started/overview.md
|
||||||
- Installation: getting-started/installation.md
|
- Installation: getting-started/installation.md
|
||||||
- Quick Start: getting-started/quickstart.md
|
- Quick Start: getting-started/quickstart.md
|
||||||
- Further Reading: getting-started/further.md
|
- Further Reading: getting-started/further.md
|
||||||
- Docs:
|
- Docs:
|
||||||
- Overview: docs/index.md
|
- Overview: docs/index.md
|
||||||
- Vulnerability:
|
- Vulnerability:
|
||||||
- Scanning:
|
- Scanning:
|
||||||
- Overview: docs/vulnerability/scanning/index.md
|
- Overview: docs/vulnerability/scanning/index.md
|
||||||
- Container Image: docs/vulnerability/scanning/image.md
|
- Container Image: docs/vulnerability/scanning/image.md
|
||||||
- Filesystem: docs/vulnerability/scanning/filesystem.md
|
- Filesystem: docs/vulnerability/scanning/filesystem.md
|
||||||
- Rootfs: docs/vulnerability/scanning/rootfs.md
|
- Rootfs: docs/vulnerability/scanning/rootfs.md
|
||||||
- Git Repository: docs/vulnerability/scanning/git-repository.md
|
- Git Repository: docs/vulnerability/scanning/git-repository.md
|
||||||
- Detection:
|
- Detection:
|
||||||
- OS Packages: docs/vulnerability/detection/os.md
|
- OS Packages: docs/vulnerability/detection/os.md
|
||||||
- Language-specific Packages: docs/vulnerability/detection/language.md
|
- Language-specific Packages: docs/vulnerability/detection/language.md
|
||||||
- Data Sources: docs/vulnerability/detection/data-source.md
|
- Data Sources: docs/vulnerability/detection/data-source.md
|
||||||
- Supported: docs/vulnerability/detection/supported.md
|
- Supported: docs/vulnerability/detection/supported.md
|
||||||
- Examples:
|
- Examples:
|
||||||
- Vulnerability Filtering: docs/vulnerability/examples/filter.md
|
- Vulnerability Filtering: docs/vulnerability/examples/filter.md
|
||||||
- Report Formats: docs/vulnerability/examples/report.md
|
- Report Formats: docs/vulnerability/examples/report.md
|
||||||
- Vulnerability DB: docs/vulnerability/examples/db.md
|
- Vulnerability DB: docs/vulnerability/examples/db.md
|
||||||
- Cache: docs/vulnerability/examples/cache.md
|
- Cache: docs/vulnerability/examples/cache.md
|
||||||
- Others: docs/vulnerability/examples/others.md
|
- Others: docs/vulnerability/examples/others.md
|
||||||
- Distributions: docs/vulnerability/distributions.md
|
- Distributions: docs/vulnerability/distributions.md
|
||||||
- Languages:
|
- Languages:
|
||||||
- Go: docs/vulnerability/languages/golang.md
|
- Go: docs/vulnerability/languages/golang.md
|
||||||
- Misconfiguration:
|
- Misconfiguration:
|
||||||
- Scanning:
|
- Scanning:
|
||||||
- Overview: docs/misconfiguration/index.md
|
- Overview: docs/misconfiguration/index.md
|
||||||
- Infrastructure as Code: docs/misconfiguration/iac.md
|
- Infrastructure as Code: docs/misconfiguration/iac.md
|
||||||
- Filesystem: docs/misconfiguration/filesystem.md
|
- Filesystem: docs/misconfiguration/filesystem.md
|
||||||
- Policy:
|
- Policy:
|
||||||
- Built-in Policies: docs/misconfiguration/policy/builtin.md
|
- Built-in Policies: docs/misconfiguration/policy/builtin.md
|
||||||
- Exceptions: docs/misconfiguration/policy/exceptions.md
|
- Exceptions: docs/misconfiguration/policy/exceptions.md
|
||||||
- Custom Policies:
|
- Custom Policies:
|
||||||
- Overview: docs/misconfiguration/custom/index.md
|
- Overview: docs/misconfiguration/custom/index.md
|
||||||
- Data: docs/misconfiguration/custom/data.md
|
- Data: docs/misconfiguration/custom/data.md
|
||||||
- Combine: docs/misconfiguration/custom/combine.md
|
- Combine: docs/misconfiguration/custom/combine.md
|
||||||
- Testing: docs/misconfiguration/custom/testing.md
|
- Testing: docs/misconfiguration/custom/testing.md
|
||||||
- Debugging Policies: docs/misconfiguration/custom/debug.md
|
- Debugging Policies: docs/misconfiguration/custom/debug.md
|
||||||
- Examples: docs/misconfiguration/custom/examples.md
|
- Examples: docs/misconfiguration/custom/examples.md
|
||||||
- Options:
|
- Options:
|
||||||
- Policy: docs/misconfiguration/options/policy.md
|
- Policy: docs/misconfiguration/options/policy.md
|
||||||
- Filtering: docs/misconfiguration/options/filter.md
|
- Filtering: docs/misconfiguration/options/filter.md
|
||||||
- Report Formats: docs/misconfiguration/options/report.md
|
- Report Formats: docs/misconfiguration/options/report.md
|
||||||
- Others: docs/misconfiguration/options/others.md
|
- Others: docs/misconfiguration/options/others.md
|
||||||
- Comparison:
|
- Comparison:
|
||||||
- vs Conftest: docs/misconfiguration/comparison/conftest.md
|
- vs Conftest: docs/misconfiguration/comparison/conftest.md
|
||||||
- vs tfsec: docs/misconfiguration/comparison/tfsec.md
|
- vs tfsec: docs/misconfiguration/comparison/tfsec.md
|
||||||
- vs cfsec: docs/misconfiguration/comparison/cfsec.md
|
- vs cfsec: docs/misconfiguration/comparison/cfsec.md
|
||||||
- Secret:
|
- Secret:
|
||||||
- Scanning: docs/secret/scanning.md
|
- Scanning: docs/secret/scanning.md
|
||||||
- Configuration: docs/secret/configuration.md
|
- Configuration: docs/secret/configuration.md
|
||||||
- Examples: docs/secret/examples.md
|
- Examples: docs/secret/examples.md
|
||||||
- SBOM:
|
- SBOM:
|
||||||
- Overview: docs/sbom/index.md
|
- Overview: docs/sbom/index.md
|
||||||
- CycloneDX: docs/sbom/cyclonedx.md
|
- CycloneDX: docs/sbom/cyclonedx.md
|
||||||
- Integrations:
|
- SPDX: docs/sbom/spdx.md
|
||||||
- Overview: docs/integrations/index.md
|
- Integrations:
|
||||||
- GitHub Actions: docs/integrations/github-actions.md
|
- Overview: docs/integrations/index.md
|
||||||
- CircleCI: docs/integrations/circleci.md
|
- GitHub Actions: docs/integrations/github-actions.md
|
||||||
- Travis CI: docs/integrations/travis-ci.md
|
- CircleCI: docs/integrations/circleci.md
|
||||||
- GitLab CI: docs/integrations/gitlab-ci.md
|
- Travis CI: docs/integrations/travis-ci.md
|
||||||
- Bitbucket Pipelines: docs/integrations/bitbucket.md
|
- GitLab CI: docs/integrations/gitlab-ci.md
|
||||||
- AWS CodePipeline: docs/integrations/aws-codepipeline.md
|
- Bitbucket Pipelines: docs/integrations/bitbucket.md
|
||||||
- AWS Security Hub: docs/integrations/aws-security-hub.md
|
- AWS CodePipeline: docs/integrations/aws-codepipeline.md
|
||||||
- Advanced:
|
- AWS Security Hub: docs/integrations/aws-security-hub.md
|
||||||
- Plugins: docs/advanced/plugins.md
|
- Advanced:
|
||||||
- Air-Gapped Environment: docs/advanced/air-gap.md
|
- Plugins: docs/advanced/plugins.md
|
||||||
- Container Image:
|
- Air-Gapped Environment: docs/advanced/air-gap.md
|
||||||
- Embed in Dockerfile: docs/advanced/container/embed-in-dockerfile.md
|
- Container Image:
|
||||||
- Unpacked container image filesystem: docs/advanced/container/unpacked-filesystem.md
|
- Embed in Dockerfile: docs/advanced/container/embed-in-dockerfile.md
|
||||||
- OCI Image: docs/advanced/container/oci.md
|
- Unpacked container image filesystem: docs/advanced/container/unpacked-filesystem.md
|
||||||
- Podman: docs/advanced/container/podman.md
|
- OCI Image: docs/advanced/container/oci.md
|
||||||
- Private Docker Registries:
|
- Podman: docs/advanced/container/podman.md
|
||||||
- Overview: docs/advanced/private-registries/index.md
|
- Private Docker Registries:
|
||||||
- Docker Hub: docs/advanced/private-registries/docker-hub.md
|
- Overview: docs/advanced/private-registries/index.md
|
||||||
- AWS ECR (Elastic Container Registry): docs/advanced/private-registries/ecr.md
|
- Docker Hub: docs/advanced/private-registries/docker-hub.md
|
||||||
- GCR (Google Container Registry): docs/advanced/private-registries/gcr.md
|
- AWS ECR (Elastic Container Registry): docs/advanced/private-registries/ecr.md
|
||||||
- ACR (Azure Container Registry): docs/advanced/private-registries/acr.md
|
- GCR (Google Container Registry): docs/advanced/private-registries/gcr.md
|
||||||
- Self-Hosted: docs/advanced/private-registries/self.md
|
- ACR (Azure Container Registry): docs/advanced/private-registries/acr.md
|
||||||
- References:
|
- Self-Hosted: docs/advanced/private-registries/self.md
|
||||||
- CLI:
|
- References:
|
||||||
- Overview: docs/references/cli/index.md
|
- CLI:
|
||||||
- Image: docs/references/cli/image.md
|
- Overview: docs/references/cli/index.md
|
||||||
- Config: docs/references/cli/config.md
|
- Image: docs/references/cli/image.md
|
||||||
- Filesystem: docs/references/cli/fs.md
|
- Config: docs/references/cli/config.md
|
||||||
- Rootfs: docs/references/cli/rootfs.md
|
- Filesystem: docs/references/cli/fs.md
|
||||||
- Repository: docs/references/cli/repo.md
|
- Rootfs: docs/references/cli/rootfs.md
|
||||||
- Client: docs/references/cli/client.md
|
- Repository: docs/references/cli/repo.md
|
||||||
- Server: docs/references/cli/server.md
|
- Client: docs/references/cli/client.md
|
||||||
- Plugins: docs/references/cli/plugins.md
|
- Server: docs/references/cli/server.md
|
||||||
- SBOM: docs/references/cli/sbom.md
|
- Plugins: docs/references/cli/plugins.md
|
||||||
- Modes:
|
- SBOM: docs/references/cli/sbom.md
|
||||||
- Standalone: docs/references/modes/standalone.md
|
- Modes:
|
||||||
- Client/Server: docs/references/modes/client-server.md
|
- Standalone: docs/references/modes/standalone.md
|
||||||
- Troubleshooting: docs/references/troubleshooting.md
|
- Client/Server: docs/references/modes/client-server.md
|
||||||
- Community:
|
- Troubleshooting: docs/references/troubleshooting.md
|
||||||
- Tools: community/tools.md
|
- Community:
|
||||||
- References: community/references.md
|
- Tools: community/tools.md
|
||||||
- CKS Reference: community/cks.md
|
- References: community/references.md
|
||||||
- Credits: community/credit.md
|
- CKS Reference: community/cks.md
|
||||||
- How to contribute:
|
- Credits: community/credit.md
|
||||||
- Issues: community/contribute/issue.md
|
- How to contribute:
|
||||||
- Pull Requests: community/contribute/pr.md
|
- Issues: community/contribute/issue.md
|
||||||
- Maintainer:
|
- Pull Requests: community/contribute/pr.md
|
||||||
- Help Wanted: community/maintainer/help-wanted.md
|
- Maintainer:
|
||||||
- Triage: community/maintainer/triage.md
|
- Help Wanted: community/maintainer/help-wanted.md
|
||||||
|
- Triage: community/maintainer/triage.md
|
||||||
theme:
|
theme:
|
||||||
name: material
|
name: material
|
||||||
language: 'en'
|
language: "en"
|
||||||
logo: imgs/logo-white.svg
|
logo: imgs/logo-white.svg
|
||||||
features:
|
features:
|
||||||
- navigation.tabs
|
- navigation.tabs
|
||||||
- navigation.tabs.sticky
|
- navigation.tabs.sticky
|
||||||
- navigation.sections
|
- navigation.sections
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- pymdownx.highlight
|
- pymdownx.highlight
|
||||||
@@ -148,4 +149,4 @@ extra:
|
|||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- search
|
- search
|
||||||
- macros
|
- macros
|
||||||
|
|||||||
@@ -816,7 +816,7 @@ func NewSbomCommand() *cli.Command {
|
|||||||
Name: "sbom-format",
|
Name: "sbom-format",
|
||||||
Aliases: []string{"format"},
|
Aliases: []string{"format"},
|
||||||
Value: "cyclonedx",
|
Value: "cyclonedx",
|
||||||
Usage: "SBOM format (cyclonedx)",
|
Usage: "SBOM format (cyclonedx, spdx, spdx-json)",
|
||||||
EnvVars: []string{"TRIVY_SBOM_FORMAT"},
|
EnvVars: []string{"TRIVY_SBOM_FORMAT"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/types"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReportOption holds the options for reporting scan results
|
// ReportOption holds the options for reporting scan results
|
||||||
@@ -137,8 +137,8 @@ func (c *ReportOption) populateSecurityChecks() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ReportOption) forceListAllPkgs(logger *zap.SugaredLogger) bool {
|
func (c *ReportOption) forceListAllPkgs(logger *zap.SugaredLogger) bool {
|
||||||
if c.Format == "cyclonedx" && !c.ListAllPkgs {
|
if slices.Contains(supportedSbomFormats, c.Format) && !c.ListAllPkgs {
|
||||||
logger.Debugf("'--format cyclonedx' automatically enables '--list-all-pkgs'.")
|
logger.Debugf("'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func TestReportReportConfig_Init(t *testing.T) {
|
|||||||
},
|
},
|
||||||
args: []string{"centos:7"},
|
args: []string{"centos:7"},
|
||||||
logs: []string{
|
logs: []string{
|
||||||
"'--format cyclonedx' automatically enables '--list-all-pkgs'.",
|
"'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.",
|
||||||
"Severities: CRITICAL",
|
"Severities: CRITICAL",
|
||||||
},
|
},
|
||||||
want: ReportOption{
|
want: ReportOption{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var supportedSbomFormats = []string{"cyclonedx"}
|
var supportedSbomFormats = []string{"cyclonedx", "spdx", "spdx-json"}
|
||||||
|
|
||||||
// SbomOption holds the options for SBOM generation
|
// SbomOption holds the options for SBOM generation
|
||||||
type SbomOption struct {
|
type SbomOption struct {
|
||||||
|
|||||||
170
pkg/report/spdx/spdx.go
Normal file
170
pkg/report/spdx/spdx.go
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
package spdx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/mitchellh/hashstructure/v2"
|
||||||
|
"github.com/spdx/tools-golang/jsonsaver"
|
||||||
|
"github.com/spdx/tools-golang/spdx"
|
||||||
|
"github.com/spdx/tools-golang/tvsaver"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
"k8s.io/utils/clock"
|
||||||
|
|
||||||
|
ftypes "github.com/aquasecurity/fanal/types"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SPDXVersion = "SPDX-2.2"
|
||||||
|
DataLicense = "CC0-1.0"
|
||||||
|
SPDXIdentifier = "DOCUMENT"
|
||||||
|
DocumentNamespace = "http://aquasecurity.github.io/trivy"
|
||||||
|
CreatorOrganization = "aquasecurity"
|
||||||
|
CreatorTool = "trivy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
output io.Writer
|
||||||
|
version string
|
||||||
|
*options
|
||||||
|
}
|
||||||
|
|
||||||
|
type newUUID func() uuid.UUID
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
format spdx.Document2_1
|
||||||
|
clock clock.Clock
|
||||||
|
newUUID newUUID
|
||||||
|
spdxFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
type option func(*options)
|
||||||
|
|
||||||
|
type spdxSaveFunction func(*spdx.Document2_2, io.Writer) error
|
||||||
|
|
||||||
|
func WithClock(clock clock.Clock) option {
|
||||||
|
return func(opts *options) {
|
||||||
|
opts.clock = clock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithNewUUID(newUUID newUUID) option {
|
||||||
|
return func(opts *options) {
|
||||||
|
opts.newUUID = newUUID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(output io.Writer, version string, spdxFormat string, opts ...option) Writer {
|
||||||
|
o := &options{
|
||||||
|
format: spdx.Document2_1{},
|
||||||
|
clock: clock.RealClock{},
|
||||||
|
newUUID: uuid.New,
|
||||||
|
spdxFormat: spdxFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Writer{
|
||||||
|
output: output,
|
||||||
|
version: version,
|
||||||
|
options: o,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw Writer) Write(report types.Report) error {
|
||||||
|
spdxDoc, err := cw.convertToBom(report, cw.version)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to convert bom: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var saveFunc spdxSaveFunction
|
||||||
|
if cw.spdxFormat != "spdx-json" {
|
||||||
|
saveFunc = tvsaver.Save2_2
|
||||||
|
} else {
|
||||||
|
saveFunc = jsonsaver.Save2_2
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = saveFunc(spdxDoc, cw.output); err != nil {
|
||||||
|
return xerrors.Errorf("failed to save bom: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *Writer) convertToBom(r types.Report, version string) (*spdx.Document2_2, error) {
|
||||||
|
packages := make(map[spdx.ElementID]*spdx.Package2_2)
|
||||||
|
|
||||||
|
for _, result := range r.Results {
|
||||||
|
for _, pkg := range result.Packages {
|
||||||
|
spdxPackage, err := pkgToSpdxPackage(result.Type, r.Metadata, pkg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to parse pkg: %w", err)
|
||||||
|
}
|
||||||
|
packages[spdxPackage.PackageSPDXIdentifier] = &spdxPackage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &spdx.Document2_2{
|
||||||
|
CreationInfo: &spdx.CreationInfo2_2{
|
||||||
|
SPDXVersion: SPDXVersion,
|
||||||
|
DataLicense: DataLicense,
|
||||||
|
SPDXIdentifier: SPDXIdentifier,
|
||||||
|
DocumentName: r.ArtifactName,
|
||||||
|
DocumentNamespace: getDocumentNamespace(r, cw),
|
||||||
|
CreatorOrganizations: []string{CreatorOrganization},
|
||||||
|
CreatorTools: []string{CreatorTool},
|
||||||
|
Created: cw.clock.Now().UTC().Format(time.RFC3339Nano),
|
||||||
|
},
|
||||||
|
Packages: packages,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgToSpdxPackage(t string, meta types.Metadata, pkg ftypes.Package) (spdx.Package2_2, error) {
|
||||||
|
var spdxPackage spdx.Package2_2
|
||||||
|
license := getLicense(pkg)
|
||||||
|
|
||||||
|
pkgID, err := getPackageID(pkg)
|
||||||
|
if err != nil {
|
||||||
|
return spdx.Package2_2{}, xerrors.Errorf("failed to get %s package ID: %w", pkg.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdxPackage.PackageSPDXIdentifier = spdx.ElementID(pkgID)
|
||||||
|
spdxPackage.PackageName = pkg.Name
|
||||||
|
spdxPackage.PackageVersion = pkg.Version
|
||||||
|
|
||||||
|
// The Declared License is what the authors of a project believe govern the package
|
||||||
|
spdxPackage.PackageLicenseConcluded = license
|
||||||
|
|
||||||
|
// The Concluded License field is the license the SPDX file creator believes governs the package
|
||||||
|
spdxPackage.PackageLicenseDeclared = license
|
||||||
|
|
||||||
|
return spdxPackage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLicense(p ftypes.Package) string {
|
||||||
|
if p.License == "" {
|
||||||
|
return "NONE"
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.License
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDocumentNamespace(r types.Report, cw *Writer) string {
|
||||||
|
return DocumentNamespace + "/" + string(r.ArtifactType) + "/" + r.ArtifactName + "-" + cw.newUUID().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPackageID(p ftypes.Package) (string, error) {
|
||||||
|
f, err := hashstructure.Hash(p, hashstructure.FormatV2, &hashstructure.HashOptions{
|
||||||
|
ZeroNil: true,
|
||||||
|
SlicesAsSets: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", xerrors.Errorf("could not build package ID for package=%+v: %+v", p, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", f), nil
|
||||||
|
}
|
||||||
395
pkg/report/spdx/spdx_test.go
Normal file
395
pkg/report/spdx/spdx_test.go
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
package spdx_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
fos "github.com/aquasecurity/fanal/analyzer/os"
|
||||||
|
ftypes "github.com/aquasecurity/fanal/types"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/report"
|
||||||
|
reportSpdx "github.com/aquasecurity/trivy/pkg/report/spdx"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/types"
|
||||||
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/spdx/tools-golang/jsonloader"
|
||||||
|
"github.com/spdx/tools-golang/spdx"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
fake "k8s.io/utils/clock/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWriter_Write(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
inputReport types.Report
|
||||||
|
wantSBOM *spdx.Document2_2
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "happy path for container scan",
|
||||||
|
inputReport: types.Report{
|
||||||
|
SchemaVersion: report.SchemaVersion,
|
||||||
|
ArtifactName: "rails:latest",
|
||||||
|
ArtifactType: ftypes.ArtifactContainerImage,
|
||||||
|
Metadata: types.Metadata{
|
||||||
|
Size: 1024,
|
||||||
|
OS: &ftypes.OS{
|
||||||
|
Family: fos.CentOS,
|
||||||
|
Name: "8.3.2011",
|
||||||
|
Eosl: true,
|
||||||
|
},
|
||||||
|
ImageID: "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6",
|
||||||
|
RepoTags: []string{"rails:latest"},
|
||||||
|
DiffIDs: []string{"sha256:d871dadfb37b53ef1ca45be04fc527562b91989991a8f545345ae3be0b93f92a"},
|
||||||
|
RepoDigests: []string{"rails@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177"},
|
||||||
|
ImageConfig: v1.ConfigFile{
|
||||||
|
Architecture: "arm64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Results: types.Results{
|
||||||
|
{
|
||||||
|
Target: "rails:latest (centos 8.3.2011)",
|
||||||
|
Class: types.ClassOSPkg,
|
||||||
|
Type: fos.CentOS,
|
||||||
|
Packages: []ftypes.Package{
|
||||||
|
{
|
||||||
|
Name: "binutils",
|
||||||
|
Version: "2.30",
|
||||||
|
Release: "93.el8",
|
||||||
|
Epoch: 0,
|
||||||
|
Arch: "aarch64",
|
||||||
|
SrcName: "binutils",
|
||||||
|
SrcVersion: "2.30",
|
||||||
|
SrcRelease: "93.el8",
|
||||||
|
SrcEpoch: 0,
|
||||||
|
Modularitylabel: "",
|
||||||
|
License: "GPLv3+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Target: "app/subproject/Gemfile.lock",
|
||||||
|
Class: types.ClassLangPkg,
|
||||||
|
Type: ftypes.Bundler,
|
||||||
|
Packages: []ftypes.Package{
|
||||||
|
{
|
||||||
|
Name: "actionpack",
|
||||||
|
Version: "7.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "actioncontroller",
|
||||||
|
Version: "7.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Target: "app/Gemfile.lock",
|
||||||
|
Class: types.ClassLangPkg,
|
||||||
|
Type: ftypes.Bundler,
|
||||||
|
Packages: []ftypes.Package{
|
||||||
|
{
|
||||||
|
Name: "actionpack",
|
||||||
|
Version: "7.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantSBOM: &spdx.Document2_2{
|
||||||
|
CreationInfo: &spdx.CreationInfo2_2{
|
||||||
|
SPDXVersion: "SPDX-2.2",
|
||||||
|
DataLicense: "CC0-1.0",
|
||||||
|
SPDXIdentifier: "DOCUMENT",
|
||||||
|
DocumentName: "rails:latest",
|
||||||
|
DocumentNamespace: "http://aquasecurity.github.io/trivy/container_image/rails:latest-3ff14136-e09f-4df9-80ea-000000000001",
|
||||||
|
CreatorOrganizations: []string{"aquasecurity"},
|
||||||
|
CreatorTools: []string{"trivy"},
|
||||||
|
Created: "2021-08-25T12:20:30.000000005Z",
|
||||||
|
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
|
||||||
|
},
|
||||||
|
Packages: map[spdx.ElementID]*spdx.Package2_2{
|
||||||
|
spdx.ElementID("e27d088813d330a4"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("e27d088813d330a4"),
|
||||||
|
PackageName: "actioncontroller",
|
||||||
|
PackageVersion: "7.0.0",
|
||||||
|
PackageLicenseConcluded: "NONE",
|
||||||
|
PackageLicenseDeclared: "NONE",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
spdx.ElementID("163ff5a6292fef8c"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("163ff5a6292fef8c"),
|
||||||
|
PackageName: "actionpack",
|
||||||
|
PackageVersion: "7.0.0",
|
||||||
|
PackageLicenseConcluded: "NONE",
|
||||||
|
PackageLicenseDeclared: "NONE",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
spdx.ElementID("a4aded544ebeda0a"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("a4aded544ebeda0a"),
|
||||||
|
PackageName: "binutils",
|
||||||
|
PackageVersion: "2.30",
|
||||||
|
PackageLicenseConcluded: "GPLv3+",
|
||||||
|
PackageLicenseDeclared: "GPLv3+",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UnpackagedFiles: nil,
|
||||||
|
OtherLicenses: nil,
|
||||||
|
Relationships: nil,
|
||||||
|
Annotations: nil,
|
||||||
|
Reviews: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy path for local container scan",
|
||||||
|
inputReport: types.Report{
|
||||||
|
SchemaVersion: report.SchemaVersion,
|
||||||
|
ArtifactName: "centos:latest",
|
||||||
|
ArtifactType: ftypes.ArtifactContainerImage,
|
||||||
|
Metadata: types.Metadata{
|
||||||
|
Size: 1024,
|
||||||
|
OS: &ftypes.OS{
|
||||||
|
Family: fos.CentOS,
|
||||||
|
Name: "8.3.2011",
|
||||||
|
Eosl: true,
|
||||||
|
},
|
||||||
|
ImageID: "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6",
|
||||||
|
RepoTags: []string{"centos:latest"},
|
||||||
|
RepoDigests: []string{},
|
||||||
|
ImageConfig: v1.ConfigFile{
|
||||||
|
Architecture: "arm64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Results: types.Results{
|
||||||
|
{
|
||||||
|
Target: "centos:latest (centos 8.3.2011)",
|
||||||
|
Class: types.ClassOSPkg,
|
||||||
|
Type: fos.CentOS,
|
||||||
|
Packages: []ftypes.Package{
|
||||||
|
{
|
||||||
|
Name: "acl",
|
||||||
|
Version: "2.2.53",
|
||||||
|
Release: "1.el8",
|
||||||
|
Epoch: 1,
|
||||||
|
Arch: "aarch64",
|
||||||
|
SrcName: "acl",
|
||||||
|
SrcVersion: "2.2.53",
|
||||||
|
SrcRelease: "1.el8",
|
||||||
|
SrcEpoch: 1,
|
||||||
|
Modularitylabel: "",
|
||||||
|
License: "GPLv2+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Target: "Ruby",
|
||||||
|
Class: types.ClassLangPkg,
|
||||||
|
Type: ftypes.GemSpec,
|
||||||
|
Packages: []ftypes.Package{
|
||||||
|
{
|
||||||
|
Name: "actionpack",
|
||||||
|
Version: "7.0.0",
|
||||||
|
Layer: ftypes.Layer{
|
||||||
|
DiffID: "sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488",
|
||||||
|
},
|
||||||
|
FilePath: "tools/project-john/specifications/actionpack.gemspec",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "actionpack",
|
||||||
|
Version: "7.0.1",
|
||||||
|
Layer: ftypes.Layer{
|
||||||
|
DiffID: "sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488",
|
||||||
|
},
|
||||||
|
FilePath: "tools/project-doe/specifications/actionpack.gemspec",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantSBOM: &spdx.Document2_2{
|
||||||
|
CreationInfo: &spdx.CreationInfo2_2{
|
||||||
|
SPDXVersion: "SPDX-2.2",
|
||||||
|
DataLicense: "CC0-1.0",
|
||||||
|
SPDXIdentifier: "DOCUMENT",
|
||||||
|
DocumentName: "centos:latest",
|
||||||
|
DocumentNamespace: "http://aquasecurity.github.io/trivy/container_image/centos:latest-3ff14136-e09f-4df9-80ea-000000000001",
|
||||||
|
CreatorOrganizations: []string{"aquasecurity"},
|
||||||
|
CreatorTools: []string{"trivy"},
|
||||||
|
Created: "2021-08-25T12:20:30.000000005Z",
|
||||||
|
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
|
||||||
|
},
|
||||||
|
Packages: map[spdx.ElementID]*spdx.Package2_2{
|
||||||
|
spdx.ElementID("d963712012fcbc8c"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("d963712012fcbc8c"),
|
||||||
|
PackageName: "acl",
|
||||||
|
PackageVersion: "2.2.53",
|
||||||
|
PackageLicenseConcluded: "GPLv2+",
|
||||||
|
PackageLicenseDeclared: "GPLv2+",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
spdx.ElementID("17ba95e087440896"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("17ba95e087440896"),
|
||||||
|
PackageName: "actionpack",
|
||||||
|
PackageVersion: "7.0.0",
|
||||||
|
PackageLicenseConcluded: "NONE",
|
||||||
|
PackageLicenseDeclared: "NONE",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
spdx.ElementID("50ac94ac1875540"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("50ac94ac1875540"),
|
||||||
|
PackageName: "actionpack",
|
||||||
|
PackageVersion: "7.0.1",
|
||||||
|
PackageLicenseConcluded: "NONE",
|
||||||
|
PackageLicenseDeclared: "NONE",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UnpackagedFiles: nil,
|
||||||
|
OtherLicenses: nil,
|
||||||
|
Relationships: nil,
|
||||||
|
Annotations: nil,
|
||||||
|
Reviews: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy path for fs scan",
|
||||||
|
inputReport: types.Report{
|
||||||
|
SchemaVersion: report.SchemaVersion,
|
||||||
|
ArtifactName: "masahiro331/CVE-2021-41098",
|
||||||
|
ArtifactType: ftypes.ArtifactFilesystem,
|
||||||
|
Results: types.Results{
|
||||||
|
{
|
||||||
|
Target: "Gemfile.lock",
|
||||||
|
Class: types.ClassLangPkg,
|
||||||
|
Type: ftypes.Bundler,
|
||||||
|
Packages: []ftypes.Package{
|
||||||
|
{
|
||||||
|
Name: "actioncable",
|
||||||
|
Version: "6.1.4.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantSBOM: &spdx.Document2_2{
|
||||||
|
CreationInfo: &spdx.CreationInfo2_2{
|
||||||
|
SPDXVersion: "SPDX-2.2",
|
||||||
|
DataLicense: "CC0-1.0",
|
||||||
|
SPDXIdentifier: "DOCUMENT",
|
||||||
|
DocumentName: "masahiro331/CVE-2021-41098",
|
||||||
|
DocumentNamespace: "http://aquasecurity.github.io/trivy/filesystem/masahiro331/CVE-2021-41098-3ff14136-e09f-4df9-80ea-000000000001",
|
||||||
|
CreatorOrganizations: []string{"aquasecurity"},
|
||||||
|
CreatorTools: []string{"trivy"},
|
||||||
|
Created: "2021-08-25T12:20:30.000000005Z",
|
||||||
|
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
|
||||||
|
},
|
||||||
|
Packages: map[spdx.ElementID]*spdx.Package2_2{
|
||||||
|
spdx.ElementID("5c11ed655628960c"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("5c11ed655628960c"),
|
||||||
|
PackageName: "actioncable",
|
||||||
|
PackageVersion: "6.1.4.1",
|
||||||
|
PackageLicenseConcluded: "NONE",
|
||||||
|
PackageLicenseDeclared: "NONE",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy path aggregate results",
|
||||||
|
inputReport: types.Report{
|
||||||
|
SchemaVersion: report.SchemaVersion,
|
||||||
|
ArtifactName: "test-aggregate",
|
||||||
|
ArtifactType: ftypes.ArtifactRemoteRepository,
|
||||||
|
Results: types.Results{
|
||||||
|
{
|
||||||
|
Target: "Node.js",
|
||||||
|
Class: types.ClassLangPkg,
|
||||||
|
Type: ftypes.NodePkg,
|
||||||
|
Packages: []ftypes.Package{
|
||||||
|
{
|
||||||
|
Name: "ruby-typeprof",
|
||||||
|
Version: "0.20.1",
|
||||||
|
License: "MIT",
|
||||||
|
Layer: ftypes.Layer{
|
||||||
|
DiffID: "sha256:661c3fd3cc16b34c070f3620ca6b03b6adac150f9a7e5d0e3c707a159990f88e",
|
||||||
|
},
|
||||||
|
FilePath: "usr/local/lib/ruby/gems/3.1.0/gems/typeprof-0.21.1/vscode/package.json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantSBOM: &spdx.Document2_2{
|
||||||
|
CreationInfo: &spdx.CreationInfo2_2{
|
||||||
|
SPDXVersion: "SPDX-2.2",
|
||||||
|
DataLicense: "CC0-1.0",
|
||||||
|
SPDXIdentifier: "DOCUMENT",
|
||||||
|
DocumentName: "test-aggregate",
|
||||||
|
DocumentNamespace: "http://aquasecurity.github.io/trivy/repository/test-aggregate-3ff14136-e09f-4df9-80ea-000000000001",
|
||||||
|
CreatorOrganizations: []string{"aquasecurity"},
|
||||||
|
CreatorTools: []string{"trivy"},
|
||||||
|
Created: "2021-08-25T12:20:30.000000005Z",
|
||||||
|
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
|
||||||
|
},
|
||||||
|
Packages: map[spdx.ElementID]*spdx.Package2_2{
|
||||||
|
spdx.ElementID("983b94af8413fe04"): {
|
||||||
|
PackageSPDXIdentifier: spdx.ElementID("983b94af8413fe04"),
|
||||||
|
PackageName: "ruby-typeprof",
|
||||||
|
PackageVersion: "0.20.1",
|
||||||
|
PackageLicenseConcluded: "MIT",
|
||||||
|
PackageLicenseDeclared: "MIT",
|
||||||
|
IsFilesAnalyzedTagPresent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy path empty",
|
||||||
|
inputReport: types.Report{
|
||||||
|
SchemaVersion: report.SchemaVersion,
|
||||||
|
ArtifactName: "empty/path",
|
||||||
|
ArtifactType: ftypes.ArtifactFilesystem,
|
||||||
|
Results: types.Results{},
|
||||||
|
},
|
||||||
|
wantSBOM: &spdx.Document2_2{
|
||||||
|
CreationInfo: &spdx.CreationInfo2_2{
|
||||||
|
SPDXVersion: "SPDX-2.2",
|
||||||
|
DataLicense: "CC0-1.0",
|
||||||
|
SPDXIdentifier: "DOCUMENT",
|
||||||
|
DocumentName: "empty/path",
|
||||||
|
DocumentNamespace: "http://aquasecurity.github.io/trivy/filesystem/empty/path-3ff14136-e09f-4df9-80ea-000000000001",
|
||||||
|
CreatorOrganizations: []string{"aquasecurity"},
|
||||||
|
CreatorTools: []string{"trivy"},
|
||||||
|
Created: "2021-08-25T12:20:30.000000005Z",
|
||||||
|
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
|
||||||
|
},
|
||||||
|
Packages: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clock := fake.NewFakeClock(time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC))
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
var count int
|
||||||
|
newUUID := func() uuid.UUID {
|
||||||
|
count++
|
||||||
|
return uuid.Must(uuid.Parse(fmt.Sprintf("3ff14136-e09f-4df9-80ea-%012d", count)))
|
||||||
|
}
|
||||||
|
|
||||||
|
output := bytes.NewBuffer(nil)
|
||||||
|
writer := reportSpdx.NewWriter(output, "dev", "spdx-json", reportSpdx.WithClock(clock), reportSpdx.WithNewUUID(newUUID))
|
||||||
|
|
||||||
|
err := writer.Write(tc.inputReport)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
got, err := jsonloader.Load2_2(output)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, *tc.wantSBOM, *got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||||
"github.com/aquasecurity/trivy/pkg/log"
|
"github.com/aquasecurity/trivy/pkg/log"
|
||||||
"github.com/aquasecurity/trivy/pkg/report/cyclonedx"
|
"github.com/aquasecurity/trivy/pkg/report/cyclonedx"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/report/spdx"
|
||||||
"github.com/aquasecurity/trivy/pkg/types"
|
"github.com/aquasecurity/trivy/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,6 +51,8 @@ func Write(report types.Report, option Option) error {
|
|||||||
case "cyclonedx":
|
case "cyclonedx":
|
||||||
// TODO: support xml format option with cyclonedx writer
|
// TODO: support xml format option with cyclonedx writer
|
||||||
writer = cyclonedx.NewWriter(option.Output, option.AppVersion)
|
writer = cyclonedx.NewWriter(option.Output, option.AppVersion)
|
||||||
|
case "spdx", "spdx-json":
|
||||||
|
writer = spdx.NewWriter(option.Output, option.AppVersion, option.Format)
|
||||||
case "template":
|
case "template":
|
||||||
// We keep `sarif.tpl` template working for backward compatibility for a while.
|
// We keep `sarif.tpl` template working for backward compatibility for a while.
|
||||||
if strings.HasPrefix(option.OutputTemplate, "@") && strings.HasSuffix(option.OutputTemplate, "sarif.tpl") {
|
if strings.HasPrefix(option.OutputTemplate, "@") && strings.HasSuffix(option.OutputTemplate, "sarif.tpl") {
|
||||||
|
|||||||
Reference in New Issue
Block a user