github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/format/syftjson/decoder.go (about) 1 package syftjson 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "strings" 8 9 "github.com/Masterminds/semver" 10 11 "github.com/anchore/syft/internal" 12 "github.com/anchore/syft/internal/log" 13 "github.com/anchore/syft/syft/format/internal/stream" 14 "github.com/anchore/syft/syft/format/syftjson/model" 15 "github.com/anchore/syft/syft/sbom" 16 ) 17 18 var _ sbom.FormatDecoder = (*decoder)(nil) 19 20 type decoder struct{} 21 22 func NewFormatDecoder() sbom.FormatDecoder { 23 return decoder{} 24 } 25 26 func (d decoder) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) { 27 reader, err := stream.SeekableReader(r) 28 if err != nil { 29 return nil, "", "", err 30 } 31 32 id, version := d.Identify(reader) 33 if version == "" || id != ID { 34 return nil, "", "", fmt.Errorf("not a syft-json document") 35 } 36 var doc model.Document 37 38 if _, err := reader.Seek(0, io.SeekStart); err != nil { 39 return nil, "", "", fmt.Errorf("unable to seek to start of Syft JSON SBOM: %+v", err) 40 } 41 42 dec := json.NewDecoder(reader) 43 44 if err = dec.Decode(&doc); err != nil { 45 return nil, "", "", fmt.Errorf("unable to decode syft-json document: %w", err) 46 } 47 48 if err := checkSupportedSchema(doc.Schema.Version, internal.JSONSchemaVersion); err != nil { 49 log.Warn(err) 50 } 51 52 return toSyftModel(doc), ID, doc.Schema.Version, nil 53 } 54 55 func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) { 56 reader, err := stream.SeekableReader(r) 57 if err != nil { 58 return "", "" 59 } 60 61 if _, err := reader.Seek(0, io.SeekStart); err != nil { 62 log.Debugf("unable to seek to start of Syft JSON SBOM: %+v", err) 63 return "", "" 64 } 65 66 type Document struct { 67 Schema model.Schema `json:"schema"` 68 } 69 70 dec := json.NewDecoder(reader) 71 72 var doc Document 73 if err = dec.Decode(&doc); err != nil { 74 // maybe not json? maybe not valid? doesn't matter, we won't process it. 75 return "", "" 76 } 77 78 if !strings.Contains(doc.Schema.URL, "anchore/syft") { 79 // not a syft-json document 80 return "", "" 81 } 82 83 // note: we support all previous schema versions 84 return ID, doc.Schema.Version 85 } 86 87 func checkSupportedSchema(documentVersion string, parserVersion string) error { 88 documentV, err := semver.NewVersion(documentVersion) 89 if err != nil { 90 return fmt.Errorf("error comparing document schema version with parser schema version: %w", err) 91 } 92 93 parserV, err := semver.NewVersion(parserVersion) 94 if err != nil { 95 return fmt.Errorf("error comparing document schema version with parser schema version: %w", err) 96 } 97 98 if documentV.GreaterThan(parserV) { 99 return fmt.Errorf("document has schema version %s, but parser has older schema version (%s)", documentVersion, parserVersion) 100 } 101 102 return nil 103 }