Update ui to fix some issues (#19101)

1. Add digest filter for vulnerability search, for #19023
2. Fixes #19104

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Shijun Sun 2023-08-07 14:49:50 +08:00 committed by GitHub
parent a036e4a7b0
commit 958bed2ee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 44 additions and 9 deletions

View File

@ -19,6 +19,7 @@ import {
} from './security-hub.interface'; } from './security-hub.interface';
import { ProjectService } from '../../../../../../ng-swagger-gen/services/project.service'; import { ProjectService } from '../../../../../../ng-swagger-gen/services/project.service';
import { VulnerabilityFilterComponent } from './vulnerability-filter/vulnerability-filter.component'; import { VulnerabilityFilterComponent } from './vulnerability-filter/vulnerability-filter.component';
import { DangerousArtifact } from '../../../../../../ng-swagger-gen/models/dangerous-artifact';
@Component({ @Component({
selector: 'app-security-hub', selector: 'app-security-hub',
@ -174,8 +175,11 @@ export class SecurityHubComponent {
this.currentPage = 1; this.currentPage = 1;
this.clrDgRefresh(this.state, [`${OptionType.CVE_ID}=${cveId}`]); this.clrDgRefresh(this.state, [`${OptionType.CVE_ID}=${cveId}`]);
} }
searchRepo(repoName: string) { searchRepo(artifact: DangerousArtifact) {
this.vulnerabilityFilterComponent.selectedOptions = [OptionType.REPO]; this.vulnerabilityFilterComponent.selectedOptions = [
OptionType.REPO,
OptionType.DIGEST,
];
this.vulnerabilityFilterComponent.candidates = [ this.vulnerabilityFilterComponent.candidates = [
OptionType.CVE_ID, OptionType.CVE_ID,
OptionType.SEVERITY, OptionType.SEVERITY,
@ -185,8 +189,14 @@ export class SecurityHubComponent {
OptionType.TAG, OptionType.TAG,
]; ];
this.vulnerabilityFilterComponent.valueMap = {}; this.vulnerabilityFilterComponent.valueMap = {};
this.vulnerabilityFilterComponent.valueMap[OptionType.REPO] = repoName; this.vulnerabilityFilterComponent.valueMap[OptionType.REPO] =
artifact?.repository_name;
this.vulnerabilityFilterComponent.valueMap[OptionType.DIGEST] =
artifact?.digest;
this.currentPage = 1; this.currentPage = 1;
this.clrDgRefresh(this.state, [`${OptionType.REPO}=${repoName}`]); this.clrDgRefresh(this.state, [
`${OptionType.REPO}=${artifact?.repository_name}`,
`${OptionType.DIGEST}=${artifact?.digest}`,
]);
} }
} }

View File

@ -21,6 +21,7 @@ export enum OptionType {
PACKAGE = 'package', PACKAGE = 'package',
TAG = 'tag', TAG = 'tag',
PROJECT_ID = 'project_id', PROJECT_ID = 'project_id',
DIGEST = 'digest',
} }
export const OptionType_I18n_Map = { export const OptionType_I18n_Map = {
@ -32,6 +33,7 @@ export const OptionType_I18n_Map = {
[OptionType.PACKAGE]: 'VULNERABILITY.GRID.COLUMN_PACKAGE', [OptionType.PACKAGE]: 'VULNERABILITY.GRID.COLUMN_PACKAGE',
[OptionType.TAG]: 'REPLICATION.TAG', [OptionType.TAG]: 'REPLICATION.TAG',
[OptionType.PROJECT_ID]: 'SECURITY_HUB.OPTION_PROJECT_ID_NAME', [OptionType.PROJECT_ID]: 'SECURITY_HUB.OPTION_PROJECT_ID_NAME',
[OptionType.DIGEST]: 'P2P_PROVIDER.DIGEST',
}; };
export interface OptionTypeValueMap { export interface OptionTypeValueMap {

View File

@ -24,6 +24,7 @@ export class VulnerabilityFilterComponent {
OptionType.REPO, OptionType.REPO,
OptionType.PACKAGE, OptionType.PACKAGE,
OptionType.TAG, OptionType.TAG,
OptionType.DIGEST,
]; ];
allOptions: string[] = [ allOptions: string[] = [
OptionType.CVE_ID, OptionType.CVE_ID,
@ -33,6 +34,7 @@ export class VulnerabilityFilterComponent {
OptionType.REPO, OptionType.REPO,
OptionType.PACKAGE, OptionType.PACKAGE,
OptionType.TAG, OptionType.TAG,
OptionType.DIGEST,
]; ];
valueMap: OptionTypeValueMap = {}; valueMap: OptionTypeValueMap = {};

View File

@ -88,7 +88,7 @@
{{ securitySummary?.none_cnt || 0 }} {{ securitySummary?.none_cnt || 0 }}
</div> </div>
</div> </div>
<div class="clr-row"> <div class="clr-row" [hidden]="!securitySummary">
<div class="placeholder"> <div class="placeholder">
<div class="pie-chart" id="pie-chart"></div> <div class="pie-chart" id="pie-chart"></div>
</div> </div>
@ -121,7 +121,7 @@
class="search" class="search"
href="javascript:void(0)" href="javascript:void(0)"
appScrollAnchor="{{ vulId }}" appScrollAnchor="{{ vulId }}"
(click)="searchRepoClick(item?.repository_name)" (click)="searchRepoClick(item)"
title="{{ item.repository_name }}"> title="{{ item.repository_name }}">
<span class="ellipsis"> <span class="ellipsis">
<clr-icon shape="search"></clr-icon <clr-icon shape="search"></clr-icon

View File

@ -18,6 +18,7 @@ import {
HarborEvent, HarborEvent,
} from '../../../../../services/event-service/event.service'; } from '../../../../../services/event-service/event.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
highchartsAccessibility(Highcharts); highchartsAccessibility(Highcharts);
@Component({ @Component({
@ -29,7 +30,7 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
@Output() @Output()
searchCVE = new EventEmitter<string>(); searchCVE = new EventEmitter<string>();
@Output() @Output()
searchRepo = new EventEmitter<string>(); searchRepo = new EventEmitter<DangerousArtifact>();
securitySummary: SecuritySummary; securitySummary: SecuritySummary;
readonly vulId: string = VUL_ID; readonly vulId: string = VUL_ID;
readonly severityText = severityText; readonly severityText = severityText;
@ -175,8 +176,8 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
this.searchCVE.emit(cveId); this.searchCVE.emit(cveId);
} }
searchRepoClick(repoName: string) { searchRepoClick(artifact: DangerousArtifact) {
this.searchRepo.emit(repoName); this.searchRepo.emit(artifact);
} }
getColorByTheme(): string { getColorByTheme(): string {

View File

@ -37,8 +37,18 @@ export class AppConfigService {
// Store the application configuration // Store the application configuration
configurations: AppConfig = new AppConfig(); configurations: AppConfig = new AppConfig();
private _bannerMessageClosed: boolean = false;
constructor(private http: HttpClient, private cookie: CookieService) {} constructor(private http: HttpClient, private cookie: CookieService) {}
setBannerMessageClosed(v: boolean) {
this._bannerMessageClosed = v;
}
getBannerMessageClosed(): boolean {
return this._bannerMessageClosed;
}
public load(): Observable<AppConfig> { public load(): Observable<AppConfig> {
return this.http.get(systemInfoEndpoint, HTTP_GET_OPTIONS).pipe( return this.http.get(systemInfoEndpoint, HTTP_GET_OPTIONS).pipe(
map(response => { map(response => {

View File

@ -3,6 +3,7 @@
*ngIf="hasValidBannerMessage()" *ngIf="hasValidBannerMessage()"
[clrAlertType]="getBannerMessageType()" [clrAlertType]="getBannerMessageType()"
[clrAlertAppLevel]="true" [clrAlertAppLevel]="true"
[(clrAlertClosed)]="bannerMessageClosed"
[clrAlertClosable]="getBannerMessageClosable()"> [clrAlertClosable]="getBannerMessageClosable()">
<clr-alert-item> <clr-alert-item>
<span class="alert-text banner-message">{{ <span class="alert-text banner-message">{{

View File

@ -39,6 +39,15 @@ export class AppLevelAlertsComponent implements OnInit, OnDestroy {
private jobServiceDashboardHealthCheckService: JobServiceDashboardHealthCheckService, private jobServiceDashboardHealthCheckService: JobServiceDashboardHealthCheckService,
private appConfigService: AppConfigService private appConfigService: AppConfigService
) {} ) {}
get bannerMessageClosed(): boolean {
return this.appConfigService.getBannerMessageClosed();
}
set bannerMessageClosed(v: boolean) {
this.appConfigService.setBannerMessageClosed(v);
}
ngOnInit() { ngOnInit() {
if ( if (
!( !(