1
1
<template >
2
2
<div class =" page" >
3
- <div class =" header-section" >
3
+ <div class =" header-section mb-20 " >
4
4
<div class =" title" >
5
5
{{ t("imageScanner.images.title") }}
6
6
</div >
10
10
:options =" filterCveOptions"
11
11
:close-on-select =" true"
12
12
:multiple =" false"
13
+ disabled
13
14
@selecting =" changeCveFilter"
14
15
/>
15
16
</div >
19
20
:options =" filterImageOptions"
20
21
:close-on-select =" true"
21
22
:multiple =" false"
23
+ disabled
22
24
@selecting =" changeImageFilter"
23
25
/>
24
26
</div >
34
36
</button >
35
37
</div >
36
38
</div >
37
- <div class =" summary-section" >
39
+ <!-- < div class="summary-section">
38
40
<TopRiskyImagesChart v-if="preprocessedDataset.topRiskyImages" :topRiskyImages="preprocessedDataset.topRiskyImages"/>
39
41
<ImageRiskAssessment v-if="preprocessedDataset.chartData" :chartData="preprocessedDataset.chartData" :filterFn="filterBySeverity"/>
40
- </div >
42
+ </div> -->
41
43
<SortableTable
42
- :has-advanced-filtering = " false "
44
+ :headers = " isGrouped ? REPO_BASED_TABLE : IMAGE_LIST_TABLE "
43
45
:namespaced =" false"
44
- :row-actions =" false "
46
+ :row-actions =" !isGrouped "
45
47
:table-actions =" true"
46
- :force-update-live-and-delayed =" 0"
47
- :use-query-params-for-simple-filtering =" true"
48
48
:sub-expandable =" isGrouped"
49
49
:sub-rows =" isGrouped"
50
50
:sub-expand-column =" isGrouped"
51
- :rows =" isGrouped ? preprocessedDataset.rowsByRepo : preprocessedDataset.preprocessedImages"
52
- :headers =" isGrouped ? REPO_BASED_TABLE : IMAGE_LIST_TABLE"
51
+ :rows =" isGrouped ? preprocessedDataset.rowsByRepo : rows"
53
52
:key-field =" 'id'"
54
53
@selection =" onSelectionChange"
55
54
>
82
81
:rows =" row.images"
83
82
:headers =" REPO_BASED_IMAGE_LIST_TABLE"
84
83
:search =" false"
85
- :row-actions =" false "
84
+ :row-actions =" true "
86
85
:table-actions =" false"
87
86
/>
88
87
</td >
89
88
</tr >
90
89
</template >
90
+ <template #row-actions =" { row } " >
91
+ <ActionMenu :resource =" row" :custom-actions =" customActions" />
92
+ </template >
91
93
</SortableTable >
92
94
</div >
93
95
99
101
import LabeledSelect from " @shell/components/form/LabeledSelect" ;
100
102
import DownloadCustomReport from " @pkg/components/common/DownloadCustomReport" ;
101
103
import TopRiskyImagesChart from " @pkg/components/TopRiskyImagesChart" ;
102
- import ImageRiskAssessment from " @pkg/components/ImageRiskAssessment"
103
- import { images } from " @pkg/data/sbombastic.rancher.io.image " ;
104
+ import ImageRiskAssessment from " @pkg/components/ImageRiskAssessment" ;
105
+ import ActionMenu from ' @shell/components/ActionMenuShell.vue ' ;
104
106
import { IMAGE_LIST_TABLE , REPO_BASED_TABLE , REPO_BASED_IMAGE_LIST_TABLE } from " @pkg/config/table-headers" ;
105
107
import { Checkbox } from ' @components/Form/Checkbox' ;
106
- import { filter } from " lodash" ;
108
+ import { RESOURCE } from " @pkg/types" ;
109
+ import { saveAs } from ' file-saver' ;
110
+
111
+
107
112
export default {
108
113
name: ' imageOverview' ,
109
114
components: {
@@ -113,6 +118,7 @@ import { filter } from "lodash";
113
118
SortableTable,
114
119
DownloadCustomReport,
115
120
Checkbox,
121
+ ActionMenu
116
122
},
117
123
data () {
118
124
const filterCveOptions = [
@@ -140,7 +146,7 @@ import { filter } from "lodash";
140
146
}
141
147
];
142
148
return {
143
- rows: images ,
149
+ rows: [] ,
144
150
REPO_BASED_TABLE : REPO_BASED_TABLE ,
145
151
IMAGE_LIST_TABLE : IMAGE_LIST_TABLE ,
146
152
REPO_BASED_IMAGE_LIST_TABLE : REPO_BASED_IMAGE_LIST_TABLE ,
@@ -158,14 +164,59 @@ import { filter } from "lodash";
158
164
selectedRegistry: " Any" ,
159
165
}
160
166
},
161
-
167
+ computed: {
168
+ customActions () {
169
+ const downloadSbom = {
170
+ action: ' downloadSbom' ,
171
+ label: this .t (' imageScanner.images.buttons.downloadSbom' ),
172
+ icon: ' icon-download' ,
173
+ enabled: true ,
174
+ bulkable: false ,
175
+ invoke : (_ , res ) => {
176
+ this .downloadSbom (res);
177
+ }
178
+ };
179
+ const downloadCsv = {
180
+ action: ' downloadCsv' ,
181
+ label: this .t (' imageScanner.images.buttons.downloadCsv' ),
182
+ icon: ' icon-download' ,
183
+ enabled: true ,
184
+ bulkable: false ,
185
+ invoke: async ({}, res = []) => {}
186
+ };
187
+ const downloadJson = {
188
+ action: ' downloadJson' ,
189
+ label: this .t (' imageScanner.images.buttons.downloadJson' ),
190
+ icon: ' icon-download' ,
191
+ enabled: true ,
192
+ bulkable: false ,
193
+ invoke: async ({}, res = []) => {}
194
+ };
195
+ return [
196
+ downloadSbom,
197
+ {divider: true },
198
+ downloadCsv,
199
+ downloadJson
200
+ ];
201
+ }
202
+ },
162
203
async fetch () {
163
- this .preprocessedDataset = this .preprocessData (this .rows );
204
+ await this .$store .dispatch (' cluster/findAll' , { type: RESOURCE .VULNERABILITY_REPORT });
205
+ const vulReportCRD = this .$store .getters [' cluster/all' ]? .(RESOURCE .VULNERABILITY_REPORT );
206
+ this .preprocessData (vulReportCRD);
207
+ this .preprocessedDataset = this .preprocessData (vulReportCRD);
164
208
this .preprocessedImagesBak = _ .cloneDeep (this .preprocessedDataset .preprocessedImages );
165
209
console .log (" this.preprocessedDataset" , this .preprocessedDataset )
166
210
},
167
-
168
211
methods: {
212
+ async downloadSbom (res ) {
213
+ const target = (res && res .length ? res[0 ] : null );
214
+ console .log (" target" , target);
215
+ const sbom = await this .$store .dispatch (' cluster/find' , { type: RESOURCE .SBOM , id: target .id });
216
+ const spdxString = JSON .stringify (sbom .spdx , null , 2 );
217
+ const sbomBlob = new Blob ([spdxString], { type: ' application/json;charset=utf-8' });
218
+ saveAs (sbomBlob, ` ${ sbom .metadata .name } -sbom.spdx.json` );
219
+ },
169
220
applyFilters () {
170
221
let filtered = _ .cloneDeep (this .preprocessedImagesBak );
171
222
if (this .selectedImageFilter .value === " excludeBaseImages" || this .selectedImageFilter === " excludeBaseImages" ) {
@@ -197,18 +248,28 @@ import { filter } from "lodash";
197
248
onSelectionChange (selected ) {
198
249
this .selectedRows = selected || [];
199
250
},
200
- preprocessData (images ) {
201
- console .log (" images" , images);
202
- const severityKeys = [' critical' , ' high' , ' medium' , ' low' , ' none' ];
203
- const chartData = {};
251
+ preprocessData (vulReportCRD ) {
252
+ this .rows = [];
253
+ vulReportCRD .forEach (report => {
254
+ this .rows .push ({
255
+ id: report .id ,
256
+ metadata: {
257
+ name: ` ${ report .imageMetadata .registry } :${ report .imageMetadata .tag } `
258
+ },
259
+ spec: {
260
+ isBaseImage: true ,
261
+ hasAffectedPackages: false ,
262
+ repository: report .imageMetadata .repository ,
263
+ registry: report .imageMetadata .registryURI ,
264
+ scanResult: report .report .summary ,
265
+ }
266
+ });
267
+ });
268
+ const severityKeys = [' critical' , ' high' , ' medium' , ' low' , ' unknown' ];
204
269
const repoMap = new Map ();
205
270
const preprocessedImages = [];
206
271
207
- for (const key of severityKeys) {
208
- chartData[key] = 0 ;
209
- }
210
-
211
- const topRiskyImages = images .map (image => {
272
+ this .rows .forEach (image => {
212
273
let repoRec = {};
213
274
let imageSeverity = " " ;
214
275
const mapKey = ` ${ image .spec .repository } ,${ image .spec .registry } ` ;
@@ -246,9 +307,6 @@ import { filter } from "lodash";
246
307
}
247
308
repoMap .set (mapKey, repoRec);
248
309
}
249
- for (const key of severityKeys) {
250
- chartData[key] += imageSeverity === key ? 1 : 0 ;
251
- }
252
310
preprocessedImages .push ({
253
311
... image,
254
312
severity: imageSeverity || " none" ,
@@ -257,27 +315,16 @@ import { filter } from "lodash";
257
315
imageName: image .metadata .name ,
258
316
cveCnt: image .spec .scanResult ,
259
317
}
260
- }).sort ((a , b ) => {
261
- for (const key of severityKeys) {
262
- const diff = (b .cveCnt [key] || 0 ) - (a .cveCnt [key] || 0 );
263
- if (diff !== 0 ) return diff;
264
- }
265
- return 0 ;
266
- }).slice (0 , 5 );
318
+ });
267
319
return {
268
320
preprocessedImages,
269
- topRiskyImages,
270
- chartData,
271
321
rowsByRepo: Array .from (repoMap .values ())
272
322
};
273
323
},
274
324
updateData (event ) {
275
325
console .log (" isGrouped" , this .isGrouped );
276
326
},
277
327
},
278
-
279
- computed: {
280
- },
281
328
}
282
329
283
330
< / script>
0 commit comments