Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sbom): add support for scanning a sbom attestation #2652

Merged
merged 24 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ae0862b
feat(sbom): add support for scanning a sbom attestation
otms61 Aug 2, 2022
6a9cfb6
chore: excecute go mod tidy
otms61 Aug 2, 2022
8f38112
fix: fix goimports lint errors
otms61 Aug 2, 2022
c8e2a6b
feat: support for detecting attest xml
otms61 Aug 2, 2022
50727bb
refactor: move the predicate data access logic to attestation package
otms61 Aug 2, 2022
87b2d21
refacotr: rename cosign predicate data field
otms61 Aug 3, 2022
173b381
refactor: define our own Statement structure and remove the support f…
otms61 Aug 3, 2022
8e3d190
Merge branch 'main' into scan_sbom_attest
otms61 Aug 3, 2022
1be389e
refactor: fix the comment for Decode function
otms61 Aug 3, 2022
f461112
refactor: wrap the error by xerrors
otms61 Aug 3, 2022
b6cf89f
test: add a test for Decode attestaions
otms61 Aug 3, 2022
0921717
test: add a test for Inspect an SBOM attestation
otms61 Aug 3, 2022
031dd8f
refactor(cyclonedx): implement json.Unmarshaler
knqyf263 Aug 4, 2022
3e3438e
Merge branch 'refactor_sbom' into scan_sbom_attest
knqyf263 Aug 4, 2022
5838440
test(attestation): fix expected
knqyf263 Aug 4, 2022
e2b9200
Merge branch 'main' into scan_sbom_attest
knqyf263 Aug 4, 2022
5db9da3
refactor: remove cruft
knqyf263 Aug 4, 2022
03ae38e
test: add an integration test for scanning a sbom
otms61 Aug 4, 2022
bf6f0aa
docs: add a descrition for scanning sbom attestation
otms61 Aug 5, 2022
7219892
docs: update the cosign --type option
otms61 Aug 5, 2022
26cce23
refactor: use .intoto.jsonl extension
otms61 Aug 8, 2022
a0a8cf4
docs: use .intoto.jsonl extension
otms61 Aug 8, 2022
2393a0e
docs: respond to PR feedback
otms61 Aug 8, 2022
661b0eb
refactor: rename TestDecode to TestStatement_UnmarshalJSON
otms61 Aug 8, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/google/uuid v1.3.0
github.com/google/wire v0.5.0
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-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075
Expand All @@ -46,6 +47,7 @@ require (
github.com/owenrumney/go-sarif/v2 v2.1.2
github.com/package-url/packageurl-go v0.1.1-0.20220203205134-d70459300c8a
github.com/samber/lo v1.24.0
github.com/secure-systems-lab/go-securesystemslib v0.4.0
github.com/sosedoff/gitkit v0.3.0
github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5
Expand All @@ -64,7 +66,10 @@ require (
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 (
cloud.google.com/go v0.100.2 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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/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/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-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
Expand Down Expand Up @@ -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.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
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/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
Expand Down Expand Up @@ -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.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/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.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
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/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
Expand Down
52 changes: 52 additions & 0 deletions pkg/attestation/attestation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package attestation

import (
"bytes"
"encoding/base64"
"encoding/json"
"io"

"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.
type CosignPredicate struct {
Data json.RawMessage
}

// Statement holds statement headers and the predicate.
type Statement struct {
PredicateType string `json:"predicateType"`

// Predicate contains type specific metadata.
Predicate CosignPredicate `json:"predicate"`
}

// Decode returns the statement from the attestation.
func Decode(r io.Reader) (Statement, error) {

var envelope dsse.Envelope
err := json.NewDecoder(r).Decode(&envelope)
if err != nil {
return Statement{}, xerrors.Errorf("failed to decode as a dsse envelope: %w", err)
}
if envelope.PayloadType != in_toto.PayloadType {
return Statement{}, xerrors.Errorf("invalid attestation payload type: %s", envelope.PayloadType)
}

decoded, err := base64.StdEncoding.DecodeString(envelope.Payload)
if err != nil {
return Statement{}, xerrors.Errorf("failed to decode attestation payload: %w", err)
}

var st Statement
err = json.NewDecoder(bytes.NewReader(decoded)).Decode(&st)
if err != nil {
return Statement{}, xerrors.Errorf("failed to decode attestation payload as in-toto statement: %w", err)
}

return st, nil
}
3 changes: 3 additions & 0 deletions pkg/fanal/artifact/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/sbom"
"github.com/aquasecurity/trivy/pkg/sbom/attestation"
"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
)

Expand Down Expand Up @@ -63,6 +64,8 @@ func (a Artifact) Inspect(_ context.Context) (types.ArtifactReference, error) {
switch format {
case sbom.FormatCycloneDXJSON:
unmarshaler = cyclonedx.NewJSONUnmarshaler()
case sbom.FormatAttestCycloneDXJSON:
unmarshaler = attestation.NewUnmarshaler(cyclonedx.NewJSONUnmarshaler())
default:
return types.ArtifactReference{}, xerrors.Errorf("%s scanning is not yet supported", format)

Expand Down
30 changes: 30 additions & 0 deletions pkg/sbom/attestation/attestation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package attestation

import (
"bytes"
"io"

"golang.org/x/xerrors"

"github.com/aquasecurity/trivy/pkg/attestation"
"github.com/aquasecurity/trivy/pkg/sbom"
)

type Unmarshaler struct {
predicateUnmarshaler sbom.Unmarshaler
}

func (u Unmarshaler) Unmarshal(r io.Reader) (sbom.SBOM, error) {
attest, err := attestation.Decode(r)
if err != nil {
return sbom.SBOM{}, xerrors.Errorf("failed to decode attestation: %w", err)
}

return u.predicateUnmarshaler.Unmarshal(bytes.NewReader(attest.Predicate.Data))
Copy link
Collaborator

@knqyf263 knqyf263 Aug 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you wrap the error by xerrors if it is returned? It adds stack trace and helps us debug.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

func NewUnmarshaler(predicateUnmarshaler sbom.Unmarshaler) sbom.Unmarshaler {
return &Unmarshaler{
predicateUnmarshaler: predicateUnmarshaler,
}
}
24 changes: 19 additions & 5 deletions pkg/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"io"
"strings"

"github.com/in-toto/in-toto-golang/in_toto"
"golang.org/x/xerrors"

"github.com/aquasecurity/trivy/pkg/attestation"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)

Expand All @@ -26,11 +28,12 @@ type Unmarshaler interface {
type Format string

const (
FormatCycloneDXJSON = "cyclonedx-json"
FormatCycloneDXXML = "cyclonedx-xml"
FormatSPDXJSON = "spdx-json"
FormatSPDXXML = "spdx-xml"
FormatUnknown = "unknown"
FormatCycloneDXJSON = "cyclonedx-json"
FormatCycloneDXXML = "cyclonedx-xml"
FormatSPDXJSON = "spdx-json"
FormatSPDXXML = "spdx-xml"
FormatAttestCycloneDXJSON = "attest-cyclonedx-json"
FormatUnknown = "unknown"
)

func DetectFormat(r io.ReadSeeker) (Format, error) {
Expand Down Expand Up @@ -61,7 +64,18 @@ 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

// Try Attestation
if attest, err := attestation.Decode(r); err == nil {
if attest.PredicateType == in_toto.PredicateCycloneDX {
return FormatAttestCycloneDXJSON, nil
}
}

return FormatUnknown, nil
}