|
152 | 152 | import { Checkbox } from '@components/Form/Checkbox';
|
153 | 153 | import { RESOURCE } from "@pkg/types";
|
154 | 154 | import { saveAs } from 'file-saver';
|
| 155 | + import Papa from 'papaparse'; |
| 156 | + import { flatten } from 'flat'; |
155 | 157 |
|
156 | 158 | export default {
|
157 | 159 | name: 'imageOverview',
|
|
189 | 191 | label: this.t('imageScanner.images.filters.image.includeBaseImages')
|
190 | 192 | }
|
191 | 193 | ];
|
| 194 | + const severityOptions = [ |
| 195 | + { |
| 196 | + value: "any", |
| 197 | + label: this.t('imageScanner.imageDetails.any') |
| 198 | + }, |
| 199 | + { |
| 200 | + value: "critical", |
| 201 | + label: this.t('imageScanner.enum.cve.critical') |
| 202 | + }, |
| 203 | + { |
| 204 | + value: "high", |
| 205 | + label: this.t('imageScanner.enum.cve.high') |
| 206 | + }, |
| 207 | + { |
| 208 | + value: "medium", |
| 209 | + label: this.t('imageScanner.enum.cve.medium') |
| 210 | + }, |
| 211 | + { |
| 212 | + value: "low", |
| 213 | + label: this.t('imageScanner.enum.cve.low') |
| 214 | + }, |
| 215 | + { |
| 216 | + value: "unknown", |
| 217 | + label: this.t('imageScanner.enum.cve.unknown') |
| 218 | + }, |
| 219 | + { |
| 220 | + value: "suppressed", |
| 221 | + label: this.t('imageScanner.enum.cve.suppressed') |
| 222 | + } |
| 223 | + ] |
192 | 224 | return {
|
193 | 225 | rows: [],
|
194 | 226 | REPO_BASED_TABLE: REPO_BASED_TABLE,
|
|
200 | 232 | preprocessedImagesBak: [],
|
201 | 233 | filterCveOptions,
|
202 | 234 | filterImageOptions,
|
| 235 | + severityOptions, |
203 | 236 | selectedCveFilter: filterCveOptions[0],
|
204 | 237 | selectedImageFilter: filterImageOptions[0],
|
205 | 238 | filters: {
|
206 | 239 | imageSearch: '',
|
207 |
| - severitySearch: 'any', |
208 |
| - repositorySearch: 'any', |
209 |
| - registrySearch: 'any', |
| 240 | + severitySearch: severityOptions[0], |
| 241 | + repositorySearch: 'Any', |
| 242 | + registrySearch: 'Any', |
210 | 243 | },
|
| 244 | + registryCrds: [], |
211 | 245 | }
|
212 | 246 | },
|
213 | 247 | computed: {
|
|
228 | 262 | icon: 'icon-download',
|
229 | 263 | enabled: true,
|
230 | 264 | bulkable: false,
|
231 |
| - invoke: async ({}, res = []) => {} |
| 265 | + invoke: (_, res) => { |
| 266 | + this.downloadCsv(res); |
| 267 | + } |
232 | 268 | };
|
233 | 269 | const downloadJson = {
|
234 | 270 | action: 'downloadJson',
|
235 | 271 | label: this.t('imageScanner.images.buttons.downloadJson'),
|
236 | 272 | icon: 'icon-download',
|
237 | 273 | enabled: true,
|
238 | 274 | bulkable: false,
|
239 |
| - invoke: async ({}, res = []) => {} |
| 275 | + invoke: (_, res) => { |
| 276 | + this.downloadJson(res); |
| 277 | + } |
240 | 278 | };
|
241 | 279 | return [
|
242 | 280 | downloadSbom,
|
243 | 281 | {divider: true},
|
244 | 282 | downloadCsv,
|
245 | 283 | downloadJson
|
246 | 284 | ];
|
| 285 | + }, |
| 286 | + repositoryOptions() { |
| 287 | + const repoSet = new Set(); |
| 288 | + this.registryCrds.forEach(reg => { |
| 289 | + if (reg.spec.repositories && reg.spec.repositories.length) { |
| 290 | + reg.spec.repositories.forEach(repo => repoSet.add(repo)); |
| 291 | + } |
| 292 | + }); |
| 293 | + return ["Any", ...repoSet]; |
| 294 | + }, |
| 295 | + registryOptions() { |
| 296 | + return ["Any", ...this.registryCrds.map(reg => `${reg.metadata.namespace}/${reg.metadata.name}`)]; |
247 | 297 | }
|
248 | 298 | },
|
249 | 299 | async fetch() {
|
250 |
| - await this.$store.dispatch('cluster/findAll', { type: RESOURCE.VULNERABILITY_REPORT }); |
251 |
| - const vulReportCRD = this.$store.getters['cluster/all']?.(RESOURCE.VULNERABILITY_REPORT); |
| 300 | + const vulReportCRD = await this.$store.dispatch('cluster/findAll', { type: RESOURCE.VULNERABILITY_REPORT }); |
252 | 301 | this.preprocessData(vulReportCRD);
|
253 | 302 | this.preprocessedDataset = this.preprocessData(vulReportCRD);
|
254 | 303 | this.preprocessedImagesBak = _.cloneDeep(this.preprocessedDataset.preprocessedImages);
|
255 | 304 | console.log("this.preprocessedDataset", this.preprocessedDataset)
|
| 305 | + this.registryCrds = await this.$store.dispatch('cluster/findAll', { type: RESOURCE.REGISTRY }); |
256 | 306 | },
|
257 | 307 | methods: {
|
258 | 308 | async downloadSbom(res) {
|
259 | 309 | const target = (res && res.length ? res[0] : null);
|
260 |
| - console.log("target", target); |
261 | 310 | const sbom = await this.$store.dispatch('cluster/find', { type: RESOURCE.SBOM, id: target.id });
|
262 | 311 | const spdxString = JSON.stringify(sbom.spdx, null, 2);
|
263 | 312 | const sbomBlob = new Blob([spdxString], { type: 'application/json;charset=utf-8' });
|
264 | 313 | saveAs(sbomBlob, `${sbom.metadata.name}-sbom.spdx.json`);
|
265 | 314 | },
|
| 315 | + async downloadJson(res) { |
| 316 | + const target = (res && res.length ? res[0] : null); |
| 317 | + const vulReport = await this.$store.dispatch('cluster/find', { type: RESOURCE.VULNERABILITY_REPORT, id: target.id }); |
| 318 | + const jsonBlob = new Blob([JSON.stringify(vulReport, null, 2)], { type: 'application/json;charset=utf-8' }); |
| 319 | + saveAs(jsonBlob, `${target.id}-vul-report.json`); |
| 320 | + }, |
| 321 | + async downloadCsv(res) { |
| 322 | + const target = (res && res.length ? res[0] : null); |
| 323 | + const vulReport = await this.$store.dispatch('cluster/find', { type: RESOURCE.VULNERABILITY_REPORT, id: target.id }); |
| 324 | + const csvString = JSON.stringify(vulReport.report.results[0].vulnerabilities.map(vul => flatten(vul))); |
| 325 | + const csvBlob = new Blob([Papa.unparse(csvString)], { type: 'text/csv;charset=utf-8' }); |
| 326 | + saveAs(csvBlob, `${target.id}-image-detail-report.csv`); |
| 327 | + }, |
266 | 328 | applyFilters() {
|
267 | 329 | let filtered = _.cloneDeep(this.preprocessedImagesBak);
|
268 | 330 | if (this.selectedImageFilter.value === "excludeBaseImages" || this.selectedImageFilter === "excludeBaseImages") {
|
|
0 commit comments