@@ -31,14 +31,33 @@ import (
31
31
"github.com/rs/zerolog"
32
32
)
33
33
34
- // containerComponentKind is the kind of the main component when it's a container
35
- const containerComponentKind = "container"
34
+ const (
35
+ // containerComponentKind is the kind of the main component when it's a container
36
+ containerComponentKind = "container"
37
+ // aquaTrivyRepoDigestPropertyKey is the key used by Aqua Trivy to store the repo digest
38
+ aquaTrivyRepoDigestPropertyKey = "aquasecurity:trivy:RepoDigest"
39
+ )
36
40
37
41
type CyclonedxJSONCrafter struct {
38
42
backend * casclient.CASBackend
39
43
* crafterCommon
40
44
}
41
45
46
+ // mainComponentStruct internal struct to unmarshall the incoming CycloneDX JSON
47
+ type mainComponentStruct struct {
48
+ Metadata struct {
49
+ Component struct {
50
+ Name string `json:"name"`
51
+ Type string `json:"type"`
52
+ Version string `json:"version"`
53
+ Properties []struct {
54
+ Name string `json:"name"`
55
+ Value string `json:"value"`
56
+ } `json:"properties"`
57
+ } `json:"component"`
58
+ } `json:"metadata"`
59
+ }
60
+
42
61
func NewCyclonedxJSONCrafter (materialSchema * schemaapi.CraftingSchema_Material , backend * casclient.CASBackend , l * zerolog.Logger ) (* CyclonedxJSONCrafter , error ) {
43
62
if materialSchema .Type != schemaapi .CraftingSchema_Material_SBOM_CYCLONEDX_JSON {
44
63
return nil , fmt .Errorf ("material type is not cyclonedx json" )
@@ -101,24 +120,26 @@ func (i *CyclonedxJSONCrafter) Craft(ctx context.Context, filePath string) (*api
101
120
102
121
// extractMainComponent inspects the SBOM and extracts the main component if any and available
103
122
func (i * CyclonedxJSONCrafter ) extractMainComponent (rawFile []byte ) (* SBOMMainComponentInfo , error ) {
104
- // Define the structure of the main component in the SBOM locally to perform an unmarshal
105
- type mainComponentStruct struct {
106
- Metadata struct {
107
- Component struct {
108
- Name string `json:"name"`
109
- Type string `json:"type"`
110
- Version string `json:"version"`
111
- } `json:"component"`
112
- } `json:"metadata"`
113
- }
114
-
115
123
var mainComponent mainComponentStruct
116
124
err := json .Unmarshal (rawFile , & mainComponent )
117
125
if err != nil {
118
126
return nil , fmt .Errorf ("error extracting main component: %w" , err )
119
127
}
120
128
121
129
component := mainComponent .Metadata .Component
130
+
131
+ // If the version is empty, try to extract it from the properties
132
+ if component .Version == "" {
133
+ for _ , prop := range component .Properties {
134
+ if prop .Name == aquaTrivyRepoDigestPropertyKey {
135
+ if parts := strings .Split (prop .Value , "sha256:" ); len (parts ) == 2 {
136
+ component .Version = fmt .Sprintf ("sha256:%s" , parts [1 ])
137
+ break
138
+ }
139
+ }
140
+ }
141
+ }
142
+
122
143
if component .Type != containerComponentKind {
123
144
return & SBOMMainComponentInfo {
124
145
name : component .Name ,
@@ -129,13 +150,13 @@ func (i *CyclonedxJSONCrafter) extractMainComponent(rawFile []byte) (*SBOMMainCo
129
150
130
151
// Standardize the name to have the full repository name including the registry and
131
152
// sanitize the name to remove the possible tag from the repository name
132
- stdName , err := remotename .NewRepository ( strings . Split ( component .Name , ":" )[ 0 ] )
153
+ ref , err := remotename .ParseReference ( component .Name )
133
154
if err != nil {
134
155
return nil , fmt .Errorf ("couldn't parse OCI image repository name: %w" , err )
135
156
}
136
157
137
158
return & SBOMMainComponentInfo {
138
- name : stdName .String (),
159
+ name : ref . Context () .String (),
139
160
kind : component .Type ,
140
161
version : component .Version ,
141
162
}, nil
0 commit comments