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
|
||||
- Supply chain security (SBOM support)
|
||||
- Support CycloneDX
|
||||
- Support SPDX
|
||||
|
||||
# Integrations
|
||||
- [GitHub Actions][action]
|
||||
|
||||
@@ -57,6 +57,7 @@ See [Integrations][integrations] for details.
|
||||
- remote git repository
|
||||
- [SBOM][sbom] (Software Bill of Materials) support
|
||||
- CycloneDX
|
||||
- SPDX
|
||||
|
||||
Please see [LICENSE][license] for Trivy licensing information.
|
||||
|
||||
|
||||
@@ -7,13 +7,20 @@ NAME:
|
||||
USAGE:
|
||||
trivy sbom [command options] ARTIFACT
|
||||
|
||||
DESCRIPTION:
|
||||
ARTIFACT can be a container image, file path/directory, git repository or container image archive. See examples.
|
||||
|
||||
OPTIONS:
|
||||
--output value, -o value output file name [$TRIVY_OUTPUT]
|
||||
--clear-cache, -c clear image caches without scanning (default: false) [$TRIVY_CLEAR_CACHE]
|
||||
--ignorefile value specify .trivyignore file (default: ".trivyignore") [$TRIVY_IGNOREFILE]
|
||||
--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]
|
||||
--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]
|
||||
--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)
|
||||
```
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# SBOM
|
||||
|
||||
Trivy currently supports the following SBOM formats.
|
||||
|
||||
- [CycloneDX][cyclonedx]
|
||||
- [SPDX][spdx]
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
[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/shopspring/decimal v1.2.0 // 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/stretchr/objx v0.3.0 // indirect
|
||||
github.com/tmccombs/hcl2json v0.3.4 // indirect
|
||||
@@ -198,5 +199,7 @@ require (
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
require github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
|
||||
// To resolve CVE-2022-23648
|
||||
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/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/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/mapstructure v0.0.0-20160808181253-ca63d7c062ee/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/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/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.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
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: ""
|
||||
|
||||
nav:
|
||||
- HOME: index.md
|
||||
- Getting started:
|
||||
- HOME: index.md
|
||||
- Getting started:
|
||||
- Overview: getting-started/overview.md
|
||||
- Installation: getting-started/installation.md
|
||||
- Quick Start: getting-started/quickstart.md
|
||||
- Further Reading: getting-started/further.md
|
||||
- Docs:
|
||||
- Overview: docs/index.md
|
||||
- Vulnerability:
|
||||
- Scanning:
|
||||
- Overview: docs/vulnerability/scanning/index.md
|
||||
- Container Image: docs/vulnerability/scanning/image.md
|
||||
- Filesystem: docs/vulnerability/scanning/filesystem.md
|
||||
- Rootfs: docs/vulnerability/scanning/rootfs.md
|
||||
- Git Repository: docs/vulnerability/scanning/git-repository.md
|
||||
- Detection:
|
||||
- OS Packages: docs/vulnerability/detection/os.md
|
||||
- Language-specific Packages: docs/vulnerability/detection/language.md
|
||||
- Data Sources: docs/vulnerability/detection/data-source.md
|
||||
- Supported: docs/vulnerability/detection/supported.md
|
||||
- Examples:
|
||||
- Vulnerability Filtering: docs/vulnerability/examples/filter.md
|
||||
- Report Formats: docs/vulnerability/examples/report.md
|
||||
- Vulnerability DB: docs/vulnerability/examples/db.md
|
||||
- Cache: docs/vulnerability/examples/cache.md
|
||||
- Others: docs/vulnerability/examples/others.md
|
||||
- Distributions: docs/vulnerability/distributions.md
|
||||
- Languages:
|
||||
- Go: docs/vulnerability/languages/golang.md
|
||||
- Misconfiguration:
|
||||
- Scanning:
|
||||
- Overview: docs/misconfiguration/index.md
|
||||
- Infrastructure as Code: docs/misconfiguration/iac.md
|
||||
- Filesystem: docs/misconfiguration/filesystem.md
|
||||
- Policy:
|
||||
- Built-in Policies: docs/misconfiguration/policy/builtin.md
|
||||
- Exceptions: docs/misconfiguration/policy/exceptions.md
|
||||
- Custom Policies:
|
||||
- Overview: docs/misconfiguration/custom/index.md
|
||||
- Data: docs/misconfiguration/custom/data.md
|
||||
- Combine: docs/misconfiguration/custom/combine.md
|
||||
- Testing: docs/misconfiguration/custom/testing.md
|
||||
- Debugging Policies: docs/misconfiguration/custom/debug.md
|
||||
- Examples: docs/misconfiguration/custom/examples.md
|
||||
- Options:
|
||||
- Policy: docs/misconfiguration/options/policy.md
|
||||
- Filtering: docs/misconfiguration/options/filter.md
|
||||
- Report Formats: docs/misconfiguration/options/report.md
|
||||
- Others: docs/misconfiguration/options/others.md
|
||||
- Comparison:
|
||||
- vs Conftest: docs/misconfiguration/comparison/conftest.md
|
||||
- vs tfsec: docs/misconfiguration/comparison/tfsec.md
|
||||
- vs cfsec: docs/misconfiguration/comparison/cfsec.md
|
||||
- Secret:
|
||||
- Scanning: docs/secret/scanning.md
|
||||
- Configuration: docs/secret/configuration.md
|
||||
- Examples: docs/secret/examples.md
|
||||
- SBOM:
|
||||
- Overview: docs/sbom/index.md
|
||||
- CycloneDX: docs/sbom/cyclonedx.md
|
||||
- Integrations:
|
||||
- Overview: docs/integrations/index.md
|
||||
- GitHub Actions: docs/integrations/github-actions.md
|
||||
- CircleCI: docs/integrations/circleci.md
|
||||
- Travis CI: docs/integrations/travis-ci.md
|
||||
- GitLab CI: docs/integrations/gitlab-ci.md
|
||||
- Bitbucket Pipelines: docs/integrations/bitbucket.md
|
||||
- AWS CodePipeline: docs/integrations/aws-codepipeline.md
|
||||
- AWS Security Hub: docs/integrations/aws-security-hub.md
|
||||
- Advanced:
|
||||
- Plugins: docs/advanced/plugins.md
|
||||
- Air-Gapped Environment: docs/advanced/air-gap.md
|
||||
- Container Image:
|
||||
- Embed in Dockerfile: docs/advanced/container/embed-in-dockerfile.md
|
||||
- Unpacked container image filesystem: docs/advanced/container/unpacked-filesystem.md
|
||||
- OCI Image: docs/advanced/container/oci.md
|
||||
- Podman: docs/advanced/container/podman.md
|
||||
- Private Docker Registries:
|
||||
- Overview: docs/advanced/private-registries/index.md
|
||||
- Docker Hub: docs/advanced/private-registries/docker-hub.md
|
||||
- AWS ECR (Elastic Container Registry): docs/advanced/private-registries/ecr.md
|
||||
- GCR (Google Container Registry): docs/advanced/private-registries/gcr.md
|
||||
- ACR (Azure Container Registry): docs/advanced/private-registries/acr.md
|
||||
- Self-Hosted: docs/advanced/private-registries/self.md
|
||||
- References:
|
||||
- CLI:
|
||||
- Overview: docs/references/cli/index.md
|
||||
- Image: docs/references/cli/image.md
|
||||
- Config: docs/references/cli/config.md
|
||||
- Filesystem: docs/references/cli/fs.md
|
||||
- Rootfs: docs/references/cli/rootfs.md
|
||||
- Repository: docs/references/cli/repo.md
|
||||
- Client: docs/references/cli/client.md
|
||||
- Server: docs/references/cli/server.md
|
||||
- Plugins: docs/references/cli/plugins.md
|
||||
- SBOM: docs/references/cli/sbom.md
|
||||
- Modes:
|
||||
- Standalone: docs/references/modes/standalone.md
|
||||
- Client/Server: docs/references/modes/client-server.md
|
||||
- Troubleshooting: docs/references/troubleshooting.md
|
||||
- Community:
|
||||
- Tools: community/tools.md
|
||||
- References: community/references.md
|
||||
- CKS Reference: community/cks.md
|
||||
- Credits: community/credit.md
|
||||
- How to contribute:
|
||||
- Issues: community/contribute/issue.md
|
||||
- Pull Requests: community/contribute/pr.md
|
||||
- Maintainer:
|
||||
- Help Wanted: community/maintainer/help-wanted.md
|
||||
- Triage: community/maintainer/triage.md
|
||||
- Docs:
|
||||
- Overview: docs/index.md
|
||||
- Vulnerability:
|
||||
- Scanning:
|
||||
- Overview: docs/vulnerability/scanning/index.md
|
||||
- Container Image: docs/vulnerability/scanning/image.md
|
||||
- Filesystem: docs/vulnerability/scanning/filesystem.md
|
||||
- Rootfs: docs/vulnerability/scanning/rootfs.md
|
||||
- Git Repository: docs/vulnerability/scanning/git-repository.md
|
||||
- Detection:
|
||||
- OS Packages: docs/vulnerability/detection/os.md
|
||||
- Language-specific Packages: docs/vulnerability/detection/language.md
|
||||
- Data Sources: docs/vulnerability/detection/data-source.md
|
||||
- Supported: docs/vulnerability/detection/supported.md
|
||||
- Examples:
|
||||
- Vulnerability Filtering: docs/vulnerability/examples/filter.md
|
||||
- Report Formats: docs/vulnerability/examples/report.md
|
||||
- Vulnerability DB: docs/vulnerability/examples/db.md
|
||||
- Cache: docs/vulnerability/examples/cache.md
|
||||
- Others: docs/vulnerability/examples/others.md
|
||||
- Distributions: docs/vulnerability/distributions.md
|
||||
- Languages:
|
||||
- Go: docs/vulnerability/languages/golang.md
|
||||
- Misconfiguration:
|
||||
- Scanning:
|
||||
- Overview: docs/misconfiguration/index.md
|
||||
- Infrastructure as Code: docs/misconfiguration/iac.md
|
||||
- Filesystem: docs/misconfiguration/filesystem.md
|
||||
- Policy:
|
||||
- Built-in Policies: docs/misconfiguration/policy/builtin.md
|
||||
- Exceptions: docs/misconfiguration/policy/exceptions.md
|
||||
- Custom Policies:
|
||||
- Overview: docs/misconfiguration/custom/index.md
|
||||
- Data: docs/misconfiguration/custom/data.md
|
||||
- Combine: docs/misconfiguration/custom/combine.md
|
||||
- Testing: docs/misconfiguration/custom/testing.md
|
||||
- Debugging Policies: docs/misconfiguration/custom/debug.md
|
||||
- Examples: docs/misconfiguration/custom/examples.md
|
||||
- Options:
|
||||
- Policy: docs/misconfiguration/options/policy.md
|
||||
- Filtering: docs/misconfiguration/options/filter.md
|
||||
- Report Formats: docs/misconfiguration/options/report.md
|
||||
- Others: docs/misconfiguration/options/others.md
|
||||
- Comparison:
|
||||
- vs Conftest: docs/misconfiguration/comparison/conftest.md
|
||||
- vs tfsec: docs/misconfiguration/comparison/tfsec.md
|
||||
- vs cfsec: docs/misconfiguration/comparison/cfsec.md
|
||||
- Secret:
|
||||
- Scanning: docs/secret/scanning.md
|
||||
- Configuration: docs/secret/configuration.md
|
||||
- Examples: docs/secret/examples.md
|
||||
- SBOM:
|
||||
- Overview: docs/sbom/index.md
|
||||
- CycloneDX: docs/sbom/cyclonedx.md
|
||||
- SPDX: docs/sbom/spdx.md
|
||||
- Integrations:
|
||||
- Overview: docs/integrations/index.md
|
||||
- GitHub Actions: docs/integrations/github-actions.md
|
||||
- CircleCI: docs/integrations/circleci.md
|
||||
- Travis CI: docs/integrations/travis-ci.md
|
||||
- GitLab CI: docs/integrations/gitlab-ci.md
|
||||
- Bitbucket Pipelines: docs/integrations/bitbucket.md
|
||||
- AWS CodePipeline: docs/integrations/aws-codepipeline.md
|
||||
- AWS Security Hub: docs/integrations/aws-security-hub.md
|
||||
- Advanced:
|
||||
- Plugins: docs/advanced/plugins.md
|
||||
- Air-Gapped Environment: docs/advanced/air-gap.md
|
||||
- Container Image:
|
||||
- Embed in Dockerfile: docs/advanced/container/embed-in-dockerfile.md
|
||||
- Unpacked container image filesystem: docs/advanced/container/unpacked-filesystem.md
|
||||
- OCI Image: docs/advanced/container/oci.md
|
||||
- Podman: docs/advanced/container/podman.md
|
||||
- Private Docker Registries:
|
||||
- Overview: docs/advanced/private-registries/index.md
|
||||
- Docker Hub: docs/advanced/private-registries/docker-hub.md
|
||||
- AWS ECR (Elastic Container Registry): docs/advanced/private-registries/ecr.md
|
||||
- GCR (Google Container Registry): docs/advanced/private-registries/gcr.md
|
||||
- ACR (Azure Container Registry): docs/advanced/private-registries/acr.md
|
||||
- Self-Hosted: docs/advanced/private-registries/self.md
|
||||
- References:
|
||||
- CLI:
|
||||
- Overview: docs/references/cli/index.md
|
||||
- Image: docs/references/cli/image.md
|
||||
- Config: docs/references/cli/config.md
|
||||
- Filesystem: docs/references/cli/fs.md
|
||||
- Rootfs: docs/references/cli/rootfs.md
|
||||
- Repository: docs/references/cli/repo.md
|
||||
- Client: docs/references/cli/client.md
|
||||
- Server: docs/references/cli/server.md
|
||||
- Plugins: docs/references/cli/plugins.md
|
||||
- SBOM: docs/references/cli/sbom.md
|
||||
- Modes:
|
||||
- Standalone: docs/references/modes/standalone.md
|
||||
- Client/Server: docs/references/modes/client-server.md
|
||||
- Troubleshooting: docs/references/troubleshooting.md
|
||||
- Community:
|
||||
- Tools: community/tools.md
|
||||
- References: community/references.md
|
||||
- CKS Reference: community/cks.md
|
||||
- Credits: community/credit.md
|
||||
- How to contribute:
|
||||
- Issues: community/contribute/issue.md
|
||||
- Pull Requests: community/contribute/pr.md
|
||||
- Maintainer:
|
||||
- Help Wanted: community/maintainer/help-wanted.md
|
||||
- Triage: community/maintainer/triage.md
|
||||
theme:
|
||||
name: material
|
||||
language: 'en'
|
||||
logo: imgs/logo-white.svg
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- navigation.sections
|
||||
name: material
|
||||
language: "en"
|
||||
logo: imgs/logo-white.svg
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- navigation.sections
|
||||
|
||||
markdown_extensions:
|
||||
- pymdownx.highlight
|
||||
@@ -148,4 +149,4 @@ extra:
|
||||
|
||||
plugins:
|
||||
- search
|
||||
- macros
|
||||
- macros
|
||||
|
||||
@@ -816,7 +816,7 @@ func NewSbomCommand() *cli.Command {
|
||||
Name: "sbom-format",
|
||||
Aliases: []string{"format"},
|
||||
Value: "cyclonedx",
|
||||
Usage: "SBOM format (cyclonedx)",
|
||||
Usage: "SBOM format (cyclonedx, spdx, spdx-json)",
|
||||
EnvVars: []string{"TRIVY_SBOM_FORMAT"},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
if c.Format == "cyclonedx" && !c.ListAllPkgs {
|
||||
logger.Debugf("'--format cyclonedx' automatically enables '--list-all-pkgs'.")
|
||||
if slices.Contains(supportedSbomFormats, c.Format) && !c.ListAllPkgs {
|
||||
logger.Debugf("'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestReportReportConfig_Init(t *testing.T) {
|
||||
},
|
||||
args: []string{"centos:7"},
|
||||
logs: []string{
|
||||
"'--format cyclonedx' automatically enables '--list-all-pkgs'.",
|
||||
"'cyclonedx', 'spdx', and 'spdx-json' automatically enables '--list-all-pkgs'.",
|
||||
"Severities: CRITICAL",
|
||||
},
|
||||
want: ReportOption{
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var supportedSbomFormats = []string{"cyclonedx"}
|
||||
var supportedSbomFormats = []string{"cyclonedx", "spdx", "spdx-json"}
|
||||
|
||||
// SbomOption holds the options for SBOM generation
|
||||
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"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/report/cyclonedx"
|
||||
"github.com/aquasecurity/trivy/pkg/report/spdx"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
@@ -50,6 +51,8 @@ func Write(report types.Report, option Option) error {
|
||||
case "cyclonedx":
|
||||
// TODO: support xml format option with cyclonedx writer
|
||||
writer = cyclonedx.NewWriter(option.Output, option.AppVersion)
|
||||
case "spdx", "spdx-json":
|
||||
writer = spdx.NewWriter(option.Output, option.AppVersion, option.Format)
|
||||
case "template":
|
||||
// We keep `sarif.tpl` template working for backward compatibility for a while.
|
||||
if strings.HasPrefix(option.OutputTemplate, "@") && strings.HasSuffix(option.OutputTemplate, "sarif.tpl") {
|
||||
|
||||
Reference in New Issue
Block a user