mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 12:15:20 +01:00
Merge pull request #10853 from AllForNothing/fix-scan
Fix scanning function
This commit is contained in:
commit
26f71d47b3
@ -71,7 +71,7 @@
|
|||||||
[(clrDgSelected)]="selectedRow">
|
[(clrDgSelected)]="selectedRow">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn"
|
<button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn"
|
||||||
[disabled]="!(canScanNow() && selectedRow.length==1 && hasEnabledScanner && !depth)" (click)="scanNow()">
|
[disabled]="!(canScanNow() && selectedRow.length==1 && hasEnabledScanner && hasScanImagePermission && !depth)" (click)="scanNow()">
|
||||||
<clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}
|
<clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -225,7 +225,7 @@
|
|||||||
<div class="cell">
|
<div class="cell">
|
||||||
<hbr-vulnerability-bar [scanner]="handleScanOverview(artifact.scan_overview)?.scanner"
|
<hbr-vulnerability-bar [scanner]="handleScanOverview(artifact.scan_overview)?.scanner"
|
||||||
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
|
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
|
||||||
[artifactId]="artifact.id" [summary]="handleScanOverview(artifact.scan_overview)">
|
[artifactDigest]="artifact.digest" [summary]="handleScanOverview(artifact.scan_overview)">
|
||||||
</hbr-vulnerability-bar>
|
</hbr-vulnerability-bar>
|
||||||
</div>
|
</div>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
|
@ -9,7 +9,10 @@ export class AdditionsService {
|
|||||||
constructor(private http: HttpClient) {
|
constructor(private http: HttpClient) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetailByLink(link: string): Observable<any> {
|
getDetailByLink(link: string, shouldReturnText?: boolean): Observable<any> {
|
||||||
return this.http.get(link);
|
if (shouldReturnText) {
|
||||||
|
return this.http.get(link, { observe: 'body', responseType: 'text'} );
|
||||||
|
}
|
||||||
|
return this.http.get(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,42 @@
|
|||||||
<ng-container *ngIf="additionLinks">
|
<ng-container *ngIf="additionLinks">
|
||||||
<h4 class="margin-bottom-025">{{'ARTIFACT.ADDITIONS' | translate}}</h4>
|
<h4 class="margin-bottom-025">{{'ARTIFACT.ADDITIONS' | translate}}</h4>
|
||||||
<clr-tabs>
|
<div class="min-15">
|
||||||
<clr-tab *ngIf="getVulnerability()">
|
<clr-tabs>
|
||||||
<button clrTabLink id="vulnerability">{{'REPOSITORY.VULNERABILITY' | translate}}</button>
|
<clr-tab *ngIf="getVulnerability()">
|
||||||
<clr-tab-content id="vulnerability-content" *clrIfActive>
|
<button clrTabLink id="vulnerability">{{'REPOSITORY.VULNERABILITY' | translate}}</button>
|
||||||
<hbr-artifact-vulnerabilities *ngIf="getBuildHistory()"
|
<clr-tab-content id="vulnerability-content" *clrIfActive>
|
||||||
[vulnerabilitiesLink]="getVulnerability()"></hbr-artifact-vulnerabilities>
|
<hbr-artifact-vulnerabilities [projectName]="projectName"
|
||||||
</clr-tab-content>
|
[projectId]="projectId"
|
||||||
</clr-tab>
|
[repoName]="repoName"
|
||||||
<clr-tab *ngIf="getBuildHistory()">
|
[digest]="digest" [vulnerabilitiesLink]="getVulnerability()"></hbr-artifact-vulnerabilities>
|
||||||
<button clrTabLink id="build-history">{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button>
|
</clr-tab-content>
|
||||||
<clr-tab-content *clrIfActive>
|
</clr-tab>
|
||||||
<hbr-artifact-build-history [buildHistoryLink]="getBuildHistory()"></hbr-artifact-build-history>
|
<clr-tab *ngIf="getBuildHistory()">
|
||||||
</clr-tab-content>
|
<button clrTabLink id="build-history">{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button>
|
||||||
</clr-tab>
|
<clr-tab-content *clrIfActive>
|
||||||
<clr-tab *ngIf="getSummary()">
|
<hbr-artifact-build-history [buildHistoryLink]="getBuildHistory()"></hbr-artifact-build-history>
|
||||||
<button clrTabLink id="summary-link">{{'HELM_CHART.SUMMARY' | translate}}</button>
|
</clr-tab-content>
|
||||||
<clr-tab-content id="summary-content" *clrIfActive>
|
</clr-tab>
|
||||||
<hbr-artifact-summary [summaryLink]="getSummary()"></hbr-artifact-summary>
|
<clr-tab *ngIf="getSummary()">
|
||||||
</clr-tab-content>
|
<button clrTabLink id="summary-link">{{'HELM_CHART.SUMMARY' | translate}}</button>
|
||||||
</clr-tab>
|
<clr-tab-content id="summary-content" *clrIfActive>
|
||||||
<clr-tab *ngIf="getDependencies()">
|
<hbr-artifact-summary [summaryLink]="getSummary()"></hbr-artifact-summary>
|
||||||
<button clrTabLink id="depend-link">{{'HELM_CHART.DEPENDENCIES' | translate}}</button>
|
</clr-tab-content>
|
||||||
<clr-tab-content id="depend-content" *clrIfActive>
|
</clr-tab>
|
||||||
<hbr-artifact-dependencies [dependenciesLink]="getDependencies()"></hbr-artifact-dependencies>
|
<clr-tab *ngIf="getDependencies()">
|
||||||
</clr-tab-content>
|
<button clrTabLink id="depend-link">{{'HELM_CHART.DEPENDENCIES' | translate}}</button>
|
||||||
</clr-tab>
|
<clr-tab-content id="depend-content" *clrIfActive>
|
||||||
<clr-tab *ngIf="getValues()">
|
<hbr-artifact-dependencies [dependenciesLink]="getDependencies()"></hbr-artifact-dependencies>
|
||||||
<button clrTabLink id="value-link">{{'HELM_CHART.VALUES' | translate}}</button>
|
</clr-tab-content>
|
||||||
<clr-tab-content id="value-content" *clrIfActive>
|
</clr-tab>
|
||||||
<hbr-artifact-values [valuesLink]="getValues()"></hbr-artifact-values>
|
<clr-tab *ngIf="getValues()">
|
||||||
</clr-tab-content>
|
<button clrTabLink id="value-link">{{'HELM_CHART.VALUES' | translate}}</button>
|
||||||
</clr-tab>
|
<clr-tab-content id="value-content" *clrIfActive>
|
||||||
</clr-tabs>
|
<hbr-artifact-values [valuesLink]="getValues()"></hbr-artifact-values>
|
||||||
|
</clr-tab-content>
|
||||||
|
</clr-tab>
|
||||||
|
</clr-tabs>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
.margin-bottom-025 {
|
.margin-bottom-025 {
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
.min-15 {
|
||||||
|
min-height: 15rem;
|
||||||
}
|
}
|
@ -10,6 +10,13 @@ import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-l
|
|||||||
})
|
})
|
||||||
export class ArtifactAdditionsComponent implements OnInit {
|
export class ArtifactAdditionsComponent implements OnInit {
|
||||||
@Input() additionLinks: AdditionLinks;
|
@Input() additionLinks: AdditionLinks;
|
||||||
|
@Input() projectName: string;
|
||||||
|
@Input()
|
||||||
|
projectId: number;
|
||||||
|
@Input()
|
||||||
|
repoName: string;
|
||||||
|
@Input()
|
||||||
|
digest: string;
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="row flex-items-xs-right rightPos">
|
<div class="row flex-items-xs-right rightPos">
|
||||||
<div class="flex-xs-middle option-right">
|
<div class="flex-xs-middle option-right">
|
||||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'VULNERABILITY.PLACEHOLDER' | translate}}"></hbr-filter>
|
|
||||||
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
|
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -10,7 +9,19 @@
|
|||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!hasEnabledScanner"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
<div class="clr-row center">
|
||||||
|
<div class="clr-col-1">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col">
|
||||||
|
<div [hidden]="!shouldShowBar()">
|
||||||
|
<hbr-vulnerability-bar [scanner]="scanner"
|
||||||
|
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
|
||||||
|
[artifactDigest]="digest">
|
||||||
|
</hbr-vulnerability-bar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</clr-dg-action-bar>
|
</clr-dg-action-bar>
|
||||||
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
|
<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]="severitySort">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
|
||||||
|
@ -11,4 +11,38 @@
|
|||||||
|
|
||||||
.option-right {
|
.option-right {
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.label-critical {
|
||||||
|
background:red;
|
||||||
|
color:#621501;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.label-danger {
|
||||||
|
background:#e64524!important;
|
||||||
|
color:#621501!important;
|
||||||
|
}
|
||||||
|
.label-medium {
|
||||||
|
background-color: orange;
|
||||||
|
color:#621501;
|
||||||
|
}
|
||||||
|
.label-low {
|
||||||
|
background: #007CBB;
|
||||||
|
color:#cab6b1;
|
||||||
|
}
|
||||||
|
.label-negligible {
|
||||||
|
background-color: green;
|
||||||
|
color:#bad7ba;
|
||||||
|
}
|
||||||
|
.label-unknown {
|
||||||
|
background-color: grey;
|
||||||
|
color:#bad7ba;
|
||||||
|
}
|
||||||
|
.no-border {
|
||||||
|
border: none;
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ArtifactVulnerabilitiesComponent } from './artifact-vulnerabilities.component';
|
import { ArtifactVulnerabilitiesComponent } from './artifact-vulnerabilities.component';
|
||||||
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
import { NO_ERRORS_SCHEMA } from "@angular/core";
|
||||||
import { ClarityModule } from "@clr/angular";
|
import { ClarityModule } from "@clr/angular";
|
||||||
@ -7,9 +6,11 @@ import { AdditionsService } from "../additions.service";
|
|||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { VulnerabilityItem } from "../../../../../../lib/services";
|
import { ScanningResultService, UserPermissionService, VulnerabilityItem } from "../../../../../../lib/services";
|
||||||
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
||||||
|
import { DEFAULT_SUPPORTED_MIME_TYPE } from "../../../../../../lib/utils/utils";
|
||||||
|
|
||||||
|
|
||||||
describe('ArtifactVulnerabilitiesComponent', () => {
|
describe('ArtifactVulnerabilitiesComponent', () => {
|
||||||
@ -35,16 +36,35 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
|||||||
description: 'just a test'
|
description: 'just a test'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
let scanOverview = {};
|
||||||
|
scanOverview[DEFAULT_SUPPORTED_MIME_TYPE] = {};
|
||||||
|
scanOverview[DEFAULT_SUPPORTED_MIME_TYPE].vulnerabilities = mockedVulnerabilities;
|
||||||
const mockedLink: AdditionLink = {
|
const mockedLink: AdditionLink = {
|
||||||
absolute: false,
|
absolute: false,
|
||||||
href: '/test'
|
href: '/test'
|
||||||
};
|
};
|
||||||
const fakedAdditionsService = {
|
const fakedAdditionsService = {
|
||||||
getDetailByLink() {
|
getDetailByLink() {
|
||||||
return of(mockedVulnerabilities);
|
return of(scanOverview);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const fakedUserPermissionService = {
|
||||||
|
hasProjectPermissions() {
|
||||||
|
return of(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const fakedScanningResultService = {
|
||||||
|
getProjectScanner() {
|
||||||
|
return of(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const fakedChannelService = {
|
||||||
|
ArtifactDetail$: {
|
||||||
|
subscribe() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -60,7 +80,10 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
|||||||
declarations: [ArtifactVulnerabilitiesComponent],
|
declarations: [ArtifactVulnerabilitiesComponent],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
{provide: AdditionsService, useValue: fakedAdditionsService}
|
{provide: AdditionsService, useValue: fakedAdditionsService},
|
||||||
|
{provide: UserPermissionService, useValue: fakedUserPermissionService},
|
||||||
|
{provide: ScanningResultService, useValue: fakedScanningResultService},
|
||||||
|
{provide: ChannelService, useValue: fakedChannelService},
|
||||||
],
|
],
|
||||||
schemas: [
|
schemas: [
|
||||||
NO_ERRORS_SCHEMA
|
NO_ERRORS_SCHEMA
|
||||||
@ -72,6 +95,10 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ArtifactVulnerabilitiesComponent);
|
fixture = TestBed.createComponent(ArtifactVulnerabilitiesComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
component.hasScanningPermission = true;
|
||||||
|
component.hasEnabledScanner = true;
|
||||||
|
component.vulnerabilitiesLink = mockedLink;
|
||||||
|
component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,8 +106,6 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
|||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
it('should get vulnerability list and render', async () => {
|
it('should get vulnerability list and render', async () => {
|
||||||
component.vulnerabilitiesLink = mockedLink;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');
|
const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');
|
||||||
|
@ -1,11 +1,23 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { VulnerabilityItem } from "../../../../../../lib/services";
|
import {
|
||||||
|
ScannerVo,
|
||||||
|
ScanningResultService,
|
||||||
|
UserPermissionService,
|
||||||
|
USERSTATICPERMISSION,
|
||||||
|
VulnerabilityItem
|
||||||
|
} from "../../../../../../lib/services";
|
||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../lib/utils/utils";
|
import {
|
||||||
|
DEFAULT_SUPPORTED_MIME_TYPE,
|
||||||
|
SEVERITY_LEVEL_MAP,
|
||||||
|
VULNERABILITY_SEVERITY
|
||||||
|
} from "../../../../../../lib/utils/utils";
|
||||||
|
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
||||||
|
import { ResultBarChartComponent } from "../../../vulnerability-scanning/result-bar-chart.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-artifact-vulnerabilities',
|
selector: 'hbr-artifact-vulnerabilities',
|
||||||
@ -15,17 +27,33 @@ import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../li
|
|||||||
export class ArtifactVulnerabilitiesComponent implements OnInit {
|
export class ArtifactVulnerabilitiesComponent implements OnInit {
|
||||||
@Input()
|
@Input()
|
||||||
vulnerabilitiesLink: AdditionLink;
|
vulnerabilitiesLink: AdditionLink;
|
||||||
|
@Input()
|
||||||
|
projectName: string;
|
||||||
|
@Input()
|
||||||
|
projectId: number;
|
||||||
|
@Input()
|
||||||
|
repoName: string;
|
||||||
|
@Input()
|
||||||
|
digest: string;
|
||||||
|
scan_overview: any;
|
||||||
|
scanner: ScannerVo;
|
||||||
|
|
||||||
scanningResults: VulnerabilityItem[] = [];
|
scanningResults: VulnerabilityItem[] = [];
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
shouldShowLoading: boolean = true;
|
|
||||||
hasEnabledScanner: boolean = false;
|
hasEnabledScanner: boolean = false;
|
||||||
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
||||||
|
hasScanningPermission: boolean = false;
|
||||||
|
onSendingScanCommand: boolean = false;
|
||||||
|
hasShowLoading: boolean = false;
|
||||||
|
@ViewChild(ResultBarChartComponent, {static: false})
|
||||||
|
resultBarChartComponent: ResultBarChartComponent;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private additionsService: AdditionsService,
|
private additionsService: AdditionsService,
|
||||||
|
private userPermissionService: UserPermissionService,
|
||||||
|
private scanningService: ScanningResultService,
|
||||||
|
private channel: ChannelService,
|
||||||
) {
|
) {
|
||||||
const that = this;
|
const that = this;
|
||||||
this.severitySort = {
|
this.severitySort = {
|
||||||
@ -37,29 +65,61 @@ export class ArtifactVulnerabilitiesComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.getVulnerabilities();
|
this.getVulnerabilities();
|
||||||
|
this.getScanningPermission();
|
||||||
|
this.getProjectScanner();
|
||||||
|
this.channel.ArtifactDetail$.subscribe(tag => {
|
||||||
|
this.getVulnerabilities();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getVulnerabilities() {
|
getVulnerabilities() {
|
||||||
if (this.vulnerabilitiesLink
|
if (this.vulnerabilitiesLink
|
||||||
&& !this.vulnerabilitiesLink.absolute
|
&& !this.vulnerabilitiesLink.absolute
|
||||||
&& this.vulnerabilitiesLink.href) {
|
&& this.vulnerabilitiesLink.href) {
|
||||||
// only show loading for one time
|
if (!this.hasShowLoading) {
|
||||||
if (this.shouldShowLoading) {
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.shouldShowLoading = false;
|
this.hasShowLoading = true;
|
||||||
}
|
}
|
||||||
this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href)
|
this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href)
|
||||||
.pipe(finalize(() => this.loading = false))
|
.pipe(finalize(() => this.loading = false))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this.scanningResults = 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;
|
||||||
|
this.scanner = this.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE].scanner;
|
||||||
|
}
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.scanBtnState = ClrLoadingState.ERROR;
|
||||||
|
});
|
||||||
|
}
|
||||||
getLevel(v: VulnerabilityItem): number {
|
getLevel(v: VulnerabilityItem): number {
|
||||||
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
|
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
|
||||||
return SEVERITY_LEVEL_MAP[v.severity];
|
return SEVERITY_LEVEL_MAP[v.severity];
|
||||||
@ -88,4 +148,15 @@ export class ArtifactVulnerabilitiesComponent implements OnInit {
|
|||||||
return 'UNKNOWN';
|
return 'UNKNOWN';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scanNow() {
|
||||||
|
this.onSendingScanCommand = true;
|
||||||
|
this.channel.publishScanEvent(this.repoName + "/" + this.digest);
|
||||||
|
}
|
||||||
|
submitFinish(e: boolean) {
|
||||||
|
this.onSendingScanCommand = e;
|
||||||
|
}
|
||||||
|
shouldShowBar(): boolean {
|
||||||
|
return this.resultBarChartComponent
|
||||||
|
&& (this.resultBarChartComponent.queued || this.resultBarChartComponent.scanning || this.resultBarChartComponent.error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,10 @@
|
|||||||
<div class="row flex-items-xs-center dep-container">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<div class="col-md-12">
|
<clr-dg-column>{{'HELM_CHART.NAME' | translate}}</clr-dg-column>
|
||||||
<table class="table">
|
<clr-dg-column>{{'HELM_CHART.VERSION' | translate}}</clr-dg-column>
|
||||||
<thead>
|
<clr-dg-column>{{'HELM_CHART.REPO' | translate}}</clr-dg-column>
|
||||||
<tr>
|
<clr-dg-row *clrDgItems="let dep of dependencyList" [clrDgItem]='dep' class="history-item">
|
||||||
<th class="left">{{'HELM_CHART.NAME' | translate}}</th>
|
<clr-dg-cell class="left">{{dep.name}}</clr-dg-cell>
|
||||||
<th class="left">{{'HELM_CHART.VERSION' | translate}}</th>
|
<clr-dg-cell class="left">{{dep.version}}</clr-dg-cell>
|
||||||
<th class="left">{{'HELM_CHART.REPO' | translate}}</th>
|
<clr-dg-cell class="left"><a href="{{dep.repository}}">{{dep.repository}}</a></clr-dg-cell>
|
||||||
</tr>
|
</clr-dg-row>
|
||||||
</thead>
|
</clr-datagrid>
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let dep of dependencyList">
|
|
||||||
<td class="left">{{dep.name}}</td>
|
|
||||||
<td class="left">{{dep.version}}</td>
|
|
||||||
<td class="left"><a href="{{dep.repository}}">{{dep.repository}}</a></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -7,6 +7,7 @@ import { ArtifactDependency } from "../models";
|
|||||||
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../../lib/entities/service.config";
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../../lib/entities/service.config";
|
||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import { ClarityModule } from "@clr/angular";
|
||||||
import { CURRENT_BASE_HREF } from "../../../../../../lib/utils/utils";
|
import { CURRENT_BASE_HREF } from "../../../../../../lib/utils/utils";
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +42,8 @@ describe('DependenciesComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot()
|
TranslateModule.forRoot(),
|
||||||
|
ClarityModule
|
||||||
],
|
],
|
||||||
declarations: [DependenciesComponent],
|
declarations: [DependenciesComponent],
|
||||||
providers: [
|
providers: [
|
||||||
@ -70,7 +72,7 @@ describe('DependenciesComponent', () => {
|
|||||||
component.ngOnInit();
|
component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
const trs = fixture.nativeElement.getElementsByTagName('tr');
|
const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');
|
||||||
expect(trs.length).toEqual(3);
|
expect(rows.length).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,8 @@ import { ArtifactDependency } from "../models";
|
|||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import { pipe } from "rxjs";
|
||||||
|
import { finalize } from "rxjs/operators";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -18,6 +20,7 @@ export class DependenciesComponent implements OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
dependenciesLink: AdditionLink;
|
dependenciesLink: AdditionLink;
|
||||||
dependencyList: ArtifactDependency[] = [];
|
dependencyList: ArtifactDependency[] = [];
|
||||||
|
loading: boolean = false;
|
||||||
constructor( private errorHandler: ErrorHandler,
|
constructor( private errorHandler: ErrorHandler,
|
||||||
private additionsService: AdditionsService) {}
|
private additionsService: AdditionsService) {}
|
||||||
|
|
||||||
@ -28,7 +31,10 @@ export class DependenciesComponent implements OnInit {
|
|||||||
if (this.dependenciesLink
|
if (this.dependenciesLink
|
||||||
&& !this.dependenciesLink.absolute
|
&& !this.dependenciesLink.absolute
|
||||||
&& this.dependenciesLink.href) {
|
&& this.dependenciesLink.href) {
|
||||||
this.additionsService.getDetailByLink(this.dependenciesLink.href).subscribe(
|
this.loading = true;
|
||||||
|
this.additionsService.getDetailByLink(this.dependenciesLink.href)
|
||||||
|
.pipe(finalize(() => this.loading = false))
|
||||||
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this.dependencyList = res;
|
this.dependencyList = res;
|
||||||
}, error => {
|
}, error => {
|
||||||
|
@ -34,7 +34,7 @@ export interface Addition {
|
|||||||
export enum ADDITIONS {
|
export enum ADDITIONS {
|
||||||
VULNERABILITIES = 'vulnerabilities',
|
VULNERABILITIES = 'vulnerabilities',
|
||||||
BUILD_HISTORY = 'build_history',
|
BUILD_HISTORY = 'build_history',
|
||||||
SUMMARY = 'readme',
|
SUMMARY = 'readme.md',
|
||||||
VALUES = 'values.yaml',
|
VALUES = 'values.yaml',
|
||||||
DEPENDENCIES = 'dependencies'
|
DEPENDENCIES = 'dependencies'
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<div class="row content-wrapper">
|
<div class="row content-wrapper" *ngIf="!loading">
|
||||||
<div class="col-md-8 md-container pl-1">
|
<div class="col-md-8 md-container pl-1">
|
||||||
<div *ngIf="readme" class="md-div" [innerHTML]="readme | markdown"></div>
|
<div *ngIf="readme" class="md-div" [innerHTML]="readme | markdown"></div>
|
||||||
<div *ngIf="!readme">{{'HELM_CHART.NO_README' | translate}}</div>
|
<div *ngIf="!readme">{{'HELM_CHART.NO_README' | translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table"></table>
|
<div *ngIf="loading" class="clr-row mt-3 center">
|
||||||
|
<span class="spinner spinner-md"></span>
|
||||||
|
</div>
|
@ -5,3 +5,7 @@
|
|||||||
border: solid 1px #ddd;
|
border: solid 1px #ddd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.center {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
@ -195,6 +195,6 @@ describe('SummaryComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
const tables = fixture.nativeElement.getElementsByTagName('table');
|
const tables = fixture.nativeElement.getElementsByTagName('table');
|
||||||
expect(tables.length).toEqual(2);
|
expect(tables.length).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import { finalize } from "rxjs/operators";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -16,6 +17,7 @@ import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
|||||||
export class SummaryComponent implements OnInit {
|
export class SummaryComponent implements OnInit {
|
||||||
@Input() summaryLink: AdditionLink;
|
@Input() summaryLink: AdditionLink;
|
||||||
readme: string;
|
readme: string;
|
||||||
|
loading: boolean = false;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private additionsService: AdditionsService
|
private additionsService: AdditionsService
|
||||||
@ -28,7 +30,10 @@ export class SummaryComponent implements OnInit {
|
|||||||
if (this.summaryLink
|
if (this.summaryLink
|
||||||
&& !this.summaryLink.absolute
|
&& !this.summaryLink.absolute
|
||||||
&& this.summaryLink.href) {
|
&& this.summaryLink.href) {
|
||||||
this.additionsService.getDetailByLink(this.summaryLink.href).subscribe(
|
this.loading = true;
|
||||||
|
this.additionsService.getDetailByLink(this.summaryLink.href, true)
|
||||||
|
.pipe(finalize(() => this.loading = false))
|
||||||
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this.readme = res;
|
this.readme = res;
|
||||||
}, error => {
|
}, error => {
|
||||||
|
@ -2,22 +2,34 @@
|
|||||||
<div *ngIf="valueMode" class="title-container">
|
<div *ngIf="valueMode" class="title-container">
|
||||||
<label>{{'HELM_CHART.SHOW_KV' | translate }}</label>
|
<label>{{'HELM_CHART.SHOW_KV' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="!valueMode" class="title-container">
|
||||||
|
<label>{{'HELM_CHART.SHOW_YAML' | translate }}</label>
|
||||||
|
</div>
|
||||||
<div class="switch-container">
|
<div class="switch-container">
|
||||||
<span class="card-btn" (click)="showYamlFile(false)" (mouseenter)="mouseEnter('value') " (mouseleave)="mouseLeave('value')">
|
<span class="card-btn" (click)="showYamlFile(false)" (mouseenter)="mouseEnter('value') " (mouseleave)="mouseLeave('value')">
|
||||||
<clr-icon size="24" shape="view-list" title='list values' [ngClass]="{'is-highlight': isValueMode || isHovering('value') }"></clr-icon>
|
<clr-icon size="24" shape="view-list" title='list values' [ngClass]="{'is-highlight': isValueMode || isHovering('value') }"></clr-icon>
|
||||||
</span>
|
</span>
|
||||||
|
<span class="list-btn" (click)="showYamlFile(true)" (mouseenter)="mouseEnter('yaml') " (mouseleave)="mouseLeave('yaml')">
|
||||||
|
<clr-icon size="24" shape="file" title="yaml file" [ngClass]="{'is-highlight': !isValueMode || isHovering('yaml') }"></clr-icon>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row value-container">
|
<div class="row value-container" *ngIf="!loading">
|
||||||
<div class="col-xs-8" *ngIf="valueMode">
|
<div class="col-xs-8" *ngIf="valueMode">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let item of values | keyvalue">
|
<tr *ngFor="let item of valuesObj | keyvalue">
|
||||||
<td class="left">{{item?.key}}</td>
|
<td class="left">{{item?.key}}</td>
|
||||||
<td class="left">{{item?.value}}</td>
|
<td class="left">{{item?.value}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-xs-8" *ngIf="!valueMode">
|
||||||
|
<div class="yaml-container" [innerHTML]="values | language : 'yaml' | markdown"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="loading" class="clr-row mt-1 center">
|
||||||
|
<span class="spinner spinner-md"></span>
|
||||||
</div>
|
</div>
|
@ -1,15 +1,16 @@
|
|||||||
.value-container {
|
.value-container {
|
||||||
::ng-deep pre {
|
::ng-deep pre {
|
||||||
min-height: fit-content;
|
min-height: fit-content;
|
||||||
|
max-height: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.values-header {
|
.values-header {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
max-height: max-content;
|
|
||||||
padding-left: 21px;
|
padding-left: 21px;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
@ -16,11 +16,11 @@ describe('ValuesComponent', () => {
|
|||||||
let component: ValuesComponent;
|
let component: ValuesComponent;
|
||||||
let fixture: ComponentFixture<ValuesComponent>;
|
let fixture: ComponentFixture<ValuesComponent>;
|
||||||
|
|
||||||
const mockedValues = {
|
const mockedValues = `
|
||||||
"adminserver.image.pullPolicy": "IfNotPresent",
|
adminserver.image.pullPolicy: IfNotPresent,
|
||||||
"adminserver.image.repository": "vmware/harbor-adminserver",
|
adminserver.image.repository: vmware/harbor-adminserver,
|
||||||
"adminserver.image.tag": "dev"
|
adminserver.image.tag: dev
|
||||||
};
|
`;
|
||||||
const fakedAdditionsService = {
|
const fakedAdditionsService = {
|
||||||
getDetailByLink() {
|
getDetailByLink() {
|
||||||
return of(mockedValues);
|
return of(mockedValues);
|
||||||
@ -56,15 +56,16 @@ describe('ValuesComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ValuesComponent);
|
fixture = TestBed.createComponent(ValuesComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
component.valueMode = true;
|
||||||
|
component.valuesLink = mockedLink;
|
||||||
|
component.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
/*it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});*/
|
});
|
||||||
it('should get values and render', async () => {
|
it('should get values and render', async () => {
|
||||||
component.valuesLink = mockedLink;
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
const trs = fixture.nativeElement.getElementsByTagName('tr');
|
const trs = fixture.nativeElement.getElementsByTagName('tr');
|
||||||
|
@ -6,7 +6,8 @@ import {
|
|||||||
import { AdditionsService } from "../additions.service";
|
import { AdditionsService } from "../additions.service";
|
||||||
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
|
import * as yaml from "js-yaml";
|
||||||
|
import { finalize } from "rxjs/operators";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "hbr-artifact-values",
|
selector: "hbr-artifact-values",
|
||||||
@ -17,22 +18,31 @@ export class ValuesComponent implements OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
valuesLink: AdditionLink;
|
valuesLink: AdditionLink;
|
||||||
|
|
||||||
values: any;
|
values: string;
|
||||||
|
valuesObj: object = {};
|
||||||
|
|
||||||
// Default set to yaml file
|
// Default set to yaml file
|
||||||
valueMode = true;
|
valueMode = false;
|
||||||
valueHover = false;
|
valueHover = false;
|
||||||
yamlHover = true;
|
yamlHover = true;
|
||||||
|
loading: boolean = false;
|
||||||
constructor(private errorHandler: ErrorHandler,
|
constructor(private errorHandler: ErrorHandler,
|
||||||
private additionsService: AdditionsService) {
|
private additionsService: AdditionsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.valuesLink && !this.valuesLink.absolute && this.valuesLink.href) {
|
if (this.valuesLink && !this.valuesLink.absolute && this.valuesLink.href) {
|
||||||
this.additionsService.getDetailByLink(this.valuesLink.href).subscribe(
|
this.loading = true;
|
||||||
|
this.additionsService.getDetailByLink(this.valuesLink.href, true)
|
||||||
|
.pipe(finalize(() => this.loading = false))
|
||||||
|
.subscribe(
|
||||||
res => {
|
res => {
|
||||||
this.values = res;
|
try {
|
||||||
|
this.format(yaml.safeLoad(res));
|
||||||
|
this.values = res;
|
||||||
|
} catch (e) {
|
||||||
|
this.errorHandler.error(e);
|
||||||
|
}
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
}
|
}
|
||||||
@ -71,4 +81,21 @@ export class ValuesComponent implements OnInit {
|
|||||||
this.yamlHover = false;
|
this.yamlHover = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
format(obj: object) {
|
||||||
|
for (let name in obj) {
|
||||||
|
if (obj.hasOwnProperty(name)) {
|
||||||
|
if (obj[name] instanceof Object) {
|
||||||
|
for (let key in obj[name]) {
|
||||||
|
if (obj[name].hasOwnProperty(key)) {
|
||||||
|
obj[`${name}.${key}`] = obj[name][key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete obj[name];
|
||||||
|
this.format(obj);
|
||||||
|
} else {
|
||||||
|
this.valuesObj[name] = obj[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,12 @@
|
|||||||
(refreshArtifact)="refreshArtifact()"></artifact-tag>
|
(refreshArtifact)="refreshArtifact()"></artifact-tag>
|
||||||
|
|
||||||
<!-- Additions -->
|
<!-- Additions -->
|
||||||
<artifact-additions [additionLinks]="artifact?.addition_links"></artifact-additions>
|
<artifact-additions
|
||||||
|
[projectName]="projectName"
|
||||||
|
[projectId]="projectId"
|
||||||
|
[repoName]="repositoryName"
|
||||||
|
[digest]="artifactDigest"
|
||||||
|
[additionLinks]="artifact?.addition_links"></artifact-additions>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div *ngIf="loading" class="clr-row mt-3 center">
|
<div *ngIf="loading" class="clr-row mt-3 center">
|
||||||
<span class="spinner spinner-md"></span>
|
<span class="spinner spinner-md"></span>
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
</clr-control-error>
|
</clr-control-error>
|
||||||
</label>
|
</label>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label class="ml-1">
|
||||||
<button type="button" class="btn btn-sm btn-outline" (click)="cancelAddTag()">{{
|
<button type="button" class="btn btn-sm btn-outline" (click)="cancelAddTag()">{{
|
||||||
'BUTTON.CANCEL' | translate }}
|
'BUTTON.CANCEL' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -119,7 +119,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit, OnDestroy
|
|||||||
return this.systemInfo && this.systemInfo.has_ca_root;
|
return this.systemInfo && this.systemInfo.has_ca_root;
|
||||||
}
|
}
|
||||||
|
|
||||||
goIntoRepo(repoEvt: RepositoryItem): void {
|
goIntoRepo(repoEvt: NewRepository): void {
|
||||||
let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split('/')[1]];
|
let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split('/')[1]];
|
||||||
this.router.navigate(linkUrl);
|
this.router.navigate(linkUrl);
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ import {
|
|||||||
JobLogService,
|
JobLogService,
|
||||||
ScanningResultDefaultService,
|
ScanningResultDefaultService,
|
||||||
ScanningResultService,
|
ScanningResultService,
|
||||||
VulnerabilitySummary
|
|
||||||
} from "../../../../lib/services";
|
} from "../../../../lib/services";
|
||||||
import { CURRENT_BASE_HREF, VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
|
import { CURRENT_BASE_HREF, VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
|
||||||
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
import { SharedModule } from "../../../../lib/utils/shared/shared.module";
|
||||||
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
import { ChannelService } from "../../../../lib/services/channel.service";
|
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||||
import { ArtifactDefaultService, ArtifactService } from "../artifact/artifact.service";
|
import { ArtifactDefaultService, ArtifactService } from "../artifact/artifact.service";
|
||||||
|
import { NativeReportSummary } from "../../../../../ng-swagger-gen/models/native-report-summary";
|
||||||
|
|
||||||
describe('ResultBarChartComponent (inline template)', () => {
|
describe('ResultBarChartComponent (inline template)', () => {
|
||||||
let component: ResultBarChartComponent;
|
let component: ResultBarChartComponent;
|
||||||
@ -24,10 +24,10 @@ describe('ResultBarChartComponent (inline template)', () => {
|
|||||||
let testConfig: IServiceConfig = {
|
let testConfig: IServiceConfig = {
|
||||||
vulnerabilityScanningBaseEndpoint: CURRENT_BASE_HREF + "/vulnerability/testing"
|
vulnerabilityScanningBaseEndpoint: CURRENT_BASE_HREF + "/vulnerability/testing"
|
||||||
};
|
};
|
||||||
let mockData: VulnerabilitySummary = {
|
let mockData: NativeReportSummary = {
|
||||||
scan_status: VULNERABILITY_SCAN_STATUS.SUCCESS,
|
scan_status: VULNERABILITY_SCAN_STATUS.SUCCESS,
|
||||||
severity: "High",
|
severity: "High",
|
||||||
end_time: new Date(),
|
end_time: new Date().toUTCString(),
|
||||||
summary: {
|
summary: {
|
||||||
total: 124,
|
total: 124,
|
||||||
fixable: 50,
|
fixable: 50,
|
||||||
@ -64,7 +64,7 @@ describe('ResultBarChartComponent (inline template)', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ResultBarChartComponent);
|
fixture = TestBed.createComponent(ResultBarChartComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.artifactId = "mockTag";
|
component.artifactDigest = "mockTag";
|
||||||
component.summary = mockData;
|
component.summary = mockData;
|
||||||
|
|
||||||
serviceConfig = TestBed.get(SERVICE_CONFIG);
|
serviceConfig = TestBed.get(SERVICE_CONFIG);
|
||||||
|
@ -7,13 +7,19 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Subscription , timer} from "rxjs";
|
import { Subscription , timer} from "rxjs";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { ScannerVo, ScanningResultService, VulnerabilitySummary } from "../../../../lib/services";
|
import { ScannerVo, ScanningResultService } from "../../../../lib/services";
|
||||||
import { ArtifactDefaultService } from "../artifact/artifact.service";
|
|
||||||
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../lib/utils/error-handler";
|
||||||
import { ChannelService } from "../../../../lib/services/channel.service";
|
import { ChannelService } from "../../../../lib/services/channel.service";
|
||||||
import { clone, CURRENT_BASE_HREF, DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
|
import {
|
||||||
import { ArtifactFront as Artifact } from "../artifact/artifact";
|
clone,
|
||||||
import { NativeReportSummary } from '../../../../../ng-swagger-gen/models/native-report-summary';
|
CURRENT_BASE_HREF,
|
||||||
|
DEFAULT_SUPPORTED_MIME_TYPE,
|
||||||
|
VULNERABILITY_SCAN_STATUS
|
||||||
|
} from "../../../../lib/utils/utils";
|
||||||
|
import { ArtifactService } from "../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
|
import { Artifact } from "../../../../../ng-swagger-gen/models/artifact";
|
||||||
|
import { NativeReportSummary } from "../../../../../ng-swagger-gen/models/native-report-summary";
|
||||||
|
|
||||||
|
|
||||||
const STATE_CHECK_INTERVAL: number = 3000; // 3s
|
const STATE_CHECK_INTERVAL: number = 3000; // 3s
|
||||||
const RETRY_TIMES: number = 3;
|
const RETRY_TIMES: number = 3;
|
||||||
@ -27,8 +33,8 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
@Input() scanner: ScannerVo;
|
@Input() scanner: ScannerVo;
|
||||||
@Input() repoName: string = "";
|
@Input() repoName: string = "";
|
||||||
@Input() projectName: string = "";
|
@Input() projectName: string = "";
|
||||||
@Input() artifactId: string = "";
|
@Input() artifactDigest: string = "";
|
||||||
@Input() summary: VulnerabilitySummary;
|
@Input() summary: NativeReportSummary;
|
||||||
onSubmitting: boolean = false;
|
onSubmitting: boolean = false;
|
||||||
retryCounter: number = 0;
|
retryCounter: number = 0;
|
||||||
stateCheckTimer: Subscription;
|
stateCheckTimer: Subscription;
|
||||||
@ -38,11 +44,10 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
|
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private artifactService: ArtifactDefaultService,
|
private artifactService: ArtifactService,
|
||||||
private scanningService: ScanningResultService,
|
private scanningService: ScanningResultService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private channel: ChannelService,
|
private channel: ChannelService,
|
||||||
private ref: ChangeDetectorRef,
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -55,9 +60,9 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
this.getSummary();
|
this.getSummary();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.scanSubscription = this.channel.scanCommand$.subscribe((artifactId: string) => {
|
this.scanSubscription = this.channel.scanCommand$.subscribe((artifactDigest: string) => {
|
||||||
let myFullTag: string = this.repoName + "/" + this.artifactId;
|
let myFullTag: string = this.repoName + "/" + this.artifactDigest;
|
||||||
if (myFullTag === artifactId) {
|
if (myFullTag === artifactDigest) {
|
||||||
this.scanNow();
|
this.scanNow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -107,26 +112,21 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.repoName || !this.artifactId) {
|
if (!this.repoName || !this.artifactDigest) {
|
||||||
console.log("bad repository or tag");
|
console.log("bad repository or tag");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onSubmitting = true;
|
this.onSubmitting = true;
|
||||||
|
|
||||||
this.scanningService.startVulnerabilityScanning(this.projectName, this.repoName, this.artifactId)
|
this.scanningService.startVulnerabilityScanning(this.projectName, this.repoName, this.artifactDigest)
|
||||||
.pipe(finalize(() => this.submitFinish.emit(false)))
|
.pipe(finalize(() => this.submitFinish.emit(false)))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.onSubmitting = false;
|
this.onSubmitting = false;
|
||||||
|
|
||||||
// Forcely change status to queued after successful submitting
|
// Forcely change status to queued after successful submitting
|
||||||
this.summary = {
|
this.summary = {
|
||||||
scan_status: VULNERABILITY_SCAN_STATUS.PENDING,
|
scan_status: VULNERABILITY_SCAN_STATUS.PENDING,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Forcely refresh view
|
|
||||||
this.forceRefreshView(1000);
|
|
||||||
|
|
||||||
// Start check status util the job is done
|
// Start check status util the job is done
|
||||||
if (!this.stateCheckTimer) {
|
if (!this.stateCheckTimer) {
|
||||||
// Avoid duplicated subscribing
|
// Avoid duplicated subscribing
|
||||||
@ -145,20 +145,22 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSummary(): void {
|
getSummary(): void {
|
||||||
if (!this.repoName || !this.artifactId) {
|
if (!this.repoName || !this.artifactDigest) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.tagService.getTag(this.repoName, this.artifactId)
|
// this.tagService.getTag(this.repoName, this.artifactId)
|
||||||
this.artifactService.getArtifactFromDigest(this.projectName, this.repoName, this.artifactId)
|
this.artifactService.getArtifact({
|
||||||
|
projectName: this.projectName,
|
||||||
|
repositoryName: this.repoName,
|
||||||
|
reference: this.artifactDigest,
|
||||||
|
withScanOverview: true
|
||||||
|
})
|
||||||
.subscribe((artifact: Artifact) => {
|
.subscribe((artifact: Artifact) => {
|
||||||
// To keep the same summary reference, use value copy.
|
// To keep the same summary reference, use value copy.
|
||||||
if (artifact.scan_overview) {
|
if (artifact.scan_overview) {
|
||||||
this.copyValue(artifact.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]);
|
this.copyValue(artifact.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]);
|
||||||
}
|
}
|
||||||
// Forcely refresh view
|
|
||||||
this.forceRefreshView(1000);
|
|
||||||
|
|
||||||
if (!this.queued && !this.scanning) {
|
if (!this.queued && !this.scanning) {
|
||||||
// Scanning should be done
|
// Scanning should be done
|
||||||
if (this.stateCheckTimer) {
|
if (this.stateCheckTimer) {
|
||||||
@ -185,22 +187,8 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
if (!this.summary || !newVal || !newVal.scan_status) { return; }
|
if (!this.summary || !newVal || !newVal.scan_status) { return; }
|
||||||
this.summary = clone(newVal);
|
this.summary = clone(newVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
forceRefreshView(duration: number): void {
|
|
||||||
// Reset timer
|
|
||||||
if (this.timerHandler) {
|
|
||||||
clearInterval(this.timerHandler);
|
|
||||||
}
|
|
||||||
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.timerHandler) {
|
|
||||||
clearInterval(this.timerHandler);
|
|
||||||
this.timerHandler = null;
|
|
||||||
}
|
|
||||||
}, duration);
|
|
||||||
}
|
|
||||||
viewLog(): string {
|
viewLog(): string {
|
||||||
return `${ CURRENT_BASE_HREF }/projects/${this.projectName}/repositories/${this.repoName}
|
return `${ CURRENT_BASE_HREF }/projects/${this.projectName}/repositories/${this.repoName}
|
||||||
/artifacts/${this.artifactId}/scan/${this.summary.report_id}/log`;
|
/artifacts/${this.artifactDigest}/scan/${this.summary.report_id}/log`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export abstract class ScanningResultService {
|
|||||||
abstract startVulnerabilityScanning(
|
abstract startVulnerabilityScanning(
|
||||||
projectName: string,
|
projectName: string,
|
||||||
repoName: string,
|
repoName: string,
|
||||||
artifactId: string
|
artifactDigest: string
|
||||||
): Observable<any>;
|
): Observable<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,15 +148,15 @@ export class ScanningResultDefaultService extends ScanningResultService {
|
|||||||
startVulnerabilityScanning(
|
startVulnerabilityScanning(
|
||||||
projectName: string,
|
projectName: string,
|
||||||
repoName: string,
|
repoName: string,
|
||||||
artifactId: string
|
artifactDigest: string
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
if (!repoName || repoName.trim() === "" || !artifactId || artifactId.trim() === "") {
|
if (!repoName || repoName.trim() === "" || !artifactDigest || artifactDigest.trim() === "") {
|
||||||
return observableThrowError("Bad argument");
|
return observableThrowError("Bad argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.http
|
return this.http
|
||||||
.post(
|
.post(
|
||||||
`${ CURRENT_BASE_HREF }/projects//${projectName}/repositories/${repoName}/artifacts/${artifactId}/scan`,
|
`${ CURRENT_BASE_HREF }/projects//${projectName}/repositories/${repoName}/artifacts/${artifactDigest}/scan`,
|
||||||
HTTP_JSON_OPTIONS
|
HTTP_JSON_OPTIONS
|
||||||
)
|
)
|
||||||
.pipe(map(() => {
|
.pipe(map(() => {
|
||||||
|
Loading…
Reference in New Issue
Block a user