diff --git a/internal/formats/formats.go b/internal/formats/formats.go index 6fe7c47bf4f..5c82bd6533a 100644 --- a/internal/formats/formats.go +++ b/internal/formats/formats.go @@ -16,9 +16,10 @@ func All() []format.Format { func Identify(by []byte) (*format.Format, error) { for _, f := range All() { - if f.Detect(bytes.NewReader(by)) { - return &f, nil + if err := f.Validate(bytes.NewReader(by)); err != nil { + continue } + return &f, nil } return nil, nil } diff --git a/internal/formats/formats_test.go b/internal/formats/formats_test.go index 42c6f45b51d..3df97558c5f 100644 --- a/internal/formats/formats_test.go +++ b/internal/formats/formats_test.go @@ -10,7 +10,6 @@ import ( ) func TestIdentify(t *testing.T) { - tests := []struct { fixture string expected format.Option diff --git a/internal/formats/syftjson/model/source.go b/internal/formats/syftjson/model/source.go index 182be233d72..5f7ef47b5e7 100644 --- a/internal/formats/syftjson/model/source.go +++ b/internal/formats/syftjson/model/source.go @@ -48,6 +48,7 @@ func (s *Source) UnmarshalJSON(b []byte) error { return err } s.Target = payload + default: return fmt.Errorf("unsupported package metadata type: %+v", s.Type) } diff --git a/internal/formats/syftjson/validator.go b/internal/formats/syftjson/validator.go index 1adcb90d461..d359b60d940 100644 --- a/internal/formats/syftjson/validator.go +++ b/internal/formats/syftjson/validator.go @@ -22,7 +22,7 @@ func validator(reader io.Reader) error { return fmt.Errorf("unable to decode: %w", err) } - // note: we accept al schema versions + // note: we accept all schema versions // TODO: add per-schema version parsing if strings.Contains(doc.Schema.URL, "anchore/syft") { return nil diff --git a/syft/format/decoder.go b/syft/format/decoder.go index 3a00862dd3a..a48c3c98bff 100644 --- a/syft/format/decoder.go +++ b/syft/format/decoder.go @@ -9,4 +9,5 @@ import ( "github.com/anchore/syft/syft/source" ) +// Decoder is a function that can convert an SBOM document of a specific format from a reader into Syft native objects. type Decoder func(reader io.Reader) (*pkg.Catalog, *source.Metadata, *distro.Distro, source.Scope, error) diff --git a/syft/format/encoder.go b/syft/format/encoder.go index 03c72231d8f..9f253c342d4 100644 --- a/syft/format/encoder.go +++ b/syft/format/encoder.go @@ -8,4 +8,5 @@ import ( "github.com/anchore/syft/syft/source" ) +// Encoder is a function that can transform Syft native objects into an SBOM document of a specific format written to the given writer. type Encoder func(io.Writer, *pkg.Catalog, *source.Metadata, *distro.Distro, source.Scope) error diff --git a/syft/format/format.go b/syft/format/format.go index 862012be4d1..ad78fb0aa4a 100644 --- a/syft/format/format.go +++ b/syft/format/format.go @@ -11,8 +11,9 @@ import ( ) var ( - ErrEncodingNotSupported = errors.New("encoding not supported") - ErrDecodingNotSupported = errors.New("decoding not supported") + ErrEncodingNotSupported = errors.New("encoding not supported") + ErrDecodingNotSupported = errors.New("decoding not supported") + ErrValidationNotSupported = errors.New("validation not supported") ) type Format struct { @@ -45,15 +46,12 @@ func (f Format) Decode(reader io.Reader) (*pkg.Catalog, *source.Metadata, *distr return f.decoder(reader) } -func (f Format) Detect(reader io.Reader) bool { +func (f Format) Validate(reader io.Reader) error { if f.validator == nil { - return false + return ErrValidationNotSupported } - if err := f.validator(reader); err != nil { - return false - } - return true + return f.validator(reader) } func (f Format) Presenter(catalog *pkg.Catalog, metadata *source.Metadata, d *distro.Distro, scope source.Scope) *Presenter { diff --git a/syft/format/validator.go b/syft/format/validator.go index 59269e27125..6559186b091 100644 --- a/syft/format/validator.go +++ b/syft/format/validator.go @@ -2,4 +2,11 @@ package format import "io" +// Validator reads the SBOM from the given reader and assesses whether the document conforms to the specific SBOM format. +// The validator should positively confirm if the SBOM is not only the format but also has the minimal set of values +// that the format requires. For example, all syftjson formatted documents have a schema section which should have +// "anchore/syft" within the version --if this isn't found then the validator should raise an error. These active +// assertions protect against "simple" format decoding validations that may lead to false positives (e.g. I decoded +// json successfully therefore this must be the target format, however, all values are their default zero-value and +// really represent a different format that also uses json) type Validator func(reader io.Reader) error