Add vulnerability database update time and scanner name

Signed-off-by: sshijun <sshijun@vmware.com>
This commit is contained in:
sshijun 2019-11-15 10:14:46 +08:00
parent 29be93725d
commit 8f3beb3e31
20 changed files with 108 additions and 25 deletions

View File

@ -60,4 +60,12 @@ export class ScanAllRepoService {
.pipe(catchError(error => observableThrowError(error))) .pipe(catchError(error => observableThrowError(error)))
.pipe(map(response => response as ScanningMetrics)); .pipe(map(response => response as ScanningMetrics));
} }
getScanners(): Observable<any> {
return this.http.get('/api/scanners')
.pipe(catchError(error => observableThrowError(error)));
}
getScannerMetadata(uid: string): Observable<any> {
return this.http.get(`/api/scanners/${uid}/metadata`)
.pipe(catchError(error => observableThrowError(error)));
}
} }

View File

@ -1,10 +1,16 @@
<section class="form-block"> <span *ngIf="onGettingUpdatedTimeStr || onGoing" class="mt-1 ml-1 spinner spinner-inline"></span>
<ng-container *ngIf="!(onGettingUpdatedTimeStr || onGoing)">
<section class="form-block">
<div *ngIf="updatedTimeStr" class="mt-1 display-flex">
<label class="update-time">{{ 'CONFIG.SCANNING.DB_REFRESH_TIME' | translate }}</label>
<span>{{ updatedTimeStr }} </span>
</div>
<div class="button-group"> <div class="button-group">
<cron-selection [labelCurrent]="getLabelCurrent" [labelEdit]='getLabelCurrent' <cron-selection [labelCurrent]="getLabelCurrent" [labelEdit]='getLabelCurrent'
[originCron]='originCron' (inputvalue)="saveSchedule($event)"></cron-selection> [originCron]='originCron' (inputvalue)="saveSchedule($event)"></cron-selection>
</div> </div>
</section> </section>
<div class="clr-row"> <div class="clr-row">
<div class="clr-col-2 flex-200"> <div class="clr-col-2 flex-200">
<div class="btn-scan-right btn-scan margin-top-16px"> <div class="btn-scan-right btn-scan margin-top-16px">
<button id="scan-now" class="btn btn-outline btn-sm btn-scan" (click)="scanNow()" <button id="scan-now" class="btn btn-outline btn-sm btn-scan" (click)="scanNow()"
@ -35,3 +41,6 @@
</div> </div>
</div> </div>
</div> </div>
</ng-container>

View File

@ -98,3 +98,6 @@
.float-left { .float-left {
float: left; float: left;
} }
.display-flex {
display: flex;
}

View File

