-
Notifications
You must be signed in to change notification settings - Fork 551
/
decoder.go
129 lines (106 loc) · 2.92 KB
/
decoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package cyclonedxjson
import (
"encoding/json"
"fmt"
"io"
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/format/internal/cyclonedxutil"
"github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers"
"github.com/anchore/syft/syft/format/internal/stream"
"github.com/anchore/syft/syft/sbom"
)
var _ sbom.FormatDecoder = (*decoder)(nil)
type decoder struct {
decoder cyclonedxutil.Decoder
}
func NewFormatDecoder() sbom.FormatDecoder {
return decoder{
decoder: cyclonedxutil.NewDecoder(cyclonedx.BOMFileFormatJSON),
}
}
func (d decoder) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) {
reader, err := stream.SeekableReader(r)
if err != nil {
return nil, "", "", err
}
id, version := d.Identify(reader)
if id != ID {
return nil, "", "", fmt.Errorf("not a cyclonedx json document")
}
if version == "" {
return nil, "", "", fmt.Errorf("unsupported cyclonedx json document version")
}
doc, err := d.decoder.Decode(reader)
if err != nil {
return nil, id, version, fmt.Errorf("unable to decode cyclonedx json document: %w", err)
}
s, err := helpers.ToSyftModel(doc)
if err != nil {
return nil, id, version, err
}
return s, id, version, nil
}
func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) {
reader, err := stream.SeekableReader(r)
if err != nil {
return "", ""
}
if _, err := reader.Seek(0, io.SeekStart); err != nil {
log.Debugf("unable to seek to start of CycloneDX JSON SBOM: %+v", err)
return "", ""
}
type Document struct {
JSONSchema string `json:"$schema"`
BOMFormat string `json:"bomFormat"`
SpecVersion string `json:"specVersion"`
}
dec := json.NewDecoder(reader)
var doc Document
if err = dec.Decode(&doc); err != nil {
// maybe not json? maybe not valid? doesn't matter, we won't process it.
return "", ""
}
id, version := getFormatInfo(doc.BOMFormat, doc.SpecVersion)
if version == "" || id != ID {
// not a cyclonedx json document that we support
return "", ""
}
return id, version
}
func getFormatInfo(bomFormat string, specVersion any) (sbom.FormatID, string) {
if bomFormat != "CycloneDX" {
// not a cyclonedx json document
return "", ""
}
// by this point, it looks to be cyclonedx json, but we need to know the version
var (
version string
spec cyclonedx.SpecVersion
err error
)
switch s := specVersion.(type) {
case string:
version = s
spec, err = cyclonedxutil.SpecVersionFromString(version)
if err != nil {
// not a supported version, but is cyclonedx json
return ID, ""
}
case cyclonedx.SpecVersion:
spec = s
version = cyclonedxutil.VersionFromSpecVersion(spec)
if version == "" {
// not a supported version, but is cyclonedx json
return ID, ""
}
default:
// bad input provided for version info
return ID, ""
}
if spec < 0 {
// not a supported version, but is cyclonedx json
return ID, ""
}
return ID, version
}