@@ -10,144 +10,92 @@ import { connection } from '../server';
1010import { globalConfig } from '../config' ;
1111import { isDefined , decodeUriPath } from '../utils' ;
1212import { IImage } from '../imageAnalysis/collector' ;
13+ import { AnalysisReport } from '@trustification/exhort-api-spec/model/v4/AnalysisReport' ;
14+ import { DependencyReport } from '@trustification/exhort-api-spec/model/v4/DependencyReport' ;
15+ import { SourceSummary } from '@trustification/exhort-api-spec/model/v4/SourceSummary' ;
16+ import { ProviderReport } from '@trustification/exhort-api-spec/model/v4/ProviderReport' ;
17+ import { Source } from '@trustification/exhort-api-spec/model/v4/Source' ;
1318
1419/**
1520 * Represents the Red Hat Dependency Analytics (RHDA) analysis report, with images mapped by string keys.
1621 */
1722interface IExhortAnalysisReport {
18- [ key : string ] : IImageReport ;
19- }
20-
21- /**
22- * Represents the RHDA analysis report for a single image.
23- */
24- interface IImageReport {
25- providers : Map < string , IProvider > ;
26- }
27-
28- /**
29- * Represents a provider of dependencies for an image.
30- */
31- interface IProvider {
32- status : IStatus ;
33- sources : Map < string , ISource > ;
34- }
35-
36- /**
37- * Represents the status of a provider.
38- */
39- interface IStatus {
40- ok : boolean ;
41- }
42-
43- /**
44- * Represents a source of dependencies.
45- */
46- interface ISource {
47- summary : ISummary ;
48- dependencies : ISourceDependency [ ] ;
49- }
50-
51- /**
52- * Represents the summary of vulnerabilities for a source.
53- */
54- interface ISummary {
55- total : number ,
56- critical : number ,
57- high : number ,
58- medium : number ,
59- low : number ,
60- }
61-
62- /**
63- * Represents a dependency reported by a source.
64- */
65- interface ISourceDependency {
66- recommendation : string | null ;
23+ [ key : string ] : AnalysisReport ;
6724}
6825
6926/**
7027 * Represents data collected related to an image.
7128 */
7229interface IArtifact {
7330 id : string ;
74- summary : ISummary ;
75- dependencies : ISourceDependency [ ] ;
31+ summary : SourceSummary ;
32+ dependencies : DependencyReport [ ] ;
7633}
7734
78- /**
79- * Represents data specification related to an image.
80- */
81- interface IImageData {
82- sourceId : string ;
83- issuesCount : number ;
84- recommendationRef : string ;
85- highestVulnerabilitySeverity : string ;
86- }
87-
88- /**
89- * Implementation of IImageData interface.
90- */
91- class ImageData implements IImageData {
35+ class ImageData {
9236 constructor (
93- public sourceId : string ,
94- public issuesCount : number ,
95- public recommendationRef : string ,
96- public highestVulnerabilitySeverity : string
37+ public sourceId : string ,
38+ public issuesCount : number ,
39+ public recommendationRef : string ,
40+ public highestVulnerabilitySeverity : string
9741 ) { }
9842}
9943
10044/**
101- * Represents the parsed response of Red Hat Dependency Analytics (RHDA) analysis report, with images mapped by string keys.
102- */
45+ * Represents the parsed response of Red Hat Dependency Analytics (RHDA) analysis report, with images mapped by string keys.
46+ */
10347interface IAnalysisResponse {
10448 images : Map < string , ImageData [ ] > ;
10549}
10650
10751/**
108- * Implementation of IAnalysisResponse interface.
109- */
52+ * Implementation of IAnalysisResponse interface.
53+ */
11054class AnalysisResponse implements IAnalysisResponse {
11155 images : Map < string , ImageData [ ] > = new Map < string , ImageData [ ] > ( ) ;
11256
11357 constructor ( resData : IExhortAnalysisReport , diagnosticFilePath : string ) {
114- const failedProviders : string [ ] = [ ] ;
115-
116- Object . entries ( resData ) . map ( ( [ imageRef , imageData ] ) => {
117- const artifacts : IArtifact [ ] = [ ] ;
118-
119- if ( isDefined ( imageData , 'providers' ) ) {
120- Object . entries ( imageData . providers ) . map ( ( [ providerName , providerData ] : [ string , IProvider ] ) => {
121- if ( isDefined ( providerData , 'status' , 'ok' ) && providerData . status . ok ) {
122- if ( isDefined ( providerData , 'sources' ) ) {
123- Object . entries ( providerData . sources ) . map ( ( [ sourceName , sourceData ] : [ string , ISource ] ) => {
124- if ( isDefined ( sourceData , 'summary' ) ) {
125- artifacts . push ( { id : `${ providerName } (${ sourceName } )` , summary : sourceData . summary , dependencies : sourceData . dependencies } ) ;
126- }
127- } ) ;
128- }
129- } else {
130- failedProviders . push ( providerName ) ;
58+ const failedProviders : string [ ] = [ ] ;
59+
60+ Object . entries ( resData ) . map ( ( [ imageRef , imageData ] ) => {
61+ const artifacts : IArtifact [ ] = [ ] ;
62+
63+ if ( isDefined ( imageData , 'providers' ) ) {
64+ Object . entries ( imageData . providers ) . map ( ( [ providerName , providerData ] : [ string , ProviderReport ] ) => {
65+ if ( providerData ?. status ?. ok ) {
66+ if ( isDefined ( providerData , 'sources' ) ) {
67+ Object . entries ( providerData . sources ) . map ( ( [ sourceName , sourceData ] : [ string , Source ] ) => {
68+ if ( isDefined ( sourceData , 'summary' ) ) {
69+ artifacts . push ( {
70+ id : `${ providerName } (${ sourceName } )` ,
71+ summary : sourceData . summary ,
72+ dependencies : sourceData . dependencies ,
73+ } ) ;
13174 }
132- } ) ;
133-
134- artifacts . forEach ( artifact => {
135- const sd = new ImageData ( artifact . id , this . getTotalIssues ( artifact . summary ) , this . getRecommendation ( artifact . dependencies ) , this . getHighestSeverity ( artifact . summary ) ) ;
136-
137- this . images [ imageRef ] = this . images [ imageRef ] || [ ] ;
138- this . images [ imageRef ] . push ( sd ) ;
139- } ) ;
140- }
75+ } ) ;
76+ }
77+ } else {
78+ failedProviders . push ( providerName ) ;
79+ }
80+ } ) ;
81+
82+ artifacts . forEach ( artifact => {
83+ const sd = new ImageData ( artifact . id , this . getTotalIssues ( artifact . summary ) , this . getRecommendation ( artifact . dependencies ) , this . getHighestSeverity ( artifact . summary ) ) ;
84+
85+ this . images [ imageRef ] = this . images [ imageRef ] || [ ] ;
86+ this . images [ imageRef ] . push ( sd ) ;
87+ } ) ;
88+ }
14189
142- if ( failedProviders . length !== 0 ) {
143- const uniqueFailedProviders = Array . from ( new Set ( failedProviders ) ) ;
144- const errMsg = `The image component analysis couldn't fetch data from the following providers: [${ uniqueFailedProviders . join ( ', ' ) } ]` ;
145- connection . console . warn ( `Component Analysis Error: ${ errMsg } ` ) ;
146- connection . sendNotification ( 'caError' , {
147- errorMessage : errMsg ,
148- uri : decodeUriPath ( diagnosticFilePath ) ,
149- } ) ;
150- }
90+ if ( failedProviders . length !== 0 ) {
91+ const uniqueFailedProviders = Array . from ( new Set ( failedProviders ) ) ;
92+ const errMsg = `The image component analysis couldn't fetch data from the following providers: [${ uniqueFailedProviders . join ( ', ' ) } ]` ;
93+ connection . console . warn ( `Component Analysis Error: ${ errMsg } ` ) ;
94+ connection . sendNotification ( 'caError' , {
95+ errorMessage : errMsg ,
96+ uri : decodeUriPath ( diagnosticFilePath ) ,
97+ } ) ;
98+ }
15199 } ) ;
152100 }
153101
@@ -157,8 +105,8 @@ class AnalysisResponse implements IAnalysisResponse {
157105 * @returns The total number of issues.
158106 * @private
159107 */
160- private getTotalIssues ( summary : any ) : number {
161- return isDefined ( summary , 'total' ) ? summary . total : 0 ;
108+ private getTotalIssues ( summary : any ) : number {
109+ return isDefined ( summary , 'total' ) ? summary . total : 0 ;
162110 }
163111
164112 /**
@@ -168,19 +116,19 @@ class AnalysisResponse implements IAnalysisResponse {
168116 * @private
169117 */
170118 private getHighestSeverity ( summary : any ) : string {
171- let highestSeverity = 'NONE' ;
172-
173- if ( isDefined ( summary , 'critical' ) && summary . critical > 0 ) {
174- highestSeverity = 'CRITICAL' ;
175- } else if ( isDefined ( summary , 'high' ) && summary . high > 0 ) {
176- highestSeverity = 'HIGH' ;
177- } else if ( isDefined ( summary , 'medium' ) && summary . medium > 0 ) {
178- highestSeverity = 'MEDIUM' ;
179- } else if ( isDefined ( summary , 'low' ) && summary . low > 0 ) {
180- highestSeverity = 'LOW' ;
181- }
119+ let highestSeverity = 'NONE' ;
120+
121+ if ( isDefined ( summary , 'critical' ) && summary . critical > 0 ) {
122+ highestSeverity = 'CRITICAL' ;
123+ } else if ( isDefined ( summary , 'high' ) && summary . high > 0 ) {
124+ highestSeverity = 'HIGH' ;
125+ } else if ( isDefined ( summary , 'medium' ) && summary . medium > 0 ) {
126+ highestSeverity = 'MEDIUM' ;
127+ } else if ( isDefined ( summary , 'low' ) && summary . low > 0 ) {
128+ highestSeverity = 'LOW' ;
129+ }
182130
183- return highestSeverity ;
131+ return highestSeverity ;
184132 }
185133
186134 /**
@@ -189,10 +137,10 @@ class AnalysisResponse implements IAnalysisResponse {
189137 * @returns The recommendation reference or an empty string.
190138 * @private
191139 */
192- private getRecommendation ( dependencies : ISourceDependency [ ] ) : string {
193- let recommendation = '' ;
194- if ( dependencies && dependencies . length > 0 ) {
195- recommendation = isDefined ( dependencies [ 0 ] , 'recommendation' ) ? dependencies [ 0 ] . recommendation . split ( ':' ) [ 1 ] . split ( '@' ) [ 0 ] : '' ;
140+ private getRecommendation ( dependencies : DependencyReport [ ] ) : string {
141+ let recommendation = '' ;
142+ if ( dependencies && dependencies . length > 0 ) {
143+ recommendation = isDefined ( dependencies [ 0 ] , 'recommendation' ) ? dependencies [ 0 ] . recommendation . split ( ':' ) [ 1 ] . split ( '@' ) [ 0 ] : '' ;
196144 }
197145 return recommendation ;
198146 }
@@ -202,15 +150,15 @@ class AnalysisResponse implements IAnalysisResponse {
202150 * Represents the options for running image analysis.
203151 */
204152interface IOptions {
205- RHDA_TOKEN : string ;
206- RHDA_SOURCE : string ;
207- EXHORT_SYFT_PATH : string ;
208- EXHORT_SYFT_CONFIG_PATH : string ;
209- EXHORT_SKOPEO_PATH : string ;
210- EXHORT_SKOPEO_CONFIG_PATH : string ;
211- EXHORT_DOCKER_PATH : string ;
212- EXHORT_PODMAN_PATH : string ;
213- EXHORT_IMAGE_PLATFORM : string ;
153+ RHDA_TOKEN : string ;
154+ RHDA_SOURCE : string ;
155+ EXHORT_SYFT_PATH : string ;
156+ EXHORT_SYFT_CONFIG_PATH : string ;
157+ EXHORT_SKOPEO_PATH : string ;
158+ EXHORT_SKOPEO_CONFIG_PATH : string ;
159+ EXHORT_DOCKER_PATH : string ;
160+ EXHORT_PODMAN_PATH : string ;
161+ EXHORT_IMAGE_PLATFORM : string ;
214162}
215163
216164/**
@@ -220,38 +168,38 @@ interface IOptions {
220168 * @returns A Promise resolving to the analysis response.
221169 */
222170async function imageAnalysisService ( images : IImage [ ] , options : IOptions ) : Promise < any > {
223- return await exhort . imageAnalysis ( images . map ( img => {
224- if ( img . platform ) {
225- return `${ img . name . value } ^^${ img . platform } ` ;
226- }
227- return img . name . value ;
228- } ) , true , options ) ;
229- }
230-
171+ return await exhort . imageAnalysis ( images . map ( img => {
172+ if ( img . platform ) {
173+ return `${ img . name . value } ^^${ img . platform } ` ;
174+ }
175+ return img . name . value ;
176+ } ) , false , options ) ;
177+ }
178+
231179/**
232180 * Performs RHDA image analysis on provided images.
233181 * @param diagnosticFilePath - The path to the image file to analyze.
234182 * @param images - The images to analyze.
235183 * @returns A Promise resolving to an AnalysisResponse object.
236184 */
237- async function executeImageAnalysis ( diagnosticFilePath : string , images : IImage [ ] ) : Promise < AnalysisResponse > {
238-
239- // Define configuration options for the component analysis request
240- const options : IOptions = {
241- 'RHDA_TOKEN' : globalConfig . telemetryId ,
242- 'RHDA_SOURCE' : globalConfig . utmSource ,
243- 'EXHORT_SYFT_PATH' : globalConfig . exhortSyftPath ,
244- 'EXHORT_SYFT_CONFIG_PATH' : globalConfig . exhortSyftConfigPath ,
245- 'EXHORT_SKOPEO_PATH' : globalConfig . exhortSkopeoPath ,
246- 'EXHORT_SKOPEO_CONFIG_PATH' : globalConfig . exhortSkopeoConfigPath ,
247- 'EXHORT_DOCKER_PATH' : globalConfig . exhortDockerPath ,
248- 'EXHORT_PODMAN_PATH' : globalConfig . exhortPodmanPath ,
249- 'EXHORT_IMAGE_PLATFORM' : globalConfig . exhortImagePlatform ,
250- } ;
251-
252- const imageAnalysisJson = await imageAnalysisService ( images , options ) ;
253-
254- return new AnalysisResponse ( imageAnalysisJson , diagnosticFilePath ) ;
185+ async function executeImageAnalysis ( diagnosticFilePath : string , images : IImage [ ] ) : Promise < AnalysisResponse > {
186+
187+ // Define configuration options for the component analysis request
188+ const options : IOptions = {
189+ 'RHDA_TOKEN' : globalConfig . telemetryId ,
190+ 'RHDA_SOURCE' : globalConfig . utmSource ,
191+ 'EXHORT_SYFT_PATH' : globalConfig . exhortSyftPath ,
192+ 'EXHORT_SYFT_CONFIG_PATH' : globalConfig . exhortSyftConfigPath ,
193+ 'EXHORT_SKOPEO_PATH' : globalConfig . exhortSkopeoPath ,
194+ 'EXHORT_SKOPEO_CONFIG_PATH' : globalConfig . exhortSkopeoConfigPath ,
195+ 'EXHORT_DOCKER_PATH' : globalConfig . exhortDockerPath ,
196+ 'EXHORT_PODMAN_PATH' : globalConfig . exhortPodmanPath ,
197+ 'EXHORT_IMAGE_PLATFORM' : globalConfig . exhortImagePlatform ,
198+ } ;
199+
200+ const imageAnalysisJson = await imageAnalysisService ( images , options ) ;
201+
202+ return new AnalysisResponse ( imageAnalysisJson , diagnosticFilePath ) ;
255203}
256204
257205export { executeImageAnalysis , ImageData } ;
0 commit comments