2020-03-09 03:52:25 +01:00
|
|
|
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
2020-02-13 08:39:29 +01:00
|
|
|
import { AdditionsService } from "../additions.service";
|
|
|
|
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
|
|
|
import { finalize } from "rxjs/operators";
|
2021-02-18 02:12:23 +01:00
|
|
|
import { AdditionLink } from "../../../../../../../../ng-swagger-gen/models/addition-link";
|
2020-02-24 08:42:18 +01:00
|
|
|
import {
|
2020-10-23 09:03:33 +02:00
|
|
|
ProjectService,
|
2020-02-24 08:42:18 +01:00
|
|
|
ScannerVo,
|
2021-06-07 04:06:06 +02:00
|
|
|
ScanningResultService,
|
|
|
|
SystemInfoService,
|
2020-02-24 08:42:18 +01:00
|
|
|
UserPermissionService,
|
|
|
|
USERSTATICPERMISSION,
|
|
|
|
VulnerabilityItem
|
2021-02-18 02:12:23 +01:00
|
|
|
} from "../../../../../../shared/services";
|
|
|
|
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
2021-06-07 04:06:06 +02:00
|
|
|
import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../shared/units/utils";
|
2021-02-18 02:12:23 +01:00
|
|
|
import { ResultBarChartComponent } from "../../vulnerability-scanning/result-bar-chart.component";
|
2020-03-09 03:52:25 +01:00
|
|
|
import { Subscription } from "rxjs";
|
2021-02-18 02:12:23 +01:00
|
|
|
import { Artifact } from "../../../../../../../../ng-swagger-gen/models/artifact";
|
2021-06-07 04:06:06 +02:00
|
|
|
import { SessionService } from "../../../../../../shared/services/session.service";
|
2021-09-15 11:00:08 +02:00
|
|
|
import { EventService, HarborEvent } from "../../../../../../services/event-service/event.service";
|
2020-02-13 08:39:29 +01:00
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'hbr-artifact-vulnerabilities',
|
|
|
|
templateUrl: './artifact-vulnerabilities.component.html',
|
|
|
|
styleUrls: ['./artifact-vulnerabilities.component.scss']
|
|
|
|
})
|
2020-03-09 03:52:25 +01:00
|
|
|
export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
2020-02-13 08:39:29 +01:00
|
|
|
@Input()
|
|
|
|
vulnerabilitiesLink: AdditionLink;
|
2020-02-24 08:42:18 +01:00
|
|
|
@Input()
|
|
|
|
projectName: string;
|
|
|
|
@Input()
|
|
|
|
projectId: number;
|
|
|
|
@Input()
|
|
|
|
repoName: string;
|
|
|
|
@Input()
|
|
|
|
digest: string;
|
2020-04-07 11:13:40 +02:00
|
|
|
@Input() artifact: Artifact;
|
2020-02-24 08:42:18 +01:00
|
|
|
scan_overview: any;
|
|
|
|
scanner: ScannerVo;
|
2021-01-07 10:16:52 +01:00
|
|
|
projectScanner: ScannerVo;
|
2020-02-13 08:39:29 +01:00
|
|
|
|
|
|
|
scanningResults: VulnerabilityItem[] = [];
|
|
|
|
loading: boolean = false;
|
|
|
|
hasEnabledScanner: boolean = false;
|
|
|
|
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
|
|
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
2021-01-07 10:16:52 +01:00
|
|
|
cvssSort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
2020-02-24 08:42:18 +01:00
|
|
|
hasScanningPermission: boolean = false;
|
|
|
|
onSendingScanCommand: boolean = false;
|
2021-09-15 11:00:08 +02:00
|
|
|
onSendingStopCommand: boolean = false;
|
2020-02-24 08:42:18 +01:00
|
|
|
hasShowLoading: boolean = false;
|
2020-09-22 11:42:01 +02:00
|
|
|
@ViewChild(ResultBarChartComponent)
|
2020-02-24 08:42:18 +01:00
|
|
|
resultBarChartComponent: ResultBarChartComponent;
|
2020-03-09 03:52:25 +01:00
|
|
|
sub: Subscription;
|
2020-04-07 11:13:40 +02:00
|
|
|
hasViewInitWithDelay: boolean = false;
|
2020-10-23 09:03:33 +02:00
|
|
|
currentCVEList: Array<{ "cve_id": string; }> = [];
|
2020-02-13 08:39:29 +01:00
|
|
|
constructor(
|
|
|
|
private errorHandler: ErrorHandler,
|
|
|
|
private additionsService: AdditionsService,
|
2020-02-24 08:42:18 +01:00
|
|
|
private userPermissionService: UserPermissionService,
|
|
|
|
private scanningService: ScanningResultService,
|
2021-09-15 11:00:08 +02:00
|
|
|
private eventService: EventService,
|
2020-10-23 09:03:33 +02:00
|
|
|
private session: SessionService,
|
|
|
|
private projectService: ProjectService,
|
|
|
|
private systemInfoService: SystemInfoService,
|
2020-02-13 08:39:29 +01:00
|
|
|
) {
|
|
|
|
const that = this;
|
|
|
|
this.severitySort = {
|
|
|
|
compare(a: VulnerabilityItem, b: VulnerabilityItem): number {
|
|
|
|
return that.getLevel(a) - that.getLevel(b);
|
|
|
|
}
|
|
|
|
};
|
2021-01-07 10:16:52 +01:00
|
|
|
this.cvssSort = {
|
|
|
|
compare(a: VulnerabilityItem, b: VulnerabilityItem): number {
|
|
|
|
if (a && a.preferred_cvss && a.preferred_cvss.score_v3 && b && b.preferred_cvss && b.preferred_cvss.score_v3) {
|
|
|
|
return (+a.preferred_cvss.score_v3) - (+b.preferred_cvss.score_v3);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
2020-02-13 08:39:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ngOnInit() {
|
|
|
|
this.getVulnerabilities();
|
2020-02-24 08:42:18 +01:00
|
|
|
this.getScanningPermission();
|
|
|
|
this.getProjectScanner();
|
2020-03-09 03:52:25 +01:00
|
|
|
if (!this.sub) {
|
2021-09-15 11:00:08 +02:00
|
|
|
this.sub = this.eventService.subscribe(HarborEvent.UPDATE_VULNERABILITY_INFO, (artifact: Artifact) => {
|
|
|
|
if (artifact?.digest === this.artifact?.digest) {
|
|
|
|
this.getVulnerabilities();
|
|
|
|
}
|
2020-03-09 03:52:25 +01:00
|
|
|
});
|
|
|
|
}
|
2020-04-07 11:13:40 +02:00
|
|
|
setTimeout(() => {
|
|
|
|
this.hasViewInitWithDelay = true;
|
|
|
|
}, 0);
|
2020-10-23 09:03:33 +02:00
|
|
|
if (this.isSystemAdmin()) {
|
|
|
|
// get system and project CVE allow list
|
|
|
|
this.getCurrentCVEAllowList();
|
|
|
|
}
|
2020-03-09 03:52:25 +01:00
|
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
|
|
if (this.sub) {
|
|
|
|
this.sub.unsubscribe();
|
|
|
|
this.sub = null;
|
|
|
|
}
|
2020-02-13 08:39:29 +01:00
|
|
|
}
|
|
|
|
getVulnerabilities() {
|
|
|
|
if (this.vulnerabilitiesLink
|
|
|
|
&& !this.vulnerabilitiesLink.absolute
|
|
|
|
&& this.vulnerabilitiesLink.href) {
|
2020-02-24 08:42:18 +01:00
|
|
|
if (!this.hasShowLoading) {
|
2020-02-13 08:39:29 +01:00
|
|
|
this.loading = true;
|
2020-02-24 08:42:18 +01:00
|
|
|
this.hasShowLoading = true;
|
2020-02-13 08:39:29 +01:00
|
|
|
}
|
2021-01-07 10:16:52 +01:00
|
|
|
this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href, true, false)
|
2020-03-24 03:56:18 +01:00
|
|
|
.pipe(finalize(() => {
|
|
|
|
this.loading = false;
|
|
|
|
this.hasShowLoading = false;
|
|
|
|
}))
|
2020-02-13 08:39:29 +01:00
|
|
|
.subscribe(
|
2020-02-24 08:42:18 +01:00
|
|
|
res => {
|
|
|
|
this.scan_overview = res;
|
2021-01-07 10:16:52 +01:00
|
|
|
if (this.scan_overview && Object.values(this.scan_overview)[0]) {
|
|
|
|
this.scanningResults = (Object.values(this.scan_overview)[0] as any).vulnerabilities || [];
|
2020-03-30 11:22:28 +02:00
|
|
|
// sort
|
|
|
|
if (this.scanningResults) {
|
|
|
|
this.scanningResults.sort(((a, b) => this.getLevel(b) - this.getLevel(a)));
|
|
|
|
}
|
2021-01-07 10:16:52 +01:00
|
|
|
this.scanner = (Object.values(this.scan_overview)[0] as any).scanner;
|
2020-02-24 08:42:18 +01:00
|
|
|
}
|
2020-02-13 08:39:29 +01:00
|
|
|
}, error => {
|
|
|
|
this.errorHandler.error(error);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-02-24 08:42:18 +01:00
|
|
|
getScanningPermission(): void {
|
|
|
|
const permissions = [
|
|
|
|
{ resource: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE },
|
|
|
|
];
|
|
|
|
this.userPermissionService.hasProjectPermissions(this.projectId, permissions).subscribe((results: Array<boolean>) => {
|
|
|
|
this.hasScanningPermission = results[0];
|
|
|
|
// only has label permission
|
|
|
|
}, error => this.errorHandler.error(error));
|
|
|
|
}
|
|
|
|
getProjectScanner(): void {
|
|
|
|
this.hasEnabledScanner = false;
|
|
|
|
this.scanBtnState = ClrLoadingState.LOADING;
|
|
|
|
this.scanningService.getProjectScanner(this.projectId)
|
|
|
|
.subscribe(response => {
|
|
|
|
if (response && "{}" !== JSON.stringify(response) && !response.disabled
|
|
|
|
&& response.health === "healthy") {
|
|
|
|
this.scanBtnState = ClrLoadingState.SUCCESS;
|
|
|
|
this.hasEnabledScanner = true;
|
|
|
|
} else {
|
|
|
|
this.scanBtnState = ClrLoadingState.ERROR;
|
|
|
|
}
|
2021-01-07 10:16:52 +01:00
|
|
|
this.projectScanner = response;
|
2020-02-24 08:42:18 +01:00
|
|
|
}, error => {
|
|
|
|
this.scanBtnState = ClrLoadingState.ERROR;
|
|
|
|
});
|
|
|
|
}
|
2020-02-13 08:39:29 +01:00
|
|
|
getLevel(v: VulnerabilityItem): number {
|
|
|
|
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
|
|
|
|
return SEVERITY_LEVEL_MAP[v.severity];
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
refresh(): void {
|
|
|
|
this.getVulnerabilities();
|
|
|
|
}
|
|
|
|
|
|
|
|
severityText(severity: string): string {
|
|
|
|
switch (severity) {
|
|
|
|
case VULNERABILITY_SEVERITY.CRITICAL:
|
|
|
|
return 'VULNERABILITY.SEVERITY.CRITICAL';
|
|
|
|
case VULNERABILITY_SEVERITY.HIGH:
|
|
|
|
return 'VULNERABILITY.SEVERITY.HIGH';
|
|
|
|
case VULNERABILITY_SEVERITY.MEDIUM:
|
|
|
|
return 'VULNERABILITY.SEVERITY.MEDIUM';
|
|
|
|
case VULNERABILITY_SEVERITY.LOW:
|
|
|
|
return 'VULNERABILITY.SEVERITY.LOW';
|
|
|
|
case VULNERABILITY_SEVERITY.NEGLIGIBLE:
|
|
|
|
return 'VULNERABILITY.SEVERITY.NEGLIGIBLE';
|
|
|
|
case VULNERABILITY_SEVERITY.UNKNOWN:
|
|
|
|
return 'VULNERABILITY.SEVERITY.UNKNOWN';
|
|
|
|
default:
|
|
|
|
return 'UNKNOWN';
|
|
|
|
}
|
|
|
|
}
|
2020-02-24 08:42:18 +01:00
|
|
|
scanNow() {
|
|
|
|
this.onSendingScanCommand = true;
|
2021-09-15 11:00:08 +02:00
|
|
|
this.eventService.publish(HarborEvent.START_SCAN_ARTIFACT, this.repoName + "/" + this.digest);
|
2020-02-24 08:42:18 +01:00
|
|
|
}
|
|
|
|
submitFinish(e: boolean) {
|
|
|
|
this.onSendingScanCommand = e;
|
|
|
|
}
|
2021-09-15 11:00:08 +02:00
|
|
|
submitStopFinish(e: boolean) {
|
|
|
|
this.onSendingStopCommand = e;
|
|
|
|
}
|
2020-02-24 08:42:18 +01:00
|
|
|
shouldShowBar(): boolean {
|
2020-04-07 11:13:40 +02:00
|
|
|
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
2021-09-15 11:00:08 +02:00
|
|
|
&& (this.resultBarChartComponent.queued
|
|
|
|
|| this.resultBarChartComponent.scanning
|
|
|
|
|| this.resultBarChartComponent.error
|
|
|
|
|| this.resultBarChartComponent.stopped);
|
2020-02-24 08:42:18 +01:00
|
|
|
}
|
2020-05-06 10:41:35 +02:00
|
|
|
hasScanned(): boolean {
|
|
|
|
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
|
|
|
&& !(this.resultBarChartComponent.completed
|
|
|
|
|| this.resultBarChartComponent.error
|
|
|
|
|| this.resultBarChartComponent.queued
|
2021-09-15 11:00:08 +02:00
|
|
|
|| this.resultBarChartComponent.stopped
|
2020-05-06 10:41:35 +02:00
|
|
|
|| this.resultBarChartComponent.scanning);
|
|
|
|
}
|
2020-04-07 11:13:40 +02:00
|
|
|
handleScanOverview(scanOverview: any): any {
|
|
|
|
if (scanOverview) {
|
2021-01-07 10:16:52 +01:00
|
|
|
return Object.values(scanOverview)[0];
|
2020-04-07 11:13:40 +02:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2020-10-23 09:03:33 +02:00
|
|
|
isSystemAdmin(): boolean {
|
|
|
|
const account = this.session.getCurrentUser();
|
|
|
|
return account && account.has_admin_role;
|
|
|
|
}
|
|
|
|
getCurrentCVEAllowList() {
|
|
|
|
this.projectService.getProject(this.projectId).subscribe(
|
|
|
|
projectRes => {
|
|
|
|
if (projectRes && projectRes.cve_allowlist
|
2021-01-21 03:41:09 +01:00
|
|
|
&& projectRes.metadata && projectRes.metadata.reuse_sys_cve_allowlist === "false"
|
2020-10-23 09:03:33 +02:00
|
|
|
) { // use project CVE allow list
|
|
|
|
this.currentCVEList = projectRes.cve_allowlist['items'];
|
|
|
|
} else { // use system CVE allow list
|
|
|
|
this.systemInfoService.getSystemAllowlist().subscribe(
|
|
|
|
systemRes => {
|
|
|
|
if (systemRes && systemRes.items && systemRes.items.length) {
|
|
|
|
this.currentCVEList = systemRes.items;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
isInAllowList(CVEId: string): boolean {
|
|
|
|
if (this.currentCVEList && this.currentCVEList.length) {
|
|
|
|
for (let i = 0; i < this.currentCVEList.length; i++) {
|
|
|
|
if (CVEId === this.currentCVEList[i].cve_id) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2021-01-07 10:16:52 +01:00
|
|
|
getScannerInfo(scanner: ScannerVo): string {
|
|
|
|
if (scanner) {
|
|
|
|
if (scanner.name && scanner.version) {
|
|
|
|
return `${scanner.name}@${scanner.version}`;
|
|
|
|
}
|
|
|
|
if (scanner.name && !scanner.version) {
|
|
|
|
return `${scanner.name}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
2021-09-15 11:00:08 +02:00
|
|
|
isRunningState(): boolean {
|
|
|
|
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
|
|
|
&& (this.resultBarChartComponent.queued || this.resultBarChartComponent.scanning);
|
|
|
|
}
|
|
|
|
|
|
|
|
scanOrStop() {
|
|
|
|
if (this.isRunningState()) {
|
|
|
|
this.stopNow();
|
|
|
|
} else {
|
|
|
|
this.scanNow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stopNow() {
|
|
|
|
this.onSendingStopCommand = true;
|
|
|
|
this.eventService.publish(HarborEvent.STOP_SCAN_ARTIFACT, this.repoName + "/" + this.digest);
|
|
|
|
}
|
|
|
|
canScan(): boolean {
|
|
|
|
return this.hasEnabledScanner && this.hasScanningPermission && !this.onSendingScanCommand;
|
|
|
|
}
|
2020-02-13 08:39:29 +01:00
|
|
|
}
|