@ -42,6 +42,9 @@ let fakedScanAllRepoService = {
}, },
manualScan() { manualScan() {
return of(true); return of(true);
},
getScanners() {
return of([]);
} }
}; };
let fakedErrorHandler = { let fakedErrorHandler = {

View File

@ -6,8 +6,9 @@ import { TranslateService } from '@ngx-translate/core';
import { ScanAllRepoService } from './scanAll.service'; import { ScanAllRepoService } from './scanAll.service';
import { OriginCron } from '../../service/interface'; import { OriginCron } from '../../service/interface';
import { CronScheduleComponent } from "../../cron-schedule/cron-schedule.component"; import { CronScheduleComponent } from "../../cron-schedule/cron-schedule.component";
import { VULNERABILITY_SCAN_STATUS } from "../../utils"; import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../utils";
import { errorHandler as errorHandFn} from "../../shared/shared.utils"; import { errorHandler as errorHandFn} from "../../shared/shared.utils";
import { DatePipe } from "@angular/common";
const SCHEDULE_TYPE_NONE = "None"; const SCHEDULE_TYPE_NONE = "None";
@Component({ @Component({
@ -38,6 +39,8 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
}; };
private _loopScheduleInterval; private _loopScheduleInterval;
private _loopManualInterval; private _loopManualInterval;
updatedTimeStr: string;
onGettingUpdatedTimeStr: boolean;
constructor( constructor(
private scanningService: ScanAllRepoService, private scanningService: ScanAllRepoService,
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
@ -103,6 +106,39 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
this.errorHandler.error(error); this.errorHandler.error(error);
}); });
} }
getScanners() {
this.onGettingUpdatedTimeStr = true;
this.scanningService.getScanners()
.subscribe(scanners => {
let flag = false;
if (scanners && scanners.length) {
scanners.forEach(scanner => {
if (scanner.is_default) {
flag = true;
this.getScannerMetadata(scanner.uuid);
}
});
}
if (!flag) {
this.onGettingUpdatedTimeStr = false;
}
}, error => {
this.onGettingUpdatedTimeStr = false;
});
}
getScannerMetadata(uid: string) {
this.scanningService.getScannerMetadata(uid)
.pipe(finalize(() => this.onGettingUpdatedTimeStr = false))
.subscribe(metadata => {
if (metadata && metadata.properties) {
for (let key in metadata.properties) {
if (key === DATABASE_UPDATED_PROPERTY && metadata.properties[key]) {
this.updatedTimeStr = new DatePipe(this.translate.currentLang).transform(metadata.properties[key], 'short');
}
}
}
});
}
public initSchedule(schedule: any) { public initSchedule(schedule: any) {
if (schedule && schedule.schedule !== null) { if (schedule && schedule.schedule !== null) {
@ -119,6 +155,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
this.getScanText(); this.getScanText();
this.getSchedule(); this.getSchedule();
this.initMetrics(); this.initMetrics();
this.getScanners();
} }
ngOnDestroy() { ngOnDestroy() {

View File

@ -312,6 +312,12 @@ export interface VulnerabilitySummary {
summary?: SeveritySummary; summary?: SeveritySummary;
start_time?: Date; start_time?: Date;
end_time?: Date; end_time?: Date;
scanner?: ScannerVo;
}
export interface ScannerVo {
name?: string;
vendor?: string;
version?: string;
} }
export interface SeveritySummary { export interface SeveritySummary {
total: number; total: number;

View File

@ -109,7 +109,7 @@
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell> <clr-dg-cell>
<div class="cell"> <div class="cell">
<hbr-vulnerability-bar (submitFinish)="submitFinish($event)" [repoName]="repoName" [tagId]="t.name" [summary]="handleScanOverview(t.scan_overview)"></hbr-vulnerability-bar> <hbr-vulnerability-bar [scanner]="handleScanOverview(t.scan_overview)?.scanner" (submitFinish)="submitFinish($event)" [repoName]="repoName" [tagId]="t.name" [summary]="handleScanOverview(t.scan_overview)"></hbr-vulnerability-bar>
</div> </div>
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell *ngIf="withNotary" [ngSwitch]="t.signature !== null"> <clr-dg-cell *ngIf="withNotary" [ngSwitch]="t.signature !== null">

View File

@ -230,6 +230,11 @@ export const DEFAULT_PAGE_SIZE: number = 15;
*/ */
export const DEFAULT_SUPPORTED_MIME_TYPE = "application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0"; export const DEFAULT_SUPPORTED_MIME_TYPE = "application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0";
/**
* the property name of vulnerability database updated time
*/
export const DATABASE_UPDATED_PROPERTY = "harbor.scanner-adapter/vulnerability-database-updated-at";
/** /**
* The state of vulnerability scanning * The state of vulnerability scanning
*/ */

View File

@ -13,7 +13,7 @@
<div class="progress loop loop-height"><progress></progress></div> <div class="progress loop loop-height"><progress></progress></div>
</div> </div>
<div *ngIf="completed" class="bar-state bar-state-chart"> <div *ngIf="completed" class="bar-state bar-state-chart">
<hbr-result-tip-histogram [vulnerabilitySummary]="summary"></hbr-result-tip-histogram> <hbr-result-tip-histogram [scanner]="scanner" [vulnerabilitySummary]="summary"></hbr-result-tip-histogram>
</div> </div>
<div *ngIf="otherStatus" class="bar-state"> <div *ngIf="otherStatus" class="bar-state">
<span class="label">{{'VULNERABILITY.STATE.OTHER_STATUS' | translate}}</span> <span class="label">{{'VULNERABILITY.STATE.OTHER_STATUS' | translate}}</span>

View File

@ -12,7 +12,7 @@ import {
VulnerabilitySummary, VulnerabilitySummary,
TagService, TagService,
ScanningResultService, ScanningResultService,
Tag Tag, ScannerVo
} from '../service/index'; } from '../service/index';
import { ErrorHandler } from '../error-handler/index'; import { ErrorHandler } from '../error-handler/index';
import { ChannelService } from '../channel/index'; import { ChannelService } from '../channel/index';
@ -28,6 +28,7 @@ const RETRY_TIMES: number = 3;
styleUrls: ['./scanning.scss'] styleUrls: ['./scanning.scss']
}) })
export class ResultBarChartComponent implements OnInit, OnDestroy { export class ResultBarChartComponent implements OnInit, OnDestroy {
@Input() scanner: ScannerVo;
@Input() repoName: string = ""; @Input() repoName: string = "";
@Input() tagId: string = ""; @Input() tagId: string = "";
@Input() summary: VulnerabilitySummary; @Input() summary: VulnerabilitySummary;

View File

@ -63,6 +63,10 @@
<div class="bar-summary bar-tooltip-fon" *ngIf="!isNone"> <div class="bar-summary bar-tooltip-fon" *ngIf="!isNone">
<histogram-chart [isWhiteBackground]="false" [metadata]="passMetadataToChart()"></histogram-chart> <histogram-chart [isWhiteBackground]="false" [metadata]="passMetadataToChart()"></histogram-chart>
</div> </div>
<div *ngIf="scanner">
<span class="bar-scanning-time">{{'SCANNER.SCANNED_BY' | translate }}</span>
<span class="margin-left-5">{{scanner?.name}}</span>
</div>
<div> <div>
<span class="bar-scanning-time">{{'SCANNER.DURATION' | translate }}</span> <span class="bar-scanning-time">{{'SCANNER.DURATION' | translate }}</span>
<span class="margin-left-5">{{duration()}}</span> <span class="margin-left-5">{{duration()}}</span>

View File

@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { VulnerabilitySummary } from "../../service"; import { ScannerVo, VulnerabilitySummary } from "../../service";
import { VULNERABILITY_SCAN_STATUS, VULNERABILITY_SEVERITY } from "../../utils"; import { VULNERABILITY_SCAN_STATUS, VULNERABILITY_SEVERITY } from "../../utils";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
@ -21,6 +21,7 @@ const CLASS_MAP = {
styleUrls: ['./result-tip-histogram.component.scss'] styleUrls: ['./result-tip-histogram.component.scss']
}) })
export class ResultTipHistogramComponent implements OnInit { export class ResultTipHistogramComponent implements OnInit {
@Input() scanner: ScannerVo;
_tipTitle: string = ""; _tipTitle: string = "";
@Input() vulnerabilitySummary: VulnerabilitySummary = { @Input() vulnerabilitySummary: VulnerabilitySummary = {
scan_status: VULNERABILITY_SCAN_STATUS.NOT_SCANNED, scan_status: VULNERABILITY_SCAN_STATUS.NOT_SCANNED,

View File

@ -1,12 +1,13 @@
import { import {
Component, Inject, Input, LOCALE_ID, Component, Input,
OnInit OnInit
} from "@angular/core"; } from "@angular/core";
import { ConfigScannerService } from "../config-scanner.service"; import { ConfigScannerService } from "../config-scanner.service";
import { finalize } from "rxjs/operators"; import { finalize } from "rxjs/operators";
import { ErrorHandler } from "@harbor/ui"; import { ErrorHandler, DATABASE_UPDATED_PROPERTY } from "@harbor/ui";
import { ScannerMetadata } from "../scanner-metadata"; import { ScannerMetadata } from "../scanner-metadata";
import { DatePipe } from "@angular/common"; import { DatePipe } from "@angular/common";
import { TranslateService } from "@ngx-translate/core";
@Component({ @Component({
selector: 'scanner-metadata', selector: 'scanner-metadata',
@ -19,7 +20,7 @@ export class ScannerMetadataComponent implements OnInit {
scannerMetadata: ScannerMetadata; scannerMetadata: ScannerMetadata;
constructor(private configScannerService: ConfigScannerService, constructor(private configScannerService: ConfigScannerService,
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
@Inject(LOCALE_ID) private _locale: string) { private translate: TranslateService) {
} }
ngOnInit(): void { ngOnInit(): void {
this.loading = true; this.loading = true;
@ -31,15 +32,14 @@ export class ScannerMetadataComponent implements OnInit {
this.errorHandler.error(error); this.errorHandler.error(error);
}); });
} }
parseDate(str: string): string { parseDate(item: any): string {
try { if (item && item.value && item.key === DATABASE_UPDATED_PROPERTY) {
if (str === new Date(str).toISOString()) { return new DatePipe(this.translate.currentLang).transform(item.value, 'short');
return new DatePipe(this._locale).transform(str, 'short');
}
} catch (e) {
return str;
} }
return str; if (item && item.value) {
return item.value;
}
return '';
} }
toString(arr: string[]) { toString(arr: string[]) {
if (arr && arr.length > 0) { if (arr && arr.length > 0) {

View File

@ -24,7 +24,7 @@
<div class="clr-control-label">{{'SCANNER.PROPERTIES' | translate}}</div> <div class="clr-control-label">{{'SCANNER.PROPERTIES' | translate}}</div>
<div class="ml-1" *ngFor="let item of scannerMetadata?.properties | keyvalue"> <div class="ml-1" *ngFor="let item of scannerMetadata?.properties | keyvalue">
<span>{{item?.key}}:</span> <span>{{item?.key}}:</span>
<span class="ml-1">{{parseDate(item?.value)}}</span> <span class="ml-1">{{parseDate(item)}}</span>
</div> </div>
</div> </div>

View File

@ -1343,6 +1343,7 @@
"VULNERABILITY_SEVERITY": "Vulnerability severity:", "VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion", "CONFIRM_DELETION": "Confirm Scanner deletion",
"NO_PROJECT_SCANNER": "No Scanner", "NO_PROJECT_SCANNER": "No Scanner",
"SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy" "SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy",
"SCANNED_BY": "Scanned by:"
} }
} }

View File

@ -1340,6 +1340,7 @@
"VULNERABILITY_SEVERITY": "Vulnerability severity:", "VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion", "CONFIRM_DELETION": "Confirm Scanner deletion",
"NO_PROJECT_SCANNER": "No Scanner", "NO_PROJECT_SCANNER": "No Scanner",
"SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy" "SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy",
"SCANNED_BY": "Scanned by:"
} }
} }

View File

@ -1312,6 +1312,7 @@
"VULNERABILITY_SEVERITY": "Vulnerability severity:", "VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion", "CONFIRM_DELETION": "Confirm Scanner deletion",
"NO_PROJECT_SCANNER": "No Scanner", "NO_PROJECT_SCANNER": "No Scanner",
"SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy" "SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy",
"SCANNED_BY": "Scanned by:"
} }
} }

View File

@ -1337,7 +1337,8 @@
"VULNERABILITY_SEVERITY": "Vulnerability severity:", "VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion", "CONFIRM_DELETION": "Confirm Scanner deletion",
"NO_PROJECT_SCANNER": "No Scanner", "NO_PROJECT_SCANNER": "No Scanner",
"SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy" "SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy",
"SCANNED_BY": "Scanned by:"
} }
} }

View File

@ -1342,6 +1342,7 @@
"VULNERABILITY_SEVERITY": "Vulnerability severity:", "VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion", "CONFIRM_DELETION": "Confirm Scanner deletion",
"NO_PROJECT_SCANNER": "No Scanner", "NO_PROJECT_SCANNER": "No Scanner",
"SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy" "SET_UNHEALTHY_SCANNER": "Selected scanner:{{name}} is unhealthy",
"SCANNED_BY": "Scanned by:"
} }
} }

View File

@ -1339,6 +1339,7 @@
"VULNERABILITY_SEVERITY": "漏洞严重度:", "VULNERABILITY_SEVERITY": "漏洞严重度:",
"CONFIRM_DELETION": "删除扫描器确认", "CONFIRM_DELETION": "删除扫描器确认",
"NO_PROJECT_SCANNER": "无扫描器", "NO_PROJECT_SCANNER": "无扫描器",
"SET_UNHEALTHY_SCANNER": "选择的扫描器:{{name}}是不健康的" "SET_UNHEALTHY_SCANNER": "选择的扫描器:{{name}}是不健康的",
"SCANNED_BY": "扫描器:"
} }
} }