44 "context"
55 "errors"
66 "fmt"
7+ "strings"
78
9+ ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
810 smithy "github.com/aws/smithy-go"
911 "github.com/loilo-inc/canarycage/v5/awsiface"
1012)
@@ -55,11 +57,7 @@ func scanImage(ctx context.Context, ecrTool EcrTool, info ImageInfo) ScanResult
5557 } else if findings , err := ecrTool .GetImageScanFindings (ctx , & info , imageID ); err != nil {
5658 return ScanResult {ImageInfo : info , Err : parseError (err )}
5759 } else {
58- var cves []CVE
59- for _ , f := range findings .Findings {
60- cve := findingToCVE (f )
61- cves = append (cves , cve )
62- }
60+ cves := scanFindingsToCVEs (findings )
6361 return ScanResult {ImageInfo : info , Cves : cves }
6462 }
6563}
@@ -71,3 +69,134 @@ func parseError(err error) error {
7169 }
7270 return err
7371}
72+
73+ func scanFindingsToCVEs (findings * ecrtypes.ImageScanFindings ) []CVE {
74+ var cves []CVE
75+ if findings .Findings != nil {
76+ for _ , f := range findings .Findings {
77+ cves = append (cves , findingToCVE (f ))
78+ }
79+ return cves
80+ } else if findings .EnhancedFindings != nil {
81+ for _ , f := range findings .EnhancedFindings {
82+ cves = append (cves , enhancedFindingToCVE (f ))
83+ }
84+ }
85+ return cves
86+ }
87+
88+ func findingToCVE (finding ecrtypes.ImageScanFinding ) CVE {
89+ cve := CVE {
90+ Name : "unknown" ,
91+ PackageName : "unknown" ,
92+ PackageVersion : "unknown" ,
93+ Severity : finding .Severity ,
94+ }
95+ if finding .Name != nil {
96+ cve .Name = * finding .Name
97+ }
98+ attrs := unwrapAttributes (finding .Attributes )
99+ if val , ok := attrs ["package_name" ]; ok {
100+ cve .PackageName = val
101+ }
102+ if val , ok := attrs ["package_version" ]; ok {
103+ cve .PackageVersion = val
104+ }
105+ if finding .Uri != nil {
106+ cve .Uri = * finding .Uri
107+ }
108+ if finding .Description != nil {
109+ cve .Description = * finding .Description
110+ }
111+ return cve
112+ }
113+
114+ func enhancedFindingToCVE (finding ecrtypes.EnhancedImageScanFinding ) CVE {
115+ analysis := & EnchancedAnalysis {Score : finding .Score }
116+ cve := CVE {
117+ Name : "unknown" ,
118+ PackageName : "unknown" ,
119+ PackageVersion : "unknown" ,
120+ Severity : ecrtypes .FindingSeverityUndefined ,
121+ EnchancedAnalysis : analysis ,
122+ }
123+ if finding .Description != nil {
124+ cve .Description = * finding .Description
125+ }
126+ if finding .Severity != nil {
127+ cve .Severity = ecrtypes .FindingSeverity (* finding .Severity )
128+ }
129+ if finding .Status != nil {
130+ analysis .Status = * finding .Status
131+ }
132+ if finding .ExploitAvailable != nil {
133+ analysis .ExploitAvailable = * finding .ExploitAvailable
134+ }
135+ if finding .FixAvailable != nil {
136+ analysis .FixAvailable = * finding .FixAvailable
137+ }
138+ if finding .PackageVulnerabilityDetails == nil {
139+ return cve
140+ }
141+ details := finding .PackageVulnerabilityDetails
142+ if details .VulnerabilityId != nil {
143+ cve .Name = * details .VulnerabilityId
144+ }
145+ if cve .Severity == ecrtypes .FindingSeverityUndefined && details .VendorSeverity != nil {
146+ cve .Severity = ecrtypes .FindingSeverity (* details .VendorSeverity )
147+ }
148+ if details .SourceUrl != nil {
149+ cve .Uri = * details .SourceUrl
150+ } else if len (details .ReferenceUrls ) > 0 {
151+ cve .Uri = details .ReferenceUrls [0 ]
152+ }
153+ cve .PackageName , cve .PackageVersion = vulnerablePackagesToNameVersion (details .VulnerablePackages )
154+ analysis .FixedInVersion = strings .Join (uniquePackageValues (details .VulnerablePackages , func (pkg ecrtypes.VulnerablePackage ) * string {
155+ return pkg .FixedInVersion
156+ }), ", " )
157+ return cve
158+ }
159+
160+ func unwrapAttributes (attrs []ecrtypes.Attribute ) map [string ]string {
161+ m := make (map [string ]string )
162+ for _ , attr := range attrs {
163+ if attr .Key != nil && attr .Value != nil {
164+ m [* attr .Key ] = * attr .Value
165+ }
166+ }
167+ return m
168+ }
169+
170+ func vulnerablePackagesToNameVersion (packages []ecrtypes.VulnerablePackage ) (string , string ) {
171+ names := uniquePackageValues (packages , func (pkg ecrtypes.VulnerablePackage ) * string {
172+ return pkg .Name
173+ })
174+ versions := uniquePackageValues (packages , func (pkg ecrtypes.VulnerablePackage ) * string {
175+ return pkg .Version
176+ })
177+ return joinOrUnknown (names ), joinOrUnknown (versions )
178+ }
179+
180+ func uniquePackageValues (packages []ecrtypes.VulnerablePackage , value func (ecrtypes.VulnerablePackage ) * string ) []string {
181+ seen := make (map [string ]struct {})
182+ var values []string
183+ for _ , pkg := range packages {
184+ v := value (pkg )
185+ if v == nil || * v == "" {
186+ continue
187+ }
188+ if _ , ok := seen [* v ]; ok {
189+ continue
190+ }
191+ seen [* v ] = struct {}{}
192+ values = append (values , * v )
193+ }
194+ return values
195+ }
196+
197+ func joinOrUnknown (values []string ) string {
198+ if len (values ) == 0 {
199+ return "unknown"
200+ }
201+ return strings .Join (values , ", " )
202+ }
0 commit comments