github.com/anchore/syft@v1.38.2/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/v3" 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 if r == nil { 57 return "", "" 58 } 59 60 type Document struct { 61 Schema model.Schema `json:"schema"` 62 } 63 64 dec := json.NewDecoder(r) 65 66 var doc Document 67 if err := dec.Decode(&doc); err != nil { 68 // maybe not json? maybe not valid? doesn't matter, we won't process it. 69 return "", "" 70 } 71 72 if !strings.Contains(doc.Schema.URL, "anchore/syft") { 73 // not a syft-json document 74 return "", "" 75 } 76 77 // note: we support all previous schema versions 78 return ID, doc.Schema.Version 79 } 80 81 func checkSupportedSchema(documentVersion string, parserVersion string) error { 82 documentV, err := semver.NewVersion(documentVersion) 83 if err != nil { 84 return fmt.Errorf("error comparing document schema version with parser schema version: %w", err) 85 } 86 87 parserV, err := semver.NewVersion(parserVersion) 88 if err != nil { 89 return fmt.Errorf("error comparing document schema version with parser schema version: %w", err) 90 } 91 92 if documentV.GreaterThan(parserV) { 93 return fmt.Errorf("document has schema version %s, but parser has older schema version (%s)", documentVersion, parserVersion) 94 } 95 96 return nil 97 }