Update security hub ui (#19062)

1. Fixes #19010
2. Fixes #19011
3. Fixes #19012
4. Fixes #19015
5. Fixes #19025
6. Fixes #19026
7. Fixes #19034
8. Fixes #19037

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Shijun Sun 2023-07-31 20:25:05 +08:00 committed by GitHub
parent d93f24a4dc
commit 854e0295d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 87 additions and 44 deletions

View File

@ -29,7 +29,7 @@
'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate 'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate
}}</clr-dg-column> }}</clr-dg-column>
<clr-dg-column class="min-width">{{ <clr-dg-column class="min-width">{{
'VULNERABILITY.PACKAGE' | translate 'VULNERABILITY.GRID.COLUMN_PACKAGE' | translate
}}</clr-dg-column> }}</clr-dg-column>
<clr-dg-column>{{ <clr-dg-column>{{
'VULNERABILITY.GRID.COLUMN_VERSION' | translate 'VULNERABILITY.GRID.COLUMN_VERSION' | translate
@ -42,12 +42,17 @@
</clr-dg-placeholder> </clr-dg-placeholder>
<clr-dg-row *ngFor="let c of vul" [clrDgItem]="c"> <clr-dg-row *ngFor="let c of vul" [clrDgItem]="c">
<clr-dg-cell class="min-width"> <clr-dg-cell class="min-width">
<span *ngIf="!c?.links || c?.links?.length === 0">{{ <span
c.cve_id *ngIf="
}}</span> !c?.links ||
c?.links?.length === 0 ||
(c?.links?.length === 1 && !c?.links[0])
"
>{{ c.cve_id }}</span
>
<a <a
rel="noopener noreferrer" rel="noopener noreferrer"
*ngIf="c?.links && c?.links.length === 1" *ngIf="c?.links && c?.links.length === 1 && c?.links[0]"
href="{{ c?.links[0] }}" href="{{ c?.links[0] }}"
target="_blank" target="_blank"
>{{ c.cve_id }}</a >{{ c.cve_id }}</a

View File

@ -10,12 +10,12 @@ import {
import { finalize } from 'rxjs/operators'; import { finalize } from 'rxjs/operators';
import { VulnerabilityItem } from '../../../../../../ng-swagger-gen/models/vulnerability-item'; import { VulnerabilityItem } from '../../../../../../ng-swagger-gen/models/vulnerability-item';
import { import {
getDigestLink,
getRepoLink,
OptionType, OptionType,
SearchEventData, SearchEventData,
severityText, severityText,
VUL_ID, VUL_ID,
getDigestLink,
getRepoLink,
} 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';
@ -96,14 +96,17 @@ export class SecurityHubComponent {
search(res: SearchEventData) { search(res: SearchEventData) {
if (res?.projectId) { if (res?.projectId) {
this.projectService this.projectService
.getProject({ .listProjects({
projectNameOrId: res.projectId, name: res.projectId,
page: 1,
pageSize: 1,
withDetail: false,
}) })
.subscribe({ .subscribe({
next: project => { next: projects => {
if (project?.project_id) { if (projects?.length) {
res.normal.push( res.normal.push(
`${OptionType.PROJECT_ID}=${project?.project_id}` `${OptionType.PROJECT_ID}=${projects[0]?.project_id}`
); );
this.clrDgRefresh(this.state, res?.normal); this.clrDgRefresh(this.state, res?.normal);
} else { } else {
@ -112,7 +115,8 @@ export class SecurityHubComponent {
} }
}, },
error: err => { error: err => {
this.messageHandler.error(err); res.normal.push(`${OptionType.PROJECT_ID}=0`);
this.clrDgRefresh(this.state, res?.normal);
}, },
}); });
} else { } else {
@ -157,12 +161,30 @@ export class SecurityHubComponent {
searchCVE(cveId: string) { searchCVE(cveId: string) {
this.vulnerabilityFilterComponent.selectedOptions = [OptionType.CVE_ID]; this.vulnerabilityFilterComponent.selectedOptions = [OptionType.CVE_ID];
this.vulnerabilityFilterComponent.candidates = [
OptionType.SEVERITY,
OptionType.CVSS3,
OptionType.PROJECT_ID,
OptionType.REPO,
OptionType.PACKAGE,
OptionType.TAG,
];
this.vulnerabilityFilterComponent.valueMap = {};
this.vulnerabilityFilterComponent.valueMap[OptionType.CVE_ID] = cveId; this.vulnerabilityFilterComponent.valueMap[OptionType.CVE_ID] = cveId;
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(repoName: string) {
this.vulnerabilityFilterComponent.selectedOptions = [OptionType.REPO]; this.vulnerabilityFilterComponent.selectedOptions = [OptionType.REPO];
this.vulnerabilityFilterComponent.candidates = [
OptionType.CVE_ID,
OptionType.SEVERITY,
OptionType.CVSS3,
OptionType.PROJECT_ID,
OptionType.PACKAGE,
OptionType.TAG,
];
this.vulnerabilityFilterComponent.valueMap = {};
this.vulnerabilityFilterComponent.valueMap[OptionType.REPO] = repoName; this.vulnerabilityFilterComponent.valueMap[OptionType.REPO] = repoName;
this.currentPage = 1; this.currentPage = 1;
this.clrDgRefresh(this.state, [`${OptionType.REPO}=${repoName}`]); this.clrDgRefresh(this.state, [`${OptionType.REPO}=${repoName}`]);

View File

@ -29,7 +29,7 @@ export const OptionType_I18n_Map = {
[OptionType.SEVERITY]: 'VULNERABILITY.GRID.COLUMN_SEVERITY', [OptionType.SEVERITY]: 'VULNERABILITY.GRID.COLUMN_SEVERITY',
[OptionType.CVSS3]: 'VULNERABILITY.GRID.CVSS3', [OptionType.CVSS3]: 'VULNERABILITY.GRID.CVSS3',
[OptionType.REPO]: 'SECURITY_HUB.REPO_NAME', [OptionType.REPO]: 'SECURITY_HUB.REPO_NAME',
[OptionType.PACKAGE]: 'VULNERABILITY.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',
}; };

View File

@ -56,7 +56,7 @@ export class SingleBarComponent implements OnChanges {
enabled: true, enabled: true,
distance: -8, distance: -8,
style: { style: {
fontSize: 8, fontSize: '8px',
fontWeight: 1, fontWeight: 1,
}, },
pointFormat: '{point.y}', pointFormat: '{point.y}',

View File

@ -97,7 +97,7 @@ export class VulnerabilityFilterComponent {
item === OptionType.PROJECT_ID && item === OptionType.PROJECT_ID &&
this.valueMap[OptionType.PROJECT_ID] this.valueMap[OptionType.PROJECT_ID]
) { ) {
result.projectId = this.valueMap[OptionType.PROJECT_ID]; result.projectId = this.valueMap[OptionType.PROJECT_ID]?.trim();
} else if (item === OptionType.SEVERITY) { } else if (item === OptionType.SEVERITY) {
if (this.severity) { if (this.severity) {
result.normal.push( result.normal.push(
@ -108,12 +108,12 @@ export class VulnerabilityFilterComponent {
if (this.startScore || this.endScore) { if (this.startScore || this.endScore) {
result.normal.push( result.normal.push(
`${OptionType.CVSS3}=[${ `${OptionType.CVSS3}=[${
this.startScore ? this.startScore : '0.0' this.startScore ? this.startScore?.trim() : '0.0'
}~${this.endScore ? this.endScore : '10.0'}]` }~${this.endScore ? this.endScore?.trim() : '10.0'}]`
); );
} }
} else if (this.valueMap[item]) { } else if (this.valueMap[item]) {
result.normal.push(`${item}=${this.valueMap[item]}`); result.normal.push(`${item}=${this.valueMap[item]?.trim()}`);
} }
}); });
this.search.emit(result); this.search.emit(result);

View File

@ -116,16 +116,18 @@
<div <div
class="clr-row row" class="clr-row row"
*ngFor="let item of securitySummary?.dangerous_artifacts"> *ngFor="let item of securitySummary?.dangerous_artifacts">
<div class="clr-col ellipsis"> <div class="clr-col">
<a <a
class="search" class="search"
href="javascript:void(0)" href="javascript:void(0)"
appScrollAnchor="{{ vulId }}" appScrollAnchor="{{ vulId }}"
(click)="searchRepoClick(item?.repository_name)" (click)="searchRepoClick(item?.repository_name)"
title="{{ item.repository_name }}" title="{{ item.repository_name }}">
><clr-icon shape="search"></clr-icon <span class="ellipsis">
>{{ item.repository_name }}</a <clr-icon shape="search"></clr-icon
> >{{ item.repository_name }}
</span>
</a>
</div> </div>
<div class="clr-col" title="{{ item?.digest }}"> <div class="clr-col" title="{{ item?.digest }}">
<a <a
@ -137,8 +139,10 @@
item.digest item.digest
) )
" "
>{{ item?.digest?.slice(0, 15) }}</a ><span class="ellipsis">{{
> item?.digest?.slice(0, 15)
}}</span>
</a>
</div> </div>
<div class="clr-col"> <div class="clr-col">
<div class="single-bar-container"> <div class="single-bar-container">
@ -163,7 +167,7 @@
{{ 'VULNERABILITY.GRID.CVSS3' | translate }} {{ 'VULNERABILITY.GRID.CVSS3' | translate }}
</div> </div>
<div class="clr-col-4 column"> <div class="clr-col-4 column">
{{ 'VULNERABILITY.PACKAGE' | translate }} {{ 'VULNERABILITY.GRID.COLUMN_PACKAGE' | translate }}
</div> </div>
</div> </div>
</div> </div>
@ -171,16 +175,18 @@
<div <div
class="clr-row row" class="clr-row row"
*ngFor="let item of securitySummary?.dangerous_cves"> *ngFor="let item of securitySummary?.dangerous_cves">
<div class="clr-col-4 ellipsis"> <div class="clr-col-4">
<a <a
class="search" class="search"
href="javascript:void(0)" href="javascript:void(0)"
appScrollAnchor="{{ vulId }}" appScrollAnchor="{{ vulId }}"
(click)="searchCVEClick(item?.cve_id)" (click)="searchCVEClick(item?.cve_id)"
title="{{ item.cve_id }}" title="{{ item.cve_id }}">
><clr-icon shape="search"></clr-icon <span class="ellipsis">
>{{ item.cve_id }}</a <clr-icon shape="search"></clr-icon
> >{{ item.cve_id }}
</span>
</a>
</div> </div>
<div class="clr-col-2"> <div class="clr-col-2">
<ng-container [ngSwitch]="item.severity"> <ng-container [ngSwitch]="item.severity">

View File

@ -113,6 +113,7 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
enabled: false, enabled: false,
}, },
showInLegend: true, showInLegend: true,
borderWidth: 0,
}, },
}, },
legend: { legend: {

View File

@ -1,7 +1,7 @@
import { throwError as observableThrowError, Observable, of } from 'rxjs'; import { throwError as observableThrowError, Observable, of } from 'rxjs';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators'; import { catchError, share } from 'rxjs/operators';
import { Project } from '../../base/project/project-config/project-policy-config/project'; import { Project } from '../../base/project/project-config/project-policy-config/project';
import { ProjectPolicy } from '../../base/project/project-config/project-policy-config/project-policy-config.component'; import { ProjectPolicy } from '../../base/project/project-config/project-policy-config/project-policy-config.component';
import { import {
@ -10,6 +10,7 @@ import {
buildHttpRequestOptionsWithObserveResponse, buildHttpRequestOptionsWithObserveResponse,
CURRENT_BASE_HREF, CURRENT_BASE_HREF,
} from '../units/utils'; } from '../units/utils';
import { CacheObservable } from '../units/cache-util';
/** /**
* Define the service methods to handle the Project related things. * Define the service methods to handle the Project related things.
@ -87,18 +88,26 @@ export abstract class ProjectService {
*/ */
@Injectable() @Injectable()
export class ProjectDefaultService extends ProjectService { export class ProjectDefaultService extends ProjectService {
// to avoid multiple requests for one navigating action
private _sharedProjectObservableMap: {
[key: number | string]: Observable<Project>;
} = {};
constructor(private http: HttpClient) { constructor(private http: HttpClient) {
super(); super();
} }
@CacheObservable({ maxAge: 1000 * 60 })
public getProject(projectId: number | string): Observable<Project> { public getProject(projectId: number | string): Observable<Project> {
if (!projectId) { if (!projectId) {
return observableThrowError('Bad argument'); return observableThrowError('Bad argument');
} }
let baseUrl: string = CURRENT_BASE_HREF + '/projects'; let baseUrl: string = CURRENT_BASE_HREF + '/projects';
return this.http if (this._sharedProjectObservableMap[projectId]) {
return this._sharedProjectObservableMap[projectId];
}
this._sharedProjectObservableMap[projectId] = this.http
.get<Project>(`${baseUrl}/${projectId}`, HTTP_GET_OPTIONS) .get<Project>(`${baseUrl}/${projectId}`, HTTP_GET_OPTIONS)
.pipe(catchError(error => observableThrowError(error))); .pipe(share());
return this._sharedProjectObservableMap[projectId];
} }
public updateProjectPolicy( public updateProjectPolicy(

View File

@ -1886,7 +1886,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "Filter by", "FILTER_BY": "Filter by",
"OPTION_ALL": "All", "OPTION_ALL": "All",
"OPTION_PROJECT_ID_NAME": "Project id or name", "OPTION_PROJECT_ID_NAME": "Project Name",
"SEARCH": "SEARCH", "SEARCH": "SEARCH",
"REPO_NAME": "Repository Name", "REPO_NAME": "Repository Name",
"TOOLTIP": "All filters except CVSS3 only support exact matches", "TOOLTIP": "All filters except CVSS3 only support exact matches",

View File

@ -1887,7 +1887,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "Filter by", "FILTER_BY": "Filter by",
"OPTION_ALL": "All", "OPTION_ALL": "All",
"OPTION_PROJECT_ID_NAME": "Project Id or Name", "OPTION_PROJECT_ID_NAME": "Project Name",
"SEARCH": "SEARCH", "SEARCH": "SEARCH",
"REPO_NAME": "Repository Name", "REPO_NAME": "Repository Name",
"TOOLTIP": "All filters except CVSS3 only support exact matches", "TOOLTIP": "All filters except CVSS3 only support exact matches",

View File

@ -1883,7 +1883,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "Filter by", "FILTER_BY": "Filter by",
"OPTION_ALL": "All", "OPTION_ALL": "All",
"OPTION_PROJECT_ID_NAME": "Project id or name", "OPTION_PROJECT_ID_NAME": "Project Name",
"SEARCH": "SEARCH", "SEARCH": "SEARCH",
"REPO_NAME": "Repository Name", "REPO_NAME": "Repository Name",
"TOOLTIP": "All filters except CVSS3 only support exact matches", "TOOLTIP": "All filters except CVSS3 only support exact matches",

View File

@ -1853,7 +1853,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "Filter by", "FILTER_BY": "Filter by",
"OPTION_ALL": "All", "OPTION_ALL": "All",
"OPTION_PROJECT_ID_NAME": "Project id or name", "OPTION_PROJECT_ID_NAME": "Project Name",
"SEARCH": "SEARCH", "SEARCH": "SEARCH",
"REPO_NAME": "Repository Name", "REPO_NAME": "Repository Name",
"TOOLTIP": "All filters except CVSS3 only support exact matches", "TOOLTIP": "All filters except CVSS3 only support exact matches",

View File

@ -1883,7 +1883,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "Filter by", "FILTER_BY": "Filter by",
"OPTION_ALL": "All", "OPTION_ALL": "All",
"OPTION_PROJECT_ID_NAME": "Project id or name", "OPTION_PROJECT_ID_NAME": "Project Name",
"SEARCH": "SEARCH", "SEARCH": "SEARCH",
"REPO_NAME": "Repository Name", "REPO_NAME": "Repository Name",
"TOOLTIP": "All filters except CVSS3 only support exact matches", "TOOLTIP": "All filters except CVSS3 only support exact matches",

View File

@ -1886,7 +1886,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "Filter by", "FILTER_BY": "Filter by",
"OPTION_ALL": "All", "OPTION_ALL": "All",
"OPTION_PROJECT_ID_NAME": "Project id or name", "OPTION_PROJECT_ID_NAME": "Project Name",
"SEARCH": "SEARCH", "SEARCH": "SEARCH",
"REPO_NAME": "Repository Name", "REPO_NAME": "Repository Name",
"TOOLTIP": "All filters except CVSS3 only support exact matches", "TOOLTIP": "All filters except CVSS3 only support exact matches",

View File

@ -1883,7 +1883,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "过滤条件", "FILTER_BY": "过滤条件",
"OPTION_ALL": "全部", "OPTION_ALL": "全部",
"OPTION_PROJECT_ID_NAME": "项目 ID 或 名称", "OPTION_PROJECT_ID_NAME": "项目名称",
"SEARCH": "搜索", "SEARCH": "搜索",
"REPO_NAME": "仓库名称", "REPO_NAME": "仓库名称",
"TOOLTIP": "CVSS3 除外的所有过滤项只支持精确匹配", "TOOLTIP": "CVSS3 除外的所有过滤项只支持精确匹配",

View File

@ -1875,7 +1875,7 @@
"CVE": "CVEs", "CVE": "CVEs",
"FILTER_BY": "Filter by", "FILTER_BY": "Filter by",
"OPTION_ALL": "All", "OPTION_ALL": "All",
"OPTION_PROJECT_ID_NAME": "Project id or name", "OPTION_PROJECT_ID_NAME": "Project Name",
"SEARCH": "SEARCH", "SEARCH": "SEARCH",
"REPO_NAME": "Repository Name", "REPO_NAME": "Repository Name",
"TOOLTIP": "All filters except CVSS3 only support exact matches", "TOOLTIP": "All filters except CVSS3 only support exact matches",