mirror of https://github.com/goharbor/harbor.git
210 lines
6.9 KiB
TypeScript
210 lines
6.9 KiB
TypeScript
import {
|
|
Component,
|
|
ElementRef,
|
|
EventEmitter,
|
|
HostListener,
|
|
OnDestroy,
|
|
OnInit,
|
|
Output,
|
|
ViewChild,
|
|
} from '@angular/core';
|
|
import { SecurityhubService } from '../../../../../../../ng-swagger-gen/services/securityhub.service';
|
|
import { SecuritySummary } from '../../../../../../../ng-swagger-gen/models/security-summary';
|
|
import { MessageHandlerService } from '../../../../../shared/services/message-handler.service';
|
|
import {
|
|
getDigestLink,
|
|
SeverityColors,
|
|
severityText,
|
|
VUL_ID,
|
|
} from '../security-hub.interface';
|
|
import { HAS_STYLE_MODE, StyleMode } from '../../../../../services/theme';
|
|
import { Subscription } from 'rxjs';
|
|
import {
|
|
EventService,
|
|
HarborEvent,
|
|
} from '../../../../../services/event-service/event.service';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
|
|
import * as echarts from 'echarts/core';
|
|
import { finalize } from 'rxjs/operators';
|
|
|
|
@Component({
|
|
selector: 'app-vulnerability-summary',
|
|
templateUrl: './vulnerability-summary.component.html',
|
|
styleUrls: ['./vulnerability-summary.component.scss'],
|
|
})
|
|
export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
|
|
@Output()
|
|
searchCVE = new EventEmitter<string>();
|
|
@Output()
|
|
searchRepo = new EventEmitter<DangerousArtifact>();
|
|
securitySummary: SecuritySummary;
|
|
readonly vulId: string = VUL_ID;
|
|
readonly severityText = severityText;
|
|
readonly getDigestLink = getDigestLink;
|
|
harborEventSub: Subscription;
|
|
chart: any;
|
|
@ViewChild('pieChart', { static: true })
|
|
pieChartEle: ElementRef;
|
|
loadingSummary: boolean = false;
|
|
constructor(
|
|
private securityHubService: SecurityhubService,
|
|
private messageHandler: MessageHandlerService,
|
|
private event: EventService,
|
|
private translate: TranslateService
|
|
) {}
|
|
|
|
ngOnInit() {
|
|
this.chart = echarts.init(this.pieChartEle.nativeElement);
|
|
this.getSummary();
|
|
if (!this.harborEventSub) {
|
|
this.harborEventSub = this.event.subscribe(
|
|
HarborEvent.THEME_CHANGE,
|
|
() => {
|
|
if (this.securitySummary) {
|
|
this.setOption(this.securitySummary);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
ngOnDestroy() {
|
|
if (this.harborEventSub) {
|
|
this.harborEventSub.unsubscribe();
|
|
this.harborEventSub = null;
|
|
}
|
|
}
|
|
|
|
getSummary() {
|
|
this.loadingSummary = true;
|
|
this.securityHubService
|
|
.getSecuritySummary({
|
|
withDangerousArtifact: true,
|
|
withDangerousCve: true,
|
|
})
|
|
.pipe(finalize(() => (this.loadingSummary = false)))
|
|
.subscribe({
|
|
next: res => {
|
|
this.securitySummary = res;
|
|
this.setOption(res);
|
|
},
|
|
error: err => {
|
|
this.messageHandler.error(err);
|
|
},
|
|
});
|
|
}
|
|
|
|
setOption(summary: SecuritySummary) {
|
|
const [severity, c, h, m, l, n, u] = [
|
|
'VULNERABILITY.GRID.COLUMN_SEVERITY',
|
|
'VULNERABILITY.SEVERITY.CRITICAL',
|
|
'VULNERABILITY.SEVERITY.HIGH',
|
|
'VULNERABILITY.SEVERITY.MEDIUM',
|
|
'VULNERABILITY.SEVERITY.LOW',
|
|
'VULNERABILITY.SEVERITY.NONE',
|
|
'UNKNOWN',
|
|
];
|
|
this.translate.get([severity, c, h, m, l, n, u]).subscribe(res => {
|
|
this.chart.setOption({
|
|
color: [
|
|
SeverityColors.CRITICAL,
|
|
SeverityColors.HIGH,
|
|
SeverityColors.MEDIUM,
|
|
SeverityColors.LOW,
|
|
SeverityColors.NA,
|
|
SeverityColors.NONE,
|
|
],
|
|
title: {
|
|
text: '',
|
|
},
|
|
tooltip: {
|
|
formatter: '<b>{b}: {d}%</b>',
|
|
},
|
|
legend: {
|
|
align: 'left',
|
|
left: 5,
|
|
bottom: 5,
|
|
formatter: '{a|{name}}',
|
|
textStyle: {
|
|
color: this.getColorByTheme(),
|
|
width: 50,
|
|
backgroundColor: 'transparent',
|
|
rich: {
|
|
a: {
|
|
fontWeight: '100',
|
|
fontSize: '12px',
|
|
verticalAlign: 'bottom',
|
|
height: 12,
|
|
lineHeight: 15,
|
|
},
|
|
},
|
|
},
|
|
itemWidth: 12,
|
|
itemHeight: 12,
|
|
width: '50%',
|
|
},
|
|
series: [
|
|
{
|
|
itemStyle: {
|
|
borderRadius: 3,
|
|
borderWidth: 1,
|
|
},
|
|
label: {
|
|
show: false,
|
|
},
|
|
radius: ['50%', '80%'],
|
|
name: res[severity],
|
|
type: 'pie',
|
|
center: ['68%', '50%'],
|
|
data: [
|
|
{
|
|
name: res[c],
|
|
value: summary?.critical_cnt || 0,
|
|
},
|
|
{
|
|
name: res[h],
|
|
value: summary?.high_cnt || 0,
|
|
},
|
|
{
|
|
name: res[m],
|
|
value: summary?.medium_cnt || 0,
|
|
},
|
|
{
|
|
name: res[l],
|
|
value: summary?.low_cnt || 0,
|
|
},
|
|
{
|
|
name: res[u],
|
|
value: summary?.unknown_cnt || 0,
|
|
},
|
|
{
|
|
name: res[n],
|
|
value: summary?.none_cnt || 0,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
});
|
|
}
|
|
|
|
searchCVEClick(cveId: string) {
|
|
this.searchCVE.emit(cveId);
|
|
}
|
|
|
|
searchRepoClick(artifact: DangerousArtifact) {
|
|
this.searchRepo.emit(artifact);
|
|
}
|
|
|
|
getColorByTheme(): string {
|
|
return localStorage?.getItem(HAS_STYLE_MODE) === StyleMode.LIGHT
|
|
? '#000'
|
|
: '#fff';
|
|
}
|
|
|
|
@HostListener('window:resize', ['$event'])
|
|
onResize(event: any) {
|
|
this.chart.resize();
|
|
}
|
|
}
|