mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-26 04:23:22 +02:00
Modify UI to match scanner upgrading (#13915)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
0cf43d766c
commit
4ea881564e
@ -39,7 +39,6 @@ import { ScannerComponent } from "./scanner/scanner.component";
|
||||
import { ConfigScannerService } from "../config/scanner/config-scanner.service";
|
||||
import { RepositoryGridviewComponent } from "./repository/repository-gridview.component";
|
||||
import { ResultTipHistogramComponent } from "./repository/vulnerability-scanning/result-tip-histogram/result-tip-histogram.component";
|
||||
import { ResultGridComponent } from "./repository/vulnerability-scanning/result-grid.component";
|
||||
import { ResultBarChartComponent } from "./repository/vulnerability-scanning/result-bar-chart.component";
|
||||
import { HistogramChartComponent } from "./repository/vulnerability-scanning/histogram-chart/histogram-chart.component";
|
||||
import { ResultTipComponent } from "./repository/vulnerability-scanning/result-tip.component";
|
||||
@ -100,7 +99,6 @@ import { P2pProviderComponent } from './p2p-provider/p2p-provider.component';
|
||||
HistogramChartComponent,
|
||||
ResultTipHistogramComponent,
|
||||
ResultBarChartComponent,
|
||||
ResultGridComponent,
|
||||
ResultTipComponent,
|
||||
ArtifactListPageComponent,
|
||||
ArtifactListComponent,
|
||||
|
@ -92,7 +92,9 @@
|
||||
<clr-dg-action-bar>
|
||||
<button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn"
|
||||
[disabled]="!(canScanNow() && selectedRowHasVul() && hasEnabledScanner && hasScanImagePermission )" (click)="scanNow()">
|
||||
<clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}
|
||||
<clr-icon shape="shield-check" size="16"></clr-icon>
|
||||
<span *ngIf="projectScanner">{{'VULNERABILITY.SCAN_BY' | translate: {scanner: getScannerInfo()} }}</span>
|
||||
<span *ngIf="!projectScanner">{{'VULNERABILITY.NO_SCANNER' | translate}}</span>
|
||||
</button>
|
||||
|
||||
<clr-dropdown class="btn btn-link" *ngIf="!depth">
|
||||
|
@ -382,6 +382,7 @@ clr-datagrid {
|
||||
}
|
||||
.scan-btn{
|
||||
margin-top: -.3rem;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.eslip {
|
||||
|
@ -26,15 +26,15 @@ import { ClrLoadingState, ClrDatagridStateInterface, ClrDatagridComparatorInterf
|
||||
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import {
|
||||
Comparator, Label, LabelService, ScanningResultService,
|
||||
Comparator, Label, LabelService, ScannerVo, ScanningResultService,
|
||||
UserPermissionService, USERSTATICPERMISSION, VulnerabilitySummary
|
||||
} from "../../../../../../lib/services";
|
||||
import {
|
||||
calculatePage,
|
||||
clone,
|
||||
CustomComparator,
|
||||
DEFAULT_PAGE_SIZE, DEFAULT_SUPPORTED_MIME_TYPE,
|
||||
formatSize, VULNERABILITY_SCAN_STATUS, dbEncodeURIComponent, doSorting
|
||||
DEFAULT_PAGE_SIZE,
|
||||
formatSize, VULNERABILITY_SCAN_STATUS, dbEncodeURIComponent, doSorting, DEFAULT_SUPPORTED_MIME_TYPES
|
||||
} from "../../../../../../lib/utils/utils";
|
||||
import {
|
||||
ConfirmationAcknowledgement,
|
||||
@ -151,6 +151,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
hasDeleteImagePermission: boolean;
|
||||
hasScanImagePermission: boolean;
|
||||
hasEnabledScanner: boolean;
|
||||
projectScanner: ScannerVo;
|
||||
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||
onSendingScanCommand: boolean;
|
||||
|
||||
@ -341,7 +342,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
withImmutableStatus: true,
|
||||
withLabel: true,
|
||||
withScanOverview: true,
|
||||
withTag: false
|
||||
withTag: false,
|
||||
XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES
|
||||
};
|
||||
this.newArtifactService.getArtifact(artifactParam).subscribe(
|
||||
res => {
|
||||
@ -357,7 +359,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
withImmutableStatus: true,
|
||||
withLabel: true,
|
||||
withScanOverview: true,
|
||||
withTag: false
|
||||
withTag: false,
|
||||
XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES
|
||||
};
|
||||
platFormAttr.push({platform: child.platform});
|
||||
observableLists.push(this.newArtifactService.getArtifact(childParams));
|
||||
@ -386,7 +389,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
repositoryName: dbEncodeURIComponent(this.repoName),
|
||||
withLabel: true,
|
||||
withScanOverview: true,
|
||||
withTag: false
|
||||
withTag: false,
|
||||
XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES
|
||||
};
|
||||
Object.assign(listArtifactParams, params);
|
||||
this.newArtifactService.listArtifactsResponse(listArtifactParams)
|
||||
@ -914,6 +918,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
this.scanBtnState = ClrLoadingState.ERROR;
|
||||
}
|
||||
this.projectScanner = response;
|
||||
}, error => {
|
||||
this.scanBtnState = ClrLoadingState.ERROR;
|
||||
});
|
||||
@ -921,7 +926,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
|
||||
handleScanOverview(scanOverview: any): any {
|
||||
if (scanOverview) {
|
||||
return scanOverview[DEFAULT_SUPPORTED_MIME_TYPE];
|
||||
return Object.values(scanOverview)[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -1038,4 +1043,15 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
}
|
||||
getScannerInfo(): string {
|
||||
if (this.projectScanner) {
|
||||
if (this.projectScanner.name && this.projectScanner.version) {
|
||||
return `${this.projectScanner.name}@${this.projectScanner.version}`;
|
||||
}
|
||||
if (this.projectScanner.name && !this.projectScanner.version) {
|
||||
return `${this.projectScanner.name}`;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ describe('TagRetentionService', () => {
|
||||
|
||||
it('should be created and get right data', inject([AdditionsService], (service: AdditionsService) => {
|
||||
expect(service).toBeTruthy();
|
||||
service.getDetailByLink(testLink).subscribe(res => {
|
||||
service.getDetailByLink(testLink, false, false).subscribe(res => {
|
||||
expect(res).toEqual(data);
|
||||
}
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Observable } from "rxjs";
|
||||
import { DEFAULT_SUPPORTED_MIME_TYPES } from "../../../../../lib/utils/utils";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -9,10 +10,13 @@ export class AdditionsService {
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
getDetailByLink(link: string, shouldReturnText?: boolean): Observable<any> {
|
||||
getDetailByLink(link: string, shouldSetHeader: boolean, shouldReturnText: boolean): Observable<any> {
|
||||
if (shouldReturnText) {
|
||||
return this.http.get(link, { observe: 'body', responseType: 'text'} );
|
||||
}
|
||||
if (shouldSetHeader) {
|
||||
return this.http.get(link, {headers: {"X-Accept-Vulnerabilities": DEFAULT_SUPPORTED_MIME_TYPES}});
|
||||
}
|
||||
return this.http.get(link);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,11 @@
|
||||
<clr-dg-action-bar>
|
||||
<div class="clr-row center">
|
||||
<div class="ml-05">
|
||||
<button (click)="scanNow()" type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!(hasEnabledScanner && hasScanningPermission && !onSendingScanCommand)"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||
<button (click)="scanNow()" type="button" class="btn btn-secondary text-transform-none" [clrLoading]="scanBtnState" [disabled]="!(hasEnabledScanner && hasScanningPermission && !onSendingScanCommand)">
|
||||
<clr-icon shape="shield-check" size="16"></clr-icon>
|
||||
<span *ngIf="projectScanner">{{'VULNERABILITY.SCAN_BY' | translate: {scanner: getScannerInfo(projectScanner)} }}</span>
|
||||
<span *ngIf="!projectScanner">{{'VULNERABILITY.NO_SCANNER' | translate}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<div [hidden]="!shouldShowBar()">
|
||||
@ -21,10 +25,12 @@
|
||||
</hbr-vulnerability-bar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="severitySort">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="cvssSort">{{'VULNERABILITY.GRID.CVSS3' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'package'">{{'VULNERABILITY.GRID.COLUMN_PACKAGE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'version'">{{'VULNERABILITY.GRID.COLUMN_VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'fix_version'">{{'VULNERABILITY.GRID.COLUMN_FIXED' | translate}}</clr-dg-column>
|
||||
@ -59,6 +65,7 @@
|
||||
<span *ngSwitchCase="'Unknown'" class="label label-unknown no-border">{{severityText(res.severity) | translate}}</span>
|
||||
<span *ngSwitchDefault>{{severityText(res.severity) | translate}}</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{res.preferred_cvss?.score_v3}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{res.package}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{res.version}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
@ -76,6 +83,9 @@
|
||||
</clr-dg-row>
|
||||
|
||||
<clr-dg-footer>
|
||||
<div class="report">
|
||||
<i>{{'VULNERABILITY.REPORTED_BY' | translate: {scanner: getScannerInfo(scanner)} }}</i>
|
||||
</div>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="25" [clrDgTotalItems]="scanningResults?.length">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[15,25,50]">{{"PAGINATION.PAGE_SIZE" | translate}}</clr-dg-page-size>
|
||||
<span *ngIf="scanningResults?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'VULNERABILITY.GRID.FOOT_OF' | translate}}</span> {{scanningResults?.length}} {{'VULNERABILITY.GRID.FOOT_ITEMS' | translate}}
|
||||
|
@ -49,3 +49,9 @@
|
||||
.ml-05 {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
.report {
|
||||
text-align: left;
|
||||
}
|
||||
.text-transform-none {
|
||||
text-transform: none;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
||||
import { DEFAULT_SUPPORTED_MIME_TYPE } from "../../../../../../lib/utils/utils";
|
||||
import {SessionService} from "../../../../../shared/session.service";
|
||||
import {SessionUser} from "../../../../../shared/session-user";
|
||||
import {delay} from "rxjs/operators";
|
||||
@ -46,8 +45,8 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
||||
},
|
||||
];
|
||||
let scanOverview = {};
|
||||
scanOverview[DEFAULT_SUPPORTED_MIME_TYPE] = {};
|
||||
scanOverview[DEFAULT_SUPPORTED_MIME_TYPE].vulnerabilities = mockedVulnerabilities;
|
||||
scanOverview["keyForTest"] = {};
|
||||
scanOverview["keyForTest"].vulnerabilities = mockedVulnerabilities;
|
||||
const mockedLink: AdditionLink = {
|
||||
absolute: false,
|
||||
href: '/test'
|
||||
@ -156,7 +155,7 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
||||
await fixture.whenStable();
|
||||
const cols = fixture.nativeElement.querySelectorAll("clr-dg-column");
|
||||
expect(cols).toBeTruthy();
|
||||
expect(cols.length).toEqual(6);
|
||||
expect(cols.length).toEqual(7);
|
||||
const firstRow = fixture.nativeElement.querySelector("clr-dg-row");
|
||||
const cells = firstRow.querySelectorAll("clr-dg-cell");
|
||||
expect(cells[cells.length - 1].innerText).toEqual("TAG_RETENTION.YES");
|
||||
|
@ -13,7 +13,6 @@ import {
|
||||
} from "../../../../../../lib/services";
|
||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||
import {
|
||||
DEFAULT_SUPPORTED_MIME_TYPE,
|
||||
SEVERITY_LEVEL_MAP,
|
||||
VULNERABILITY_SEVERITY
|
||||
} from "../../../../../../lib/utils/utils";
|
||||
@ -42,12 +41,14 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
||||
@Input() artifact: Artifact;
|
||||
scan_overview: any;
|
||||
scanner: ScannerVo;
|
||||
projectScanner: ScannerVo;
|
||||
|
||||
scanningResults: VulnerabilityItem[] = [];
|
||||
loading: boolean = false;
|
||||
hasEnabledScanner: boolean = false;
|
||||
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
||||
cvssSort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
||||
hasScanningPermission: boolean = false;
|
||||
onSendingScanCommand: boolean = false;
|
||||
hasShowLoading: boolean = false;
|
||||
@ -72,6 +73,14 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
||||
return that.getLevel(a) - that.getLevel(b);
|
||||
}
|
||||
};
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -105,7 +114,7 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
||||
this.loading = true;
|
||||
this.hasShowLoading = true;
|
||||
}
|
||||
this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href)
|
||||
this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href, true, false)
|
||||
.pipe(finalize(() => {
|
||||
this.loading = false;
|
||||
this.hasShowLoading = false;
|
||||
@ -113,13 +122,13 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
||||
.subscribe(
|
||||
res => {
|
||||
this.scan_overview = res;
|
||||
if (this.scan_overview && this.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]) {
|
||||
this.scanningResults = this.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE].vulnerabilities || [];
|
||||
if (this.scan_overview && Object.values(this.scan_overview)[0]) {
|
||||
this.scanningResults = (Object.values(this.scan_overview)[0] as any).vulnerabilities || [];
|
||||
// sort
|
||||
if (this.scanningResults) {
|
||||
this.scanningResults.sort(((a, b) => this.getLevel(b) - this.getLevel(a)));
|
||||
}
|
||||
this.scanner = this.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE].scanner;
|
||||
this.scanner = (Object.values(this.scan_overview)[0] as any).scanner;
|
||||
}
|
||||
}, error => {
|
||||
this.errorHandler.error(error);
|
||||
@ -148,6 +157,7 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
this.scanBtnState = ClrLoadingState.ERROR;
|
||||
}
|
||||
this.projectScanner = response;
|
||||
}, error => {
|
||||
this.scanBtnState = ClrLoadingState.ERROR;
|
||||
});
|
||||
@ -200,7 +210,7 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
handleScanOverview(scanOverview: any): any {
|
||||
if (scanOverview) {
|
||||
return scanOverview[DEFAULT_SUPPORTED_MIME_TYPE];
|
||||
return Object.values(scanOverview)[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -237,4 +247,15 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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 "";
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export class BuildHistoryComponent implements OnInit {
|
||||
&& !this.buildHistoryLink.absolute
|
||||
&& this.buildHistoryLink.href) {
|
||||
this.loading = true;
|
||||
this.additionsService.getDetailByLink(this.buildHistoryLink.href)
|
||||
this.additionsService.getDetailByLink(this.buildHistoryLink.href, false, false)
|
||||
.pipe(finalize(() => this.loading = false))
|
||||
.subscribe(
|
||||
res => {
|
||||
|
@ -32,7 +32,7 @@ export class DependenciesComponent implements OnInit {
|
||||
&& !this.dependenciesLink.absolute
|
||||
&& this.dependenciesLink.href) {
|
||||
this.loading = true;
|
||||
this.additionsService.getDetailByLink(this.dependenciesLink.href)
|
||||
this.additionsService.getDetailByLink(this.dependenciesLink.href, false, false)
|
||||
.pipe(finalize(() => this.loading = false))
|
||||
.subscribe(
|
||||
res => {
|
||||
|
@ -31,7 +31,7 @@ export class SummaryComponent implements OnInit {
|
||||
&& !this.summaryLink.absolute
|
||||
&& this.summaryLink.href) {
|
||||
this.loading = true;
|
||||
this.additionsService.getDetailByLink(this.summaryLink.href, true)
|
||||
this.additionsService.getDetailByLink(this.summaryLink.href, false, true)
|
||||
.pipe(finalize(() => this.loading = false))
|
||||
.subscribe(
|
||||
res => {
|
||||
|
@ -34,7 +34,7 @@ export class ValuesComponent implements OnInit {
|
||||
ngOnInit(): void {
|
||||
if (this.valuesLink && !this.valuesLink.absolute && this.valuesLink.href) {
|
||||
this.loading = true;
|
||||
this.additionsService.getDetailByLink(this.valuesLink.href, true)
|
||||
this.additionsService.getDetailByLink(this.valuesLink.href, false, true)
|
||||
.pipe(finalize(() => this.loading = false))
|
||||
.subscribe(
|
||||
res => {
|
||||
|
@ -13,7 +13,6 @@ import { ChannelService } from "../../../../lib/services/channel.service";
|
||||
import {
|
||||
clone,
|
||||
CURRENT_BASE_HREF,
|
||||
DEFAULT_SUPPORTED_MIME_TYPE,
|
||||
VULNERABILITY_SCAN_STATUS,
|
||||
dbEncodeURIComponent
|
||||
} from "../../../../lib/utils/utils";
|
||||
@ -159,7 +158,7 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
||||
.subscribe((artifact: Artifact) => {
|
||||
// To keep the same summary reference, use value copy.
|
||||
if (artifact.scan_overview) {
|
||||
this.copyValue(artifact.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]);
|
||||
this.copyValue(Object.values(artifact.scan_overview)[0]);
|
||||
}
|
||||
if (!this.queued && !this.scanning) {
|
||||
// Scanning should be done
|
||||
|
@ -1,65 +0,0 @@
|
||||
<div class="row result-row">
|
||||
<div>
|
||||
<div class="row flex-items-xs-right rightPos">
|
||||
<div class="flex-xs-middle option-right">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'VULNERABILITY.PLACEHOLDER' | translate}}" (filterEvt)="filterVulnerabilities($event)"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid [clrDgLoading]="loading">
|
||||
<clr-dg-action-bar>
|
||||
<button type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!hasScanImagePermission || !hasEnabledScanner" (click)="scanNow()"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="severitySort">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'package'">{{'VULNERABILITY.GRID.COLUMN_PACKAGE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'version'">{{'VULNERABILITY.GRID.COLUMN_VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'fix_version'">{{'VULNERABILITY.GRID.COLUMN_FIXED' | translate}}</clr-dg-column>
|
||||
|
||||
<clr-dg-placeholder>{{'VULNERABILITY.CHART.TOOLTIPS_TITLE_ZERO' | translate}}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let res of scanningResults">
|
||||
<clr-dg-cell>
|
||||
<span *ngIf="!res.links || res.links.length === 0">{{res.id}}</span>
|
||||
<a *ngIf="res.links && res.links.length === 1" href="{{res.links[0]}}" target="_blank">{{res.id}}</a>
|
||||
<span *ngIf="res.links && res.links.length > 1">
|
||||
{{res.id}}
|
||||
<clr-signpost>
|
||||
<clr-signpost-content *clrIfOpen>
|
||||
<div class="mt-5px" *ngFor="let link of res.links">
|
||||
<a href="{{link}}" target="_blank">{{link}}</a>
|
||||
</div>
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell [ngSwitch]="res.severity">
|
||||
<span *ngSwitchCase="'Critical'" class="label label-critical no-border">{{severityText(res.severity) | translate}}</span>
|
||||
<span *ngSwitchCase="'High'" class="label label-danger no-border">{{severityText(res.severity) | translate}}</span>
|
||||
<span *ngSwitchCase="'Medium'" class="label label-medium no-border">{{severityText(res.severity) | translate}}</span>
|
||||
<span *ngSwitchCase="'Low'" class="label label-low no-border">{{severityText(res.severity) | translate}}</span>
|
||||
<span *ngSwitchCase="'Negligible'" class="label label-negligible no-border">{{severityText(res.severity) | translate}}</span>
|
||||
<span *ngSwitchCase="'Unknown'" class="label label-unknown no-border">{{severityText(res.severity) | translate}}</span>
|
||||
<span *ngSwitchDefault>{{severityText(res.severity) | translate}}</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{res.package}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{res.version}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<div *ngIf="res.fix_version; else elseBlock">
|
||||
<clr-icon shape="wrench" class="is-success" size="20"></clr-icon> <span class="font-color-green">{{res.fix_version}}</span>
|
||||
</div>
|
||||
<ng-template #elseBlock>{{res.fix_version}}</ng-template>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-row-detail *clrIfExpanded>
|
||||
{{'VULNERABILITY.GRID.COLUMN_DESCRIPTION' | translate}}: {{res.description}}
|
||||
</clr-dg-row-detail>
|
||||
</clr-dg-row>
|
||||
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="scanningResults?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'VULNERABILITY.GRID.FOOT_OF' | translate}}</span> {{scanningResults?.length}} {{'VULNERABILITY.GRID.FOOT_ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="25" [clrDgTotalItems]="scanningResults?.length"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
@ -1,121 +0,0 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { ResultGridComponent } from './result-grid.component';
|
||||
import { of } from "rxjs";
|
||||
import { IServiceConfig, SERVICE_CONFIG } from "../../../../lib/entities/service.config";
|
||||
import {
|
||||
ScanningResultDefaultService,
|
||||
ScanningResultService,
|
||||
UserPermissionDefaultService,
|
||||
UserPermissionService, USERSTATICPERMISSION, VulnerabilityItem
|
||||
} from "../../../../lib/services";
|
||||
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
||||
import { FilterComponent } from "../../../../lib/components/filter/filter.component";
|
||||
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||
import { CURRENT_BASE_HREF, DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SEVERITY } from "../../../../lib/utils/utils";
|
||||
describe('ResultGridComponent (inline template)', () => {
|
||||
let component: ResultGridComponent;
|
||||
let fixture: ComponentFixture<ResultGridComponent>;
|
||||
let serviceConfig: IServiceConfig;
|
||||
let scanningService: ScanningResultService;
|
||||
let userPermissionService: UserPermissionService;
|
||||
let spy: jasmine.Spy;
|
||||
let mockHasScanImagePermission: boolean = true;
|
||||
let testConfig: IServiceConfig = {
|
||||
vulnerabilityScanningBaseEndpoint: CURRENT_BASE_HREF + "/vulnerability/testing"
|
||||
};
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
NoopAnimationsModule
|
||||
],
|
||||
declarations: [ResultGridComponent, FilterComponent],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
ChannelService,
|
||||
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService }
|
||||
]
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ResultGridComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.tagId = "mockTag";
|
||||
component.projectId = 1;
|
||||
|
||||
serviceConfig = TestBed.get(SERVICE_CONFIG);
|
||||
scanningService = fixture.debugElement.injector.get(ScanningResultService);
|
||||
let mockData: any = {};
|
||||
mockData[DEFAULT_SUPPORTED_MIME_TYPE] = {};
|
||||
mockData[DEFAULT_SUPPORTED_MIME_TYPE].vulnerabilities = [];
|
||||
for (let i = 0; i < 30; i++) {
|
||||
let res: VulnerabilityItem = {
|
||||
id: "CVE-2016-" + (8859 + i),
|
||||
severity: i % 2 === 0 ? VULNERABILITY_SEVERITY.HIGH : VULNERABILITY_SEVERITY.MEDIUM,
|
||||
package: "package_" + i,
|
||||
links: ["https://security-tracker.debian.org/tracker/CVE-2016-4484"],
|
||||
layer: "layer_" + i,
|
||||
version: '4.' + i + ".0",
|
||||
fix_version: '4.' + i + '.11',
|
||||
description: "Mock data"
|
||||
};
|
||||
mockData[DEFAULT_SUPPORTED_MIME_TYPE].vulnerabilities.push(res);
|
||||
}
|
||||
|
||||
spy = spyOn(scanningService, 'getVulnerabilityScanningResults')
|
||||
.and.returnValue(of(mockData));
|
||||
|
||||
|
||||
userPermissionService = fixture.debugElement.injector.get(UserPermissionService);
|
||||
|
||||
|
||||
spyOn(userPermissionService, "getPermission")
|
||||
.withArgs(component.projectId, USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY,
|
||||
USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE )
|
||||
.and.returnValue(of(mockHasScanImagePermission));
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should inject the SERVICE_CONFIG', () => {
|
||||
expect(serviceConfig).toBeTruthy();
|
||||
expect(serviceConfig.vulnerabilityScanningBaseEndpoint).toEqual(CURRENT_BASE_HREF + "/vulnerability/testing");
|
||||
});
|
||||
|
||||
it('should inject and call the ScanningResultService', () => {
|
||||
expect(scanningService).toBeTruthy();
|
||||
expect(spy.calls.any()).toBe(true, 'getScanningResults called');
|
||||
});
|
||||
|
||||
it('should get data from ScanningResultService', waitForAsync(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => { // wait for async getRecentLogs
|
||||
fixture.detectChanges();
|
||||
expect(component.scanningResults).toBeTruthy();
|
||||
expect(component.scanningResults.length).toEqual(30);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should render data to view', waitForAsync(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
let el: HTMLElement = fixture.nativeElement.querySelector('.datagrid-cell a');
|
||||
expect(el).toBeTruthy();
|
||||
expect(el.textContent.trim()).toEqual('CVE-2016-8859');
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
@ -1,162 +0,0 @@
|
||||
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
|
||||
import { forkJoin, Subscription } from "rxjs";
|
||||
import { finalize } from "rxjs/operators";
|
||||
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
||||
import {
|
||||
ScanningResultService,
|
||||
UserPermissionService,
|
||||
USERSTATICPERMISSION,
|
||||
VulnerabilityItem
|
||||
} from "../../../../lib/services";
|
||||
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||
import { DEFAULT_SUPPORTED_MIME_TYPE, SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../lib/utils/utils";
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-vulnerabilities-grid',
|
||||
templateUrl: './result-grid.component.html',
|
||||
styleUrls: ['./scanning.scss']
|
||||
})
|
||||
export class ResultGridComponent implements OnInit, OnDestroy {
|
||||
scanningResults: VulnerabilityItem[] = [];
|
||||
dataCache: VulnerabilityItem[] = [];
|
||||
loading: boolean = false;
|
||||
shouldShowLoading: boolean = true;
|
||||
@Input() tagId: string;
|
||||
@Input() repositoryId: string;
|
||||
@Input() projectId: number;
|
||||
hasScanImagePermission: boolean;
|
||||
hasEnabledScanner: boolean = false;
|
||||
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
||||
sub: Subscription;
|
||||
constructor(
|
||||
private scanningService: ScanningResultService,
|
||||
private channel: ChannelService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private errorHandler: ErrorHandler,
|
||||
) {
|
||||
const that = this;
|
||||
this.severitySort = {
|
||||
compare(a: VulnerabilityItem, b: VulnerabilityItem): number {
|
||||
return that.getLevel(a) - that.getLevel(b);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadResults(this.repositoryId, this.tagId);
|
||||
this.getScanPermissions(this.projectId);
|
||||
if (!this.sub) {
|
||||
this.channel.ArtifactDetail$.subscribe(tag => {
|
||||
this.loadResults(this.repositoryId, this.tagId);
|
||||
});
|
||||
}
|
||||
}
|
||||
ngOnDestroy() {
|
||||
if (this.sub) {
|
||||
this.sub.unsubscribe();
|
||||
this.sub = null;
|
||||
}
|
||||
}
|
||||
getLevel(v: VulnerabilityItem): number {
|
||||
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
|
||||
return SEVERITY_LEVEL_MAP[v.severity];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}, error => {
|
||||
this.scanBtnState = ClrLoadingState.ERROR;
|
||||
});
|
||||
}
|
||||
|
||||
loadResults(repositoryId: string, tagId: string): void {
|
||||
// only show loading for one time
|
||||
if (this.shouldShowLoading) {
|
||||
this.loading = true;
|
||||
this.shouldShowLoading = false;
|
||||
}
|
||||
this.scanningService.getVulnerabilityScanningResults(repositoryId, tagId)
|
||||
.pipe(finalize(() => this.loading = false))
|
||||
.subscribe((results) => {
|
||||
if (results && results[DEFAULT_SUPPORTED_MIME_TYPE]) {
|
||||
let report = results[DEFAULT_SUPPORTED_MIME_TYPE];
|
||||
if (report.vulnerabilities) {
|
||||
this.dataCache = report.vulnerabilities;
|
||||
this.scanningResults = this.dataCache.filter((item: VulnerabilityItem) => item.id !== '');
|
||||
// sort
|
||||
if (this.scanningResults) {
|
||||
this.scanningResults.sort(((a, b) => this.getLevel(b) - this.getLevel(a)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Should query from back-end service
|
||||
filterVulnerabilities(terms: string): void {
|
||||
if (terms.trim() === '') {
|
||||
this.scanningResults = this.dataCache.filter((item: VulnerabilityItem) => item.id !== '');
|
||||
} else {
|
||||
this.scanningResults = this.dataCache.filter((item: VulnerabilityItem) => this._regexpFilter(terms, item.package));
|
||||
}
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this.loadResults(this.repositoryId, this.tagId);
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
_regexpFilter(terms: string, testedValue: any): boolean {
|
||||
let reg = new RegExp('.*' + terms + '.*', 'i');
|
||||
return reg.test(testedValue);
|
||||
}
|
||||
|
||||
scanNow(): void {
|
||||
this.channel.publishScanEvent(this.repositoryId + "/" + this.tagId);
|
||||
}
|
||||
getScanPermissions(projectId: number): void {
|
||||
|
||||
const hasScanImagePermission = this.userPermissionService.getPermission(projectId,
|
||||
USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE);
|
||||
forkJoin(hasScanImagePermission).subscribe(permissions => {
|
||||
this.hasScanImagePermission = permissions[0] as boolean;
|
||||
if (this.projectId && this.hasScanImagePermission) {
|
||||
this.getProjectScanner();
|
||||
}
|
||||
}, error => {
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
}
|
||||
}
|
@ -62,7 +62,7 @@
|
||||
</div>
|
||||
<div *ngIf="scanner">
|
||||
<span class="bar-scanning-time">{{'SCANNER.SCANNED_BY' | translate }}</span>
|
||||
<span class="margin-left-5">{{scanner?.name}}</span>
|
||||
<span class="margin-left-5">{{getScannerInfo()}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="bar-scanning-time">{{'SCANNER.DURATION' | translate }}</span>
|
||||
|
@ -242,4 +242,15 @@ export class ResultTipHistogramComponent implements OnInit {
|
||||
isThemeLight() {
|
||||
return localStorage.getItem('styleModeLocal') === 'LIGHT';
|
||||
}
|
||||
getScannerInfo(): string {
|
||||
if (this.scanner) {
|
||||
if (this.scanner.name && this.scanner.version) {
|
||||
return `${this.scanner.name}@${this.scanner.version}`;
|
||||
}
|
||||
if (this.scanner.name && !this.scanner.version) {
|
||||
return `${this.scanner.name}`;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -1022,7 +1022,8 @@
|
||||
"COLUMN_DESCRIPTION": "Beschreibung",
|
||||
"FOOT_ITEMS": "Elemente",
|
||||
"FOOT_OF": "von",
|
||||
"IN_ALLOW_LIST": "In CVE Allowliste enthalten"
|
||||
"IN_ALLOW_LIST": "In CVE Allowliste enthalten",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Scan abgeschlossen um:",
|
||||
@ -1048,7 +1049,10 @@
|
||||
"PLACEHOLDER": "Filter Schwachstellen",
|
||||
"PACKAGE": "Paket",
|
||||
"PACKAGES": "Pakete",
|
||||
"SCAN_NOW": "Scan"
|
||||
"SCAN_NOW": "Scan",
|
||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||
"REPORTED_BY": "Reported by {{scanner}}",
|
||||
"NO_SCANNER": "NO SCANNER"
|
||||
},
|
||||
"PUSH_IMAGE": {
|
||||
"TITLE": "Push Befehl",
|
||||
|
@ -1022,7 +1022,8 @@
|
||||
"COLUMN_DESCRIPTION": "Description",
|
||||
"FOOT_ITEMS": "Items",
|
||||
"FOOT_OF": "of",
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist"
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Scan completed time:",
|
||||
@ -1048,7 +1049,10 @@
|
||||
"PLACEHOLDER": "Filter Vulnerabilities",
|
||||
"PACKAGE": "package",
|
||||
"PACKAGES": "packages",
|
||||
"SCAN_NOW": "Scan"
|
||||
"SCAN_NOW": "Scan",
|
||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||
"REPORTED_BY": "Reported by {{scanner}}",
|
||||
"NO_SCANNER": "NO SCANNER"
|
||||
},
|
||||
"PUSH_IMAGE": {
|
||||
"TITLE": "Push Command",
|
||||
|
@ -1022,7 +1022,8 @@
|
||||
"COLUMN_DESCRIPTION": "Description",
|
||||
"FOOT_ITEMS": "Items",
|
||||
"FOOT_OF": "of",
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist"
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Scan completed time:",
|
||||
@ -1048,7 +1049,10 @@
|
||||
"PLACEHOLDER": "Filter Vulnerabilities",
|
||||
"PACKAGE": "package",
|
||||
"PACKAGES": "packages",
|
||||
"SCAN_NOW": "Scan"
|
||||
"SCAN_NOW": "Scan",
|
||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||
"REPORTED_BY": "Reported by {{scanner}}",
|
||||
"NO_SCANNER": "NO SCANNER"
|
||||
},
|
||||
"PUSH_IMAGE": {
|
||||
"TITLE": "Push Command",
|
||||
|
@ -995,7 +995,8 @@
|
||||
"COLUMN_DESCRIPTION": "Description",
|
||||
"FOOT_ITEMS": "Items",
|
||||
"FOOT_OF": "de",
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist"
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Temps d'analyse complète :",
|
||||
@ -1021,7 +1022,10 @@
|
||||
"PLACEHOLDER": "Filtrer les Vulnérabilitiés",
|
||||
"PACKAGE": "paquet",
|
||||
"PACKAGES": "paquets",
|
||||
"SCAN_NOW": "Analyser"
|
||||
"SCAN_NOW": "Analyser",
|
||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||
"REPORTED_BY": "Reported by {{scanner}}",
|
||||
"NO_SCANNER": "NO SCANNER"
|
||||
},
|
||||
"PUSH_IMAGE": {
|
||||
"TITLE": "Push Command",
|
||||
|
@ -1018,7 +1018,8 @@
|
||||
"COLUMN_DESCRIPTION": "Descrição",
|
||||
"FOOT_ITEMS": "Itens",
|
||||
"FOOT_OF": "de",
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist"
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Tempo de conclusão da análise:",
|
||||
@ -1044,7 +1045,10 @@
|
||||
"PLACEHOLDER": "Filtrar Vulnerabilidades",
|
||||
"PACKAGE": "pacote",
|
||||
"PACKAGES": "pacotes",
|
||||
"SCAN_NOW": "Analisar"
|
||||
"SCAN_NOW": "Analisar",
|
||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||
"REPORTED_BY": "Reported by {{scanner}}",
|
||||
"NO_SCANNER": "NO SCANNER"
|
||||
},
|
||||
"PUSH_IMAGE": {
|
||||
"TITLE": "Push Command",
|
||||
|
@ -1022,7 +1022,8 @@
|
||||
"COLUMN_DESCRIPTION": "Açıklama",
|
||||
"FOOT_ITEMS": "Öğeler",
|
||||
"FOOT_OF": "of",
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist"
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "Tarama tamamlanma zamanı:",
|
||||
@ -1048,7 +1049,10 @@
|
||||
"PLACEHOLDER": "Güvenlik Açıklarını Filtrele",
|
||||
"PACKAGE": "paket",
|
||||
"PACKAGES": "paketler",
|
||||
"SCAN_NOW": "Tara"
|
||||
"SCAN_NOW": "Tara",
|
||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||
"REPORTED_BY": "Reported by {{scanner}}",
|
||||
"NO_SCANNER": "NO SCANNER"
|
||||
},
|
||||
"PUSH_IMAGE": {
|
||||
"TITLE": "Push Command",
|
||||
|
@ -1022,7 +1022,8 @@
|
||||
"COLUMN_DESCRIPTION": "简介",
|
||||
"FOOT_ITEMS": "项目",
|
||||
"FOOT_OF": "总共",
|
||||
"IN_ALLOW_LIST": "已入特赦名单"
|
||||
"IN_ALLOW_LIST": "已入特赦名单",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART": {
|
||||
"SCANNING_TIME": "扫描完成时间:",
|
||||
@ -1048,7 +1049,10 @@
|
||||
"PLACEHOLDER": "过滤漏洞",
|
||||
"PACKAGE": "组件",
|
||||
"PACKAGES": "组件",
|
||||
"SCAN_NOW": "扫描"
|
||||
"SCAN_NOW": "扫描",
|
||||
"SCAN_BY": "使用 {{scanner}} 进行扫描",
|
||||
"REPORTED_BY": "结果由 {{scanner}} 提供",
|
||||
"NO_SCANNER": "无扫描器"
|
||||
},
|
||||
"PUSH_IMAGE": {
|
||||
"TITLE": "推送命令",
|
||||
|
@ -1017,7 +1017,8 @@
|
||||
"COLUMN_DESCRIPTION": "簡介",
|
||||
"FOOT_ITEMS": "項目",
|
||||
"FOOT_OF": "總共",
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist"
|
||||
"IN_ALLOW_LIST": "Listed In CVE Allowlist",
|
||||
"CVSS3": "CVSS3"
|
||||
},
|
||||
"CHART":{
|
||||
"SCANNING_TIME": "掃描完成時間:",
|
||||
@ -1043,7 +1044,10 @@
|
||||
"PLACEHOLDER": "過濾漏洞",
|
||||
"PACKAGE": "組件",
|
||||
"PACKAGES": "組件",
|
||||
"SCAN_NOW":"掃描"
|
||||
"SCAN_NOW":"掃描",
|
||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||
"REPORTED_BY": "Reported by {{scanner}}",
|
||||
"NO_SCANNER": "NO SCANNER"
|
||||
},
|
||||
"PUSH_IMAGE":{
|
||||
"TITLE": "推送鏡像的Docker命令",
|
||||
|
@ -246,7 +246,8 @@ export interface VulnerabilityItem extends VulnerabilityBase {
|
||||
links: string[];
|
||||
fix_version: string;
|
||||
layer?: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
preferred_cvss?: {[key: string]: string | number};
|
||||
}
|
||||
|
||||
export interface VulnerabilitySummary {
|
||||
|
@ -3,13 +3,11 @@ import { Injectable, Inject } from "@angular/core";
|
||||
|
||||
import { SERVICE_CONFIG, IServiceConfig } from "../entities/service.config";
|
||||
import {
|
||||
buildHttpRequestOptions,
|
||||
CURRENT_BASE_HREF,
|
||||
DEFAULT_SUPPORTED_MIME_TYPE,
|
||||
HTTP_JSON_OPTIONS
|
||||
} from "../utils/utils";
|
||||
import { RequestQueryParams } from "./RequestQueryParams";
|
||||
import { VulnerabilityDetail, VulnerabilitySummary } from "./interface";
|
||||
import { VulnerabilitySummary } from "./interface";
|
||||
import { map, catchError } from "rxjs/operators";
|
||||
import { Observable, of, throwError as observableThrowError } from "rxjs";
|
||||
|
||||
@ -37,23 +35,6 @@ export abstract class ScanningResultService {
|
||||
queryParams?: RequestQueryParams
|
||||
):
|
||||
| Observable<VulnerabilitySummary>;
|
||||
|
||||
/**
|
||||
* Get the detailed vulnerabilities scanning results.
|
||||
*
|
||||
* @abstract
|
||||
* ** deprecated param {string} tagId
|
||||
* returns {(Observable<VulnerabilityItem[]>)}
|
||||
*
|
||||
* @memberOf ScanningResultService
|
||||
*/
|
||||
abstract getVulnerabilityScanningResults(
|
||||
repoName: string,
|
||||
tagId: string,
|
||||
queryParams?: RequestQueryParams
|
||||
):
|
||||
| Observable<any>;
|
||||
|
||||
/**
|
||||
* Start a new vulnerability scanning
|
||||
*
|
||||
@ -120,31 +101,6 @@ export class ScanningResultDefaultService extends ScanningResultService {
|
||||
|
||||
return of({} as VulnerabilitySummary);
|
||||
}
|
||||
|
||||
getVulnerabilityScanningResults(
|
||||
repoName: string,
|
||||
tagId: string,
|
||||
queryParams?: RequestQueryParams
|
||||
):
|
||||
| Observable<any> {
|
||||
if (!repoName || repoName.trim() === "" || !tagId || tagId.trim() === "") {
|
||||
return observableThrowError("Bad argument");
|
||||
}
|
||||
|
||||
let httpOptions = buildHttpRequestOptions(queryParams);
|
||||
let requestHeaders = httpOptions.headers as HttpHeaders;
|
||||
// Change the accept header to the supported report mime types
|
||||
httpOptions.headers = requestHeaders.set("Accept", DEFAULT_SUPPORTED_MIME_TYPE);
|
||||
|
||||
return this.http
|
||||
.get(
|
||||
`${this._baseUrl}/${repoName}/tags/${tagId}/scan`,
|
||||
httpOptions
|
||||
)
|
||||
.pipe(map(response => response as VulnerabilityDetail)
|
||||
, catchError(error => observableThrowError(error)));
|
||||
}
|
||||
|
||||
startVulnerabilityScanning(
|
||||
projectName: string,
|
||||
repoName: string,
|
||||
|
@ -245,7 +245,8 @@ export const DEFAULT_PAGE_SIZE: number = 15;
|
||||
/**
|
||||
* The default supported mime type
|
||||
*/
|
||||
export const DEFAULT_SUPPORTED_MIME_TYPE = "application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0";
|
||||
export const DEFAULT_SUPPORTED_MIME_TYPES =
|
||||
"application/vnd.security.vulnerability.report; version=1.1, application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0";
|
||||
|
||||
/**
|
||||
* the property name of vulnerability database updated time
|
||||
|
Loading…
Reference in New Issue
Block a user