diff --git a/go.mod b/go.mod index ce12af73f..8c988e1bc 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,7 @@ require ( require ( github.com/CycloneDX/cyclonedx-go v0.7.1 + github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 github.com/anchore/stereoscope v0.0.0-20230412183729-8602f1afc574 diff --git a/go.sum b/go.sum index e96635aad..f7c61e853 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= diff --git a/syft/formats/syftjson/decoder.go b/syft/formats/syftjson/decoder.go index b6286bd97..845c1fc0b 100644 --- a/syft/formats/syftjson/decoder.go +++ b/syft/formats/syftjson/decoder.go @@ -5,6 +5,10 @@ import ( "fmt" "io" + "github.com/Masterminds/semver" + + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/formats/syftjson/model" "github.com/anchore/syft/syft/sbom" ) @@ -18,5 +22,27 @@ func decoder(reader io.Reader) (*sbom.SBOM, error) { return nil, fmt.Errorf("unable to decode syft-json: %w", err) } + if err := checkSupportedSchema(doc.Schema.Version, internal.JSONSchemaVersion); err != nil { + log.Warn(err) + } + return toSyftModel(doc) } + +func checkSupportedSchema(documentVerion string, parserVersion string) error { + documentV, err := semver.NewVersion(documentVerion) + if err != nil { + return fmt.Errorf("error comparing document schema version with parser schema version: %w", err) + } + + parserV, err := semver.NewVersion(parserVersion) + if err != nil { + return fmt.Errorf("error comparing document schema version with parser schema version: %w", err) + } + + if documentV.GreaterThan(parserV) { + return fmt.Errorf("document has schema version %s, but parser has older schema version (%s)", documentVerion, parserVersion) + } + + return nil +} diff --git a/syft/formats/syftjson/decoder_test.go b/syft/formats/syftjson/decoder_test.go index de9ab7bcf..e0de5fffb 100644 --- a/syft/formats/syftjson/decoder_test.go +++ b/syft/formats/syftjson/decoder_test.go @@ -2,6 +2,8 @@ package syftjson import ( "bytes" + "errors" + "fmt" "strings" "testing" @@ -49,3 +51,38 @@ func TestEncodeDecodeCycle(t *testing.T) { } } } + +func TestOutOfDateParser(t *testing.T) { + tests := []struct { + name string + documentVersion string + parserVersion string + want error + }{{ + name: "no warning when doc version is older", + documentVersion: "1.0.9", + parserVersion: "3.1.0", + }, { + name: "warning when parser is older", + documentVersion: "4.3.2", + parserVersion: "3.1.0", + want: fmt.Errorf("document has schema version %s, but parser has older schema version (%s)", "4.3.2", "3.1.0"), + }, { + name: "warning when document version is unparseable", + documentVersion: "some-nonsense", + parserVersion: "3.1.0", + want: fmt.Errorf("error comparing document schema version with parser schema version: %w", errors.New("Invalid Semantic Version")), + }, { + name: "warning when parser version is unparseable", + documentVersion: "7.1.0", + parserVersion: "some-nonsense", + want: fmt.Errorf("error comparing document schema version with parser schema version: %w", errors.New("Invalid Semantic Version")), + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := checkSupportedSchema(tt.documentVersion, tt.parserVersion) + assert.Equal(t, tt.want, got) + }) + } +}