Skip to content

Commit

Permalink
feat(sbom): add support for CycloneDX JSON Attestation of the correct…
Browse files Browse the repository at this point in the history
… specification (#3849)
  • Loading branch information
otms61 authored Mar 19, 2023
1 parent a754a04 commit ba9b041
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 6 deletions.
2 changes: 1 addition & 1 deletion pkg/fanal/artifact/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (a Artifact) Inspect(_ context.Context) (types.ArtifactReference, error) {

var artifactType types.ArtifactType
switch format {
case sbom.FormatCycloneDXJSON, sbom.FormatCycloneDXXML, sbom.FormatAttestCycloneDXJSON:
case sbom.FormatCycloneDXJSON, sbom.FormatCycloneDXXML, sbom.FormatAttestCycloneDXJSON, sbom.FormatLegacyCosignAttestCycloneDXJSON:
artifactType = types.ArtifactCycloneDX
case sbom.FormatSPDXTV, sbom.FormatSPDXJSON:
artifactType = types.ArtifactSPDX
Expand Down
48 changes: 43 additions & 5 deletions pkg/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const (
FormatAttestCycloneDXJSON Format = "attest-cyclonedx-json"
FormatUnknown Format = "unknown"

// FormatLegacyCosignAttestCycloneDXJSON is used to support the older format of CycloneDX JSON Attestation
// produced by the Cosign V1.
// ref. https://github.com/sigstore/cosign/pull/2718
FormatLegacyCosignAttestCycloneDXJSON Format = "legacy-cosign-attest-cyclonedx-json"

// PredicateCycloneDXBeforeV05 is the PredicateCycloneDX value defined in in-toto-golang before v0.5.0.
// This is necessary for backward-compatible SBOM detection.
// ref. https://github.com/in-toto/in-toto-golang/pull/188
Expand Down Expand Up @@ -100,16 +105,41 @@ func DetectFormat(r io.ReadSeeker) (Format, error) {
}

// Try in-toto attestation
var s attestation.Statement
if err := json.NewDecoder(r).Decode(&s); err == nil {
if s.PredicateType == in_toto.PredicateCycloneDX || s.PredicateType == PredicateCycloneDXBeforeV05 {
return FormatAttestCycloneDXJSON, nil
}
format, ok := decodeAttestCycloneDXJSONFormat(r)
if ok {
return format, nil
}

return FormatUnknown, nil
}

func decodeAttestCycloneDXJSONFormat(r io.ReadSeeker) (Format, bool) {
var s attestation.Statement

if err := json.NewDecoder(r).Decode(&s); err != nil {
return "", false
}

if s.PredicateType != in_toto.PredicateCycloneDX && s.PredicateType != PredicateCycloneDXBeforeV05 {
return "", false
}

if s.Predicate == nil {
return "", false
}

m, ok := s.Predicate.(map[string]interface{})
if !ok {
return "", false
}

if _, ok := m["Data"]; ok {
return FormatLegacyCosignAttestCycloneDXJSON, true
}

return FormatAttestCycloneDXJSON, true
}

func Decode(f io.Reader, format Format) (types.SBOM, error) {
var (
v interface{}
Expand All @@ -122,6 +152,14 @@ func Decode(f io.Reader, format Format) (types.SBOM, error) {
v = &cyclonedx.CycloneDX{SBOM: &bom}
decoder = json.NewDecoder(f)
case FormatAttestCycloneDXJSON:
// dsse envelope
// => in-toto attestation
// => CycloneDX JSON
v = &attestation.Statement{
Predicate: &cyclonedx.CycloneDX{SBOM: &bom},
}
decoder = json.NewDecoder(f)
case FormatLegacyCosignAttestCycloneDXJSON:
// dsse envelope
// => in-toto attestation
// => cosign predicate
Expand Down

0 comments on commit ba9b041

Please sign in to comment.