@@ -31,14 +31,33 @@ import (
3131 "github.com/rs/zerolog"
3232)
3333
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+ )
3640
3741type CyclonedxJSONCrafter struct {
3842 backend * casclient.CASBackend
3943 * crafterCommon
4044}
4145
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+
4261func NewCyclonedxJSONCrafter (materialSchema * schemaapi.CraftingSchema_Material , backend * casclient.CASBackend , l * zerolog.Logger ) (* CyclonedxJSONCrafter , error ) {
4362 if materialSchema .Type != schemaapi .CraftingSchema_Material_SBOM_CYCLONEDX_JSON {
4463 return nil , fmt .Errorf ("material type is not cyclonedx json" )
@@ -101,24 +120,26 @@ func (i *CyclonedxJSONCrafter) Craft(ctx context.Context, filePath string) (*api
101120
102121// extractMainComponent inspects the SBOM and extracts the main component if any and available
103122func (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-
115123 var mainComponent mainComponentStruct
116124 err := json .Unmarshal (rawFile , & mainComponent )
117125 if err != nil {
118126 return nil , fmt .Errorf ("error extracting main component: %w" , err )
119127 }
120128
121129 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+
122143 if component .Type != containerComponentKind {
123144 return & SBOMMainComponentInfo {
124145 name : component .Name ,
@@ -129,13 +150,13 @@ func (i *CyclonedxJSONCrafter) extractMainComponent(rawFile []byte) (*SBOMMainCo
129150
130151 // Standardize the name to have the full repository name including the registry and
131152 // 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 )
133154 if err != nil {
134155 return nil , fmt .Errorf ("couldn't parse OCI image repository name: %w" , err )
135156 }
136157
137158 return & SBOMMainComponentInfo {
138- name : stdName .String (),
159+ name : ref . Context () .String (),
139160 kind : component .Type ,
140161 version : component .Version ,
141162 }, nil
0 commit comments