github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/spdxjson/decoder.go (about) 1 package spdxjson 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "strings" 8 9 spdxJson "github.com/spdx/tools-golang/json" 10 11 "github.com/anchore/syft/syft/format/common/spdxhelpers" 12 "github.com/anchore/syft/syft/sbom" 13 "github.com/lineaje-labs/syft/internal/log" 14 ) 15 16 var _ sbom.FormatDecoder = (*decoder)(nil) 17 18 type decoder struct { 19 } 20 21 func NewFormatDecoder() sbom.FormatDecoder { 22 return decoder{} 23 } 24 25 func (d decoder) Decode(reader io.ReadSeeker) (*sbom.SBOM, sbom.FormatID, string, error) { 26 if reader == nil { 27 return nil, "", "", fmt.Errorf("no SBOM bytes provided") 28 } 29 30 // since spdx lib will always return the latest version of the document, we need to identify the version 31 // first and then decode into the appropriate document object. Otherwise if we get the version info from the 32 // decoded object we will always get the latest version (instead of the version we decoded from). 33 id, version := d.Identify(reader) 34 if id != ID { 35 return nil, "", "", fmt.Errorf("not a spdx json document") 36 } 37 if version == "" { 38 return nil, "", "", fmt.Errorf("unsupported spdx json document version") 39 } 40 41 if _, err := reader.Seek(0, io.SeekStart); err != nil { 42 return nil, "", "", fmt.Errorf("unable to seek to start of SPDX JSON SBOM: %+v", err) 43 } 44 45 doc, err := spdxJson.Read(reader) 46 if err != nil { 47 return nil, id, version, fmt.Errorf("unable to decode spdx json: %w", err) 48 } 49 50 s, err := spdxhelpers.ToSyftModel(doc) 51 if err != nil { 52 return nil, id, version, err 53 } 54 return s, id, version, nil 55 } 56 57 func (d decoder) Identify(reader io.ReadSeeker) (sbom.FormatID, string) { 58 if reader == nil { 59 return "", "" 60 } 61 62 if _, err := reader.Seek(0, io.SeekStart); err != nil { 63 log.Debugf("unable to seek to start of SPDX JSON SBOM: %+v", err) 64 return "", "" 65 } 66 67 // Example JSON document 68 // { 69 // "spdxVersion": "SPDX-2.3", 70 // ... 71 type Document struct { 72 SPDXVersion string `json:"spdxVersion"` 73 } 74 75 dec := json.NewDecoder(reader) 76 77 var doc Document 78 err := dec.Decode(&doc) 79 if err != nil { 80 // maybe not json? maybe not valid? doesn't matter, we won't process it. 81 return "", "" 82 } 83 84 id, version := getFormatInfo(doc.SPDXVersion) 85 if version == "" || id != ID { 86 // not a spdx json document that we support 87 return "", "" 88 } 89 90 return id, version 91 } 92 93 func getFormatInfo(spdxVersion string) (sbom.FormatID, string) { 94 // example input: SPDX-2.3 95 if !strings.HasPrefix(strings.ToLower(spdxVersion), "spdx-") { 96 return "", "" 97 } 98 fields := strings.Split(spdxVersion, "-") 99 if len(fields) != 2 { 100 return ID, "" 101 } 102 103 return ID, fields[1] 104 }