mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 15:50:15 -08:00
feat(sbom): add support for scanning a sbom attestation (#2652)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
@@ -31,8 +31,9 @@ $ trivy image --format spdx-json -o sbom.spdx.json <IMAGE>
|
|||||||
$ cosign attest --key /path/to/cosign.key --type spdx --predicate sbom.spdx.json <IMAGE>
|
$ cosign attest --key /path/to/cosign.key --type spdx --predicate sbom.spdx.json <IMAGE>
|
||||||
|
|
||||||
# cyclonedx
|
# cyclonedx
|
||||||
|
# The cyclonedx type is supported in Cosign v1.10.0 or later.
|
||||||
$ trivy image --format cyclonedx -o sbom.cdx.json <IMAGE>
|
$ trivy image --format cyclonedx -o sbom.cdx.json <IMAGE>
|
||||||
$ cosign attest --key /path/to/cosign.key --type https://cyclonedx.org/schema --predicate sbom.cdx.json <IMAGE>
|
$ cosign attest --key /path/to/cosign.key --type cyclonedx --predicate sbom.cdx.json <IMAGE>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Keyless signing
|
## Keyless signing
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ Examples:
|
|||||||
# Scan CycloneDX and generate a CycloneDX report
|
# Scan CycloneDX and generate a CycloneDX report
|
||||||
$ trivy sbom --format cyclonedx /path/to/report.cdx
|
$ trivy sbom --format cyclonedx /path/to/report.cdx
|
||||||
|
|
||||||
|
# Scan CycloneDX-type attestation and show the result in tables
|
||||||
|
$ trivy sbom /path/to/report.cdx.intoto.jsonl
|
||||||
|
|
||||||
|
|
||||||
Scan Flags
|
Scan Flags
|
||||||
--offline-scan do not issue API requests to identify dependencies
|
--offline-scan do not issue API requests to identify dependencies
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ $ trivy fs --format cyclonedx --output result.json /app/myproject
|
|||||||
Trivy also can take the following SBOM formats as an input and scan for vulnerabilities.
|
Trivy also can take the following SBOM formats as an input and scan for vulnerabilities.
|
||||||
|
|
||||||
- CycloneDX
|
- CycloneDX
|
||||||
|
- CycloneDX-type attestation
|
||||||
|
|
||||||
To scan SBOM, you can use the `sbom` subcommand and pass the path to the SBOM.
|
To scan SBOM, you can use the `sbom` subcommand and pass the path to the SBOM.
|
||||||
|
|
||||||
@@ -209,5 +210,31 @@ Total: 3 (CRITICAL: 3)
|
|||||||
!!! note
|
!!! note
|
||||||
CycloneDX XML and SPDX are not supported at the moment.
|
CycloneDX XML and SPDX are not supported at the moment.
|
||||||
|
|
||||||
|
You can also scan an SBOM attestation.
|
||||||
|
In the following example, [Cosign][Cosign] can get an attestation and trivy scan it.
|
||||||
|
To learn more about how to create an SBOM attestation and attach it to an image, see the [SBOM attestation page][sbom_attestation].
|
||||||
|
```bash
|
||||||
|
$ cosign verify-attestation --key /path/to/cosign.pub --type cyclonedx <IMAGE> > sbom.cdx.intoto.jsonl
|
||||||
|
$ trivy sbom ./sbom.cdx.intoto.jsonl
|
||||||
|
|
||||||
|
sbom.cdx.intoto.jsonl (alpine 3.7.3)
|
||||||
|
=========================
|
||||||
|
Total: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 2)
|
||||||
|
|
||||||
|
┌────────────┬────────────────┬──────────┬───────────────────┬───────────────┬──────────────────────────────────────────────────────────┐
|
||||||
|
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │
|
||||||
|
├────────────┼────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────┤
|
||||||
|
│ musl │ CVE-2019-14697 │ CRITICAL │ 1.1.18-r3 │ 1.1.18-r4 │ musl libc through 1.1.23 has an x87 floating-point stack │
|
||||||
|
│ │ │ │ │ │ adjustment im ...... │
|
||||||
|
│ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2019-14697 │
|
||||||
|
├────────────┤ │ │ │ │ │
|
||||||
|
│ musl-utils │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ │
|
||||||
|
└────────────┴────────────────┴──────────┴───────────────────┴───────────────┴──────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
[cyclonedx]: cyclonedx.md
|
[cyclonedx]: cyclonedx.md
|
||||||
[spdx]: spdx.md
|
[spdx]: spdx.md
|
||||||
|
[Cosign]: https://github.com/sigstore/cosign
|
||||||
|
[sbom_attestation]: ../attestation/sbom.md
|
||||||
7
go.mod
7
go.mod
@@ -33,6 +33,7 @@ require (
|
|||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/google/wire v0.5.0
|
github.com/google/wire v0.5.0
|
||||||
github.com/hashicorp/go-getter v1.6.2
|
github.com/hashicorp/go-getter v1.6.2
|
||||||
|
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add
|
||||||
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
|
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
|
||||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
|
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
|
||||||
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075
|
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075
|
||||||
@@ -46,6 +47,7 @@ require (
|
|||||||
github.com/owenrumney/go-sarif/v2 v2.1.2
|
github.com/owenrumney/go-sarif/v2 v2.1.2
|
||||||
github.com/package-url/packageurl-go v0.1.1-0.20220203205134-d70459300c8a
|
github.com/package-url/packageurl-go v0.1.1-0.20220203205134-d70459300c8a
|
||||||
github.com/samber/lo v1.27.0
|
github.com/samber/lo v1.27.0
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.4.0
|
||||||
github.com/sosedoff/gitkit v0.3.0
|
github.com/sosedoff/gitkit v0.3.0
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
@@ -64,7 +66,10 @@ require (
|
|||||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
require (
|
||||||
|
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
||||||
|
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.100.2 // indirect
|
cloud.google.com/go v0.100.2 // indirect
|
||||||
|
|||||||
7
go.sum
7
go.sum
@@ -326,6 +326,7 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
|
|||||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||||
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||||
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
|
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
|
||||||
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
|
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
|
||||||
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||||
@@ -922,6 +923,8 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
|
|||||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||||
|
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add h1:DAh7mHiRT7wc6kKepYdCpH16ElPciMPQWJaJ7H3l/ng=
|
||||||
|
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add/go.mod h1:DQI8vlV6h6qSY/tCOoYKtxjWrkyiNpJ3WTV/WoBllmQ=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
|
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
|
||||||
@@ -1355,9 +1358,13 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
|||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||||
|
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||||
|
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
|
||||||
github.com/shogo82148/go-shuffle v0.0.0-20170808115208-59829097ff3b h1:VI1u+o2KZPZ5AhuPpXY0JBdpQPnkTx6Dd5XJhK/9MYE=
|
github.com/shogo82148/go-shuffle v0.0.0-20170808115208-59829097ff3b h1:VI1u+o2KZPZ5AhuPpXY0JBdpQPnkTx6Dd5XJhK/9MYE=
|
||||||
github.com/shogo82148/go-shuffle v0.0.0-20170808115208-59829097ff3b/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
|
github.com/shogo82148/go-shuffle v0.0.0-20170808115208-59829097ff3b/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
|
||||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ func TestCycloneDX(t *testing.T) {
|
|||||||
},
|
},
|
||||||
golden: "testdata/fluentd-multiple-lockfiles-cyclonedx.json.golden",
|
golden: "testdata/fluentd-multiple-lockfiles-cyclonedx.json.golden",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "centos7-bom in in-toto attestation",
|
||||||
|
args: args{
|
||||||
|
input: "testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl",
|
||||||
|
format: "cyclonedx",
|
||||||
|
artifactType: "cyclonedx",
|
||||||
|
},
|
||||||
|
golden: "testdata/centos-7-cyclonedx.json.golden",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up testing DB
|
// Set up testing DB
|
||||||
|
|||||||
1
integration/testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl
vendored
Normal file
1
integration/testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2N5Y2xvbmVkeC5vcmcvc2NoZW1hIiwic3ViamVjdCI6W3sibmFtZSI6ImdoY3IuaW8vYXF1YXNlY3VyaXR5L3RyaXZ5LXRlc3QtaW1hZ2VzIiwiZGlnZXN0Ijp7InNoYTI1NiI6IjcyYzQyZWQ0OGMzYTJkYjMxYjdkYWZlMTdkMjc1YjYzNDY2NGE3MDhkOTAxZWM5ZmQ1N2IxNTI5MjgwZjAxZmIifX1dLCJwcmVkaWNhdGUiOnsiRGF0YSI6eyJib21Gb3JtYXQiOiJDeWNsb25lRFgiLCJjb21wb25lbnRzIjpbeyJib20tcmVmIjoicGtnOnJwbS9jZW50b3MvYmFzaEA0LjIuNDYtMzEuZWw3P2FyY2g9eDg2XzY0XHUwMDI2ZGlzdHJvPWNlbnRvcy03LjYuMTgxMCIsImxpY2Vuc2VzIjpbeyJleHByZXNzaW9uIjoiR1BMdjMrIn1dLCJuYW1lIjoiYmFzaCIsInByb3BlcnRpZXMiOlt7Im5hbWUiOiJhcXVhc2VjdXJpdHk6dHJpdnk6U3JjTmFtZSIsInZhbHVlIjoiYmFzaCJ9LHsibmFtZSI6ImFxdWFzZWN1cml0eTp0cml2eTpTcmNWZXJzaW9uIiwidmFsdWUiOiI0LjIuNDYifSx7Im5hbWUiOiJhcXVhc2VjdXJpdHk6dHJpdnk6U3JjUmVsZWFzZSIsInZhbHVlIjoiMzEuZWw3In0seyJuYW1lIjoiYXF1YXNlY3VyaXR5OnRyaXZ5OkxheWVyRGlnZXN0IiwidmFsdWUiOiJzaGEyNTY6YWM5MjA4MjA3YWRhYWMzYTQ4ZTU0YTRkYzZiNDljNjllNzhjMzA3MmQyYjNhZGQ3ZWZkYWJmODE0ZGIyMTMzYiJ9LHsibmFtZSI6ImFxdWFzZWN1cml0eTp0cml2eTpMYXllckRpZmZJRCIsInZhbHVlIjoic2hhMjU2Ojg5MTY5ZDg3ZGJlMmI3MmJhNDJiZmJiMzU3OWM5NTczMjJiYWNhMjhlMDNhMWU1NTgwNzY1NDJhMWMxYjJiNGEifV0sInB1cmwiOiJwa2c6cnBtL2NlbnRvcy9iYXNoQDQuMi40Ni0zMS5lbDc/YXJjaD14ODZfNjRcdTAwMjZkaXN0cm89Y2VudG9zLTcuNi4xODEwIiwidHlwZSI6ImxpYnJhcnkiLCJ2ZXJzaW9uIjoiNC4yLjQ2LTMxLmVsNyJ9LHsiYm9tLXJlZiI6InBrZzpycG0vY2VudG9zL29wZW5zc2wtbGlic0AxOjEuMC4yay0xNi5lbDc/YXJjaD14ODZfNjRcdTAwMjZkaXN0cm89Y2VudG9zLTcuNi4xODEwIiwibGljZW5zZXMiOlt7ImV4cHJlc3Npb24iOiJPcGVuU1NMIn1dLCJuYW1lIjoib3BlbnNzbC1saWJzIiwicHJvcGVydGllcyI6W3sibmFtZSI6ImFxdWFzZWN1cml0eTp0cml2eTpTcmNOYW1lIiwidmFsdWUiOiJvcGVuc3NsIn0seyJuYW1lIjoiYXF1YXNlY3VyaXR5OnRyaXZ5OlNyY1ZlcnNpb24iLCJ2YWx1ZSI6IjEuMC4yayJ9LHsibmFtZSI6ImFxdWFzZWN1cml0eTp0cml2eTpTcmNSZWxlYXNlIiwidmFsdWUiOiIxNi5lbDcifSx7Im5hbWUiOiJhcXVhc2VjdXJpdHk6dHJpdnk6U3JjRXBvY2giLCJ2YWx1ZSI6IjEifSx7Im5hbWUiOiJhcXVhc2VjdXJpdHk6dHJpdnk6TGF5ZXJEaWdlc3QiLCJ2YWx1ZSI6InNoYTI1NjphYzkyMDgyMDdhZGFhYzNhNDhlNTRhNGRjNmI0OWM2OWU3OGMzMDcyZDJiM2FkZDdlZmRhYmY4MTRkYjIxMzNiIn0seyJuYW1lIjoiYXF1YXNlY3VyaXR5OnRyaXZ5OkxheWVyRGlmZklEIiwidmFsdWUiOiJzaGEyNTY6ODkxNjlkODdkYmUyYjcyYmE0MmJmYmIzNTc5Yzk1NzMyMmJhY2EyOGUwM2ExZTU1ODA3NjU0MmExYzFiMmI0YSJ9XSwicHVybCI6InBrZzpycG0vY2VudG9zL29wZW5zc2wtbGlic0AxOjEuMC4yay0xNi5lbDc/YXJjaD14ODZfNjRcdTAwMjZkaXN0cm89Y2VudG9zLTcuNi4xODEwIiwidHlwZSI6ImxpYnJhcnkiLCJ2ZXJzaW9uIjoiMToxLjAuMmstMTYuZWw3In0seyJib20tcmVmIjoiMDE3NWY3MzItZGY5ZC00YmI4LTlmNTYtODcwODk4ZTNmZjg5IiwibmFtZSI6ImNlbnRvcyIsInByb3BlcnRpZXMiOlt7Im5hbWUiOiJhcXVhc2VjdXJpdHk6dHJpdnk6VHlwZSIsInZhbHVlIjoiY2VudG9zIn0seyJuYW1lIjoiYXF1YXNlY3VyaXR5OnRyaXZ5OkNsYXNzIiwidmFsdWUiOiJvcy1wa2dzIn1dLCJ0eXBlIjoib3BlcmF0aW5nLXN5c3RlbSIsInZlcnNpb24iOiI3LjYuMTgxMCJ9XSwiZGVwZW5kZW5jaWVzIjpbeyJkZXBlbmRzT24iOlsicGtnOnJwbS9jZW50b3MvYmFzaEA0LjIuNDYtMzEuZWw3P2FyY2g9eDg2XzY0XHUwMDI2ZGlzdHJvPWNlbnRvcy03LjYuMTgxMCIsInBrZzpycG0vY2VudG9zL29wZW5zc2wtbGlic0AxOjEuMC4yay0xNi5lbDc/YXJjaD14ODZfNjRcdTAwMjZkaXN0cm89Y2VudG9zLTcuNi4xODEwIl0sInJlZiI6IjAxNzVmNzMyLWRmOWQtNGJiOC05ZjU2LTg3MDg5OGUzZmY4OSJ9LHsiZGVwZW5kc09uIjpbIjAxNzVmNzMyLWRmOWQtNGJiOC05ZjU2LTg3MDg5OGUzZmY4OSJdLCJyZWYiOiJkMGQ0MWUzMC05NjUwLTQ4OWQtOTQ4ZC00MjVmZjJlZDYzZDIifV0sIm1ldGFkYXRhIjp7ImNvbXBvbmVudCI6eyJib20tcmVmIjoiZDBkNDFlMzAtOTY1MC00ODlkLTk0OGQtNDI1ZmYyZWQ2M2QyIiwibmFtZSI6ImludGVncmF0aW9uL3Rlc3RkYXRhL2ZpeHR1cmVzL2ltYWdlcy9jZW50b3MtNy50YXIuZ3oiLCJwcm9wZXJ0aWVzIjpbeyJuYW1lIjoiYXF1YXNlY3VyaXR5OnRyaXZ5OlNjaGVtYVZlcnNpb24iLCJ2YWx1ZSI6IjIifSx7Im5hbWUiOiJhcXVhc2VjdXJpdHk6dHJpdnk6SW1hZ2VJRCIsInZhbHVlIjoic2hhMjU2OmYxY2I3YzdkNThiNzNlYWM4NTljMzk1ODgyZWVjNDlkNTA2NTEyNDRlMzQyY2Q2YzY4YTVjNzgwOTc4NWY0MjcifSx7Im5hbWUiOiJhcXVhc2VjdXJpdHk6dHJpdnk6RGlmZklEIiwidmFsdWUiOiJzaGEyNTY6ODkxNjlkODdkYmUyYjcyYmE0MmJmYmIzNTc5Yzk1NzMyMmJhY2EyOGUwM2ExZTU1ODA3NjU0MmExYzFiMmI0YSJ9XSwidHlwZSI6ImNvbnRhaW5lciJ9LCJ0aW1lc3RhbXAiOiIyMDIyLTA2LTE0VDE1OjA4OjQ4KzAwOjAwIiwidG9vbHMiOlt7Im5hbWUiOiJ0cml2eSIsInZlbmRvciI6ImFxdWFzZWN1cml0eSIsInZlcnNpb24iOiJkZXYifV19LCJzZXJpYWxOdW1iZXIiOiJ1cm46dXVpZDoxNDU1YzAyZC02NGNhLTQ1M2UtYTVkZi1kZGZiNzBhN2M4MDQiLCJzcGVjVmVyc2lvbiI6IjEuNCIsInZlcnNpb24iOjF9LCJUaW1lc3RhbXAiOiIifX0=","signatures":[{"keyid":"","sig":"MEUCIF52Th/Uxp9iGoqyP8ioikcefayjXh/+GhKyhhdczihaAiEAwOedZ0ovOanwY+u9Dl+/bHp8398YcXA2n0zG8Q2gnb0="}]}
|
||||||
44
pkg/attestation/attestation.go
Normal file
44
pkg/attestation/attestation.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package attestation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/in-toto/in-toto-golang/in_toto"
|
||||||
|
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CosignPredicate specifies the format of the Custom Predicate.
|
||||||
|
// Cosign uses this structure when creating an SBOM attestation.
|
||||||
|
// cf. https://github.com/sigstore/cosign/blob/e0547cff64f98585a837a524ff77ff6b47ff5609/pkg/cosign/attestation/attestation.go#L39-L43
|
||||||
|
type CosignPredicate struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statement holds in-toto statement headers and the predicate.
|
||||||
|
type Statement in_toto.Statement
|
||||||
|
|
||||||
|
func (s *Statement) UnmarshalJSON(b []byte) error {
|
||||||
|
var envelope dsse.Envelope
|
||||||
|
err := json.NewDecoder(bytes.NewReader(b)).Decode(&envelope)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to decode as a dsse envelope: %w", err)
|
||||||
|
}
|
||||||
|
if envelope.PayloadType != in_toto.PayloadType {
|
||||||
|
return xerrors.Errorf("invalid attestation payload type: %s", envelope.PayloadType)
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, err := base64.StdEncoding.DecodeString(envelope.Payload)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to decode attestation payload: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
statement := (*in_toto.Statement)(s)
|
||||||
|
if err = json.NewDecoder(bytes.NewReader(decoded)).Decode(statement); err != nil {
|
||||||
|
return xerrors.Errorf("failed to decode attestation payload as in-toto statement: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
55
pkg/attestation/attestation_test.go
Normal file
55
pkg/attestation/attestation_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package attestation_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/in-toto/in-toto-golang/in_toto"
|
||||||
|
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/aquasecurity/trivy/pkg/attestation"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStatement_UnmarshalJSON(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
inputFile string
|
||||||
|
want attestation.Statement
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "happy path",
|
||||||
|
inputFile: "testdata/attestation.json",
|
||||||
|
want: attestation.Statement{
|
||||||
|
StatementHeader: in_toto.StatementHeader{
|
||||||
|
Type: "https://in-toto.io/Statement/v0.1",
|
||||||
|
PredicateType: "cosign.sigstore.dev/attestation/v1",
|
||||||
|
Subject: []in_toto.Subject{
|
||||||
|
{
|
||||||
|
Name: "ghcr.io/aquasecurity/trivy-test-images",
|
||||||
|
Digest: slsa.DigestSet{
|
||||||
|
"sha256": "72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Predicate: &attestation.CosignPredicate{
|
||||||
|
Data: "foo\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
f, err := os.Open(tt.inputFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
got := attestation.Statement{Predicate: &attestation.CosignPredicate{}}
|
||||||
|
err = json.NewDecoder(f).Decode(&got)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
1
pkg/attestation/testdata/attestation.json
vendored
Normal file
1
pkg/attestation/testdata/attestation.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJjb3NpZ24uc2lnc3RvcmUuZGV2L2F0dGVzdGF0aW9uL3YxIiwic3ViamVjdCI6W3sibmFtZSI6ImdoY3IuaW8vYXF1YXNlY3VyaXR5L3RyaXZ5LXRlc3QtaW1hZ2VzIiwiZGlnZXN0Ijp7InNoYTI1NiI6IjcyYzQyZWQ0OGMzYTJkYjMxYjdkYWZlMTdkMjc1YjYzNDY2NGE3MDhkOTAxZWM5ZmQ1N2IxNTI5MjgwZjAxZmIifX1dLCJwcmVkaWNhdGUiOnsiRGF0YSI6ImZvb1xuIiwiVGltZXN0YW1wIjoiMjAyMi0wOC0wM1QxMzowODoyN1oifX0=","signatures":[{"keyid":"","sig":"MEUCIQClJhJ2mS78MWy4L32wxd+8gPXYwpvyn0nmuY9r5t8iiAIgHKKoIJbKAKQ8i/bgN76ocuGhwUMdbgqpgKF0yFfPfGI="}]}
|
||||||
@@ -813,6 +813,9 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
|||||||
|
|
||||||
# Scan CycloneDX and generate a CycloneDX report
|
# Scan CycloneDX and generate a CycloneDX report
|
||||||
$ trivy sbom --format cyclonedx /path/to/report.cdx
|
$ trivy sbom --format cyclonedx /path/to/report.cdx
|
||||||
|
|
||||||
|
# Scan CycloneDX-type attestation and show the result in tables
|
||||||
|
$ trivy sbom /path/to/report.cdx.intoto.jsonl
|
||||||
`,
|
`,
|
||||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := sbomFlags.Bind(cmd); err != nil {
|
if err := sbomFlags.Bind(cmd); err != nil {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/aquasecurity/trivy/pkg/attestation"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config"
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
||||||
@@ -82,7 +83,7 @@ func (a Artifact) Inspect(_ context.Context) (types.ArtifactReference, error) {
|
|||||||
|
|
||||||
var artifactType types.ArtifactType
|
var artifactType types.ArtifactType
|
||||||
switch format {
|
switch format {
|
||||||
case sbom.FormatCycloneDXJSON, sbom.FormatCycloneDXXML:
|
case sbom.FormatCycloneDXJSON, sbom.FormatCycloneDXXML, sbom.FormatAttestCycloneDXJSON:
|
||||||
artifactType = types.ArtifactCycloneDX
|
artifactType = types.ArtifactCycloneDX
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +109,16 @@ func (a Artifact) Decode(f io.Reader, format sbom.Format) (sbom.SBOM, error) {
|
|||||||
case sbom.FormatCycloneDXJSON:
|
case sbom.FormatCycloneDXJSON:
|
||||||
v = &cyclonedx.CycloneDX{SBOM: &bom}
|
v = &cyclonedx.CycloneDX{SBOM: &bom}
|
||||||
decoder = json.NewDecoder(f)
|
decoder = json.NewDecoder(f)
|
||||||
|
case sbom.FormatAttestCycloneDXJSON:
|
||||||
|
// in-toto attestation
|
||||||
|
// => cosign predicate
|
||||||
|
// => CycloneDX JSON
|
||||||
|
v = &attestation.Statement{
|
||||||
|
Predicate: &attestation.CosignPredicate{
|
||||||
|
Data: &cyclonedx.CycloneDX{SBOM: &bom},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
decoder = json.NewDecoder(f)
|
||||||
default:
|
default:
|
||||||
return sbom.SBOM{}, xerrors.Errorf("%s scanning is not yet supported", format)
|
return sbom.SBOM{}, xerrors.Errorf("%s scanning is not yet supported", format)
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,112 @@ func TestArtifact_Inspect(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "happy path for sbom attestation",
|
||||||
|
filePath: "testdata/sbom.cdx.intoto.jsonl",
|
||||||
|
putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
|
||||||
|
Args: cache.ArtifactCachePutBlobArgs{
|
||||||
|
BlobID: "sha256:21f10e5ab97c37f6c4d6a45815cd5db10e9539d5db8614d3b1d8890111d7a2b8",
|
||||||
|
BlobInfo: types.BlobInfo{
|
||||||
|
SchemaVersion: types.BlobJSONSchemaVersion,
|
||||||
|
OS: &types.OS{
|
||||||
|
Family: "alpine",
|
||||||
|
Name: "3.16.0",
|
||||||
|
},
|
||||||
|
PackageInfos: []types.PackageInfo{
|
||||||
|
{
|
||||||
|
Packages: []types.Package{
|
||||||
|
{
|
||||||
|
Name: "musl", Version: "1.2.3-r0", SrcName: "musl", SrcVersion: "1.2.3-r0", Licenses: []string{"MIT"},
|
||||||
|
Ref: "pkg:apk/alpine/musl@1.2.3-r0?distro=3.16.0",
|
||||||
|
Layer: types.Layer{
|
||||||
|
DiffID: "sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Applications: []types.Application{
|
||||||
|
{
|
||||||
|
Type: "composer",
|
||||||
|
FilePath: "app/composer/composer.lock",
|
||||||
|
Libraries: []types.Package{
|
||||||
|
{
|
||||||
|
Name: "pear/log",
|
||||||
|
Version: "1.13.1",
|
||||||
|
Ref: "pkg:composer/pear/log@1.13.1",
|
||||||
|
Layer: types.Layer{
|
||||||
|
DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
Name: "pear/pear_exception",
|
||||||
|
Version: "v1.0.0",
|
||||||
|
Ref: "pkg:composer/pear/pear_exception@v1.0.0",
|
||||||
|
Layer: types.Layer{
|
||||||
|
DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "gobinary",
|
||||||
|
FilePath: "app/gobinary/gobinary",
|
||||||
|
Libraries: []types.Package{
|
||||||
|
{
|
||||||
|
Name: "github.com/package-url/packageurl-go",
|
||||||
|
Version: "v0.1.1-0.20220203205134-d70459300c8a",
|
||||||
|
Ref: "pkg:golang/github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a",
|
||||||
|
Layer: types.Layer{
|
||||||
|
DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "jar",
|
||||||
|
FilePath: "",
|
||||||
|
Libraries: []types.Package{
|
||||||
|
{
|
||||||
|
Name: "org.codehaus.mojo:child-project",
|
||||||
|
Ref: "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar",
|
||||||
|
Version: "1.0",
|
||||||
|
Layer: types.Layer{
|
||||||
|
DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "node-pkg",
|
||||||
|
FilePath: "",
|
||||||
|
Libraries: []types.Package{
|
||||||
|
{
|
||||||
|
Name: "bootstrap",
|
||||||
|
Version: "5.0.2",
|
||||||
|
Ref: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json",
|
||||||
|
Licenses: []string{"MIT"},
|
||||||
|
Layer: types.Layer{
|
||||||
|
DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Returns: cache.ArtifactCachePutBlobReturns{},
|
||||||
|
},
|
||||||
|
want: types.ArtifactReference{
|
||||||
|
Name: "testdata/sbom.cdx.intoto.jsonl",
|
||||||
|
Type: types.ArtifactCycloneDX,
|
||||||
|
ID: "sha256:21f10e5ab97c37f6c4d6a45815cd5db10e9539d5db8614d3b1d8890111d7a2b8",
|
||||||
|
BlobIDs: []string{
|
||||||
|
"sha256:21f10e5ab97c37f6c4d6a45815cd5db10e9539d5db8614d3b1d8890111d7a2b8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "sad path with no such directory",
|
name: "sad path with no such directory",
|
||||||
filePath: "./testdata/unknown.json",
|
filePath: "./testdata/unknown.json",
|
||||||
|
|||||||
1
pkg/fanal/artifact/sbom/testdata/sbom.cdx.intoto.jsonl
vendored
Normal file
1
pkg/fanal/artifact/sbom/testdata/sbom.cdx.intoto.jsonl
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -6,6 +6,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aquasecurity/trivy/pkg/log"
|
||||||
|
|
||||||
cdx "github.com/CycloneDX/cyclonedx-go"
|
cdx "github.com/CycloneDX/cyclonedx-go"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
@@ -23,6 +25,7 @@ type CycloneDX struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CycloneDX) UnmarshalJSON(b []byte) error {
|
func (c *CycloneDX) UnmarshalJSON(b []byte) error {
|
||||||
|
log.Logger.Debug("Unmarshaling CycloneDX JSON...")
|
||||||
if c.SBOM == nil {
|
if c.SBOM == nil {
|
||||||
c.SBOM = &sbom.SBOM{}
|
c.SBOM = &sbom.SBOM{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/in-toto/in-toto-golang/in_toto"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/aquasecurity/trivy/pkg/attestation"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,11 +24,12 @@ type SBOM struct {
|
|||||||
type Format string
|
type Format string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FormatCycloneDXJSON Format = "cyclonedx-json"
|
FormatCycloneDXJSON Format = "cyclonedx-json"
|
||||||
FormatCycloneDXXML Format = "cyclonedx-xml"
|
FormatCycloneDXXML Format = "cyclonedx-xml"
|
||||||
FormatSPDXJSON Format = "spdx-json"
|
FormatSPDXJSON Format = "spdx-json"
|
||||||
FormatSPDXXML Format = "spdx-xml"
|
FormatSPDXXML Format = "spdx-xml"
|
||||||
FormatUnknown Format = "unknown"
|
FormatAttestCycloneDXJSON Format = "attest-cyclonedx-json"
|
||||||
|
FormatUnknown Format = "unknown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DetectFormat(r io.ReadSeeker) (Format, error) {
|
func DetectFormat(r io.ReadSeeker) (Format, error) {
|
||||||
@@ -57,7 +60,19 @@ func DetectFormat(r io.ReadSeeker) (Format, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||||
|
return FormatUnknown, xerrors.Errorf("seek error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: implement SPDX
|
// TODO: implement SPDX
|
||||||
|
|
||||||
|
// Try in-toto attestation
|
||||||
|
var s attestation.Statement
|
||||||
|
if err := json.NewDecoder(r).Decode(&s); err == nil {
|
||||||
|
if s.PredicateType == in_toto.PredicateCycloneDX {
|
||||||
|
return FormatAttestCycloneDXJSON, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return FormatUnknown, nil
|
return FormatUnknown, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user