mirror of
https://github.com/goharbor/harbor
synced 2025-04-14 03:04:05 +00:00
Update the style for severity (#19525)
1.Related issue #19249 Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
b337f51e7e
commit
d0a9754786
|
@ -63,3 +63,7 @@
|
||||||
.min-width {
|
.min-width {
|
||||||
min-width: 9rem !important;
|
min-width: 9rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
min-width: 3rem;
|
||||||
|
}
|
||||||
|
|
|
@ -93,3 +93,12 @@ export function getRepoLink(proId: number | string, repoName: string): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CVSS3_REG = /^([0-9]|10)(\.\d)?$/;
|
export const CVSS3_REG = /^([0-9]|10)(\.\d)?$/;
|
||||||
|
|
||||||
|
export enum SeverityColors {
|
||||||
|
CRITICAL = '#FF4D2E',
|
||||||
|
HIGH = '#FF8F3D',
|
||||||
|
MEDIUM = '#FFCE66',
|
||||||
|
LOW = '#FFF1AD',
|
||||||
|
NONE = '#2EC0FF',
|
||||||
|
NA = 'gray',
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
|
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
|
||||||
import * as echarts from 'echarts/core';
|
import * as echarts from 'echarts/core';
|
||||||
|
import { SeverityColors } from '../security-hub.interface';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-single-bar',
|
selector: 'app-single-bar',
|
||||||
|
@ -23,6 +25,8 @@ export class SingleBarComponent implements OnChanges {
|
||||||
@ViewChild('container', { static: true })
|
@ViewChild('container', { static: true })
|
||||||
container: ElementRef;
|
container: ElementRef;
|
||||||
|
|
||||||
|
constructor(private translate: TranslateService) {}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (changes && changes['dangerousArtifact']) {
|
if (changes && changes['dangerousArtifact']) {
|
||||||
this.initChart();
|
this.initChart();
|
||||||
|
@ -31,74 +35,87 @@ export class SingleBarComponent implements OnChanges {
|
||||||
|
|
||||||
initChart() {
|
initChart() {
|
||||||
const chart = echarts.init(this.container.nativeElement);
|
const chart = echarts.init(this.container.nativeElement);
|
||||||
chart.setOption({
|
const [severity, c, h, m] = [
|
||||||
color: ['red', '#e64524', 'orange'],
|
'VULNERABILITY.GRID.COLUMN_SEVERITY',
|
||||||
title: {
|
'VULNERABILITY.SEVERITY.CRITICAL',
|
||||||
text: '',
|
'VULNERABILITY.SEVERITY.HIGH',
|
||||||
},
|
'VULNERABILITY.SEVERITY.MEDIUM',
|
||||||
tooltip: {
|
];
|
||||||
formatter: '{b}: {c}',
|
this.translate.get([severity, c, h, m]).subscribe(res => {
|
||||||
textStyle: {
|
chart.setOption({
|
||||||
fontSize: '12px',
|
color: [
|
||||||
whiteSpace: 'nowrap',
|
SeverityColors.CRITICAL,
|
||||||
|
SeverityColors.HIGH,
|
||||||
|
SeverityColors.MEDIUM,
|
||||||
|
],
|
||||||
|
title: {
|
||||||
|
text: '',
|
||||||
},
|
},
|
||||||
},
|
tooltip: {
|
||||||
legend: {
|
formatter: '{b}: {c}',
|
||||||
show: false,
|
textStyle: {
|
||||||
},
|
fontSize: '12px',
|
||||||
series: [
|
whiteSpace: 'nowrap',
|
||||||
{
|
|
||||||
type: 'pie',
|
|
||||||
radius: '65%',
|
|
||||||
name: 'Severity',
|
|
||||||
// adjust the start angle
|
|
||||||
startAngle: 180,
|
|
||||||
itemStyle: {
|
|
||||||
borderRadius: 2,
|
|
||||||
borderWidth: 1,
|
|
||||||
},
|
},
|
||||||
labelLine: {
|
},
|
||||||
show: false,
|
legend: {
|
||||||
},
|
show: false,
|
||||||
label: {
|
},
|
||||||
show: true,
|
series: [
|
||||||
position: 'inside',
|
{
|
||||||
formatter: '{c}',
|
type: 'pie',
|
||||||
fontSize: '9px',
|
radius: '65%',
|
||||||
},
|
name: res[severity],
|
||||||
data: [
|
// adjust the start angle
|
||||||
{
|
startAngle: 180,
|
||||||
name: 'Critical',
|
itemStyle: {
|
||||||
value: this.dangerousArtifact?.critical_cnt || 0,
|
borderRadius: 2,
|
||||||
|
borderWidth: 1,
|
||||||
},
|
},
|
||||||
{
|
labelLine: {
|
||||||
name: 'High',
|
show: false,
|
||||||
value: this.dangerousArtifact?.high_cnt || 0,
|
|
||||||
},
|
},
|
||||||
{
|
label: {
|
||||||
name: 'Medium',
|
show: true,
|
||||||
value: this.dangerousArtifact?.medium_cnt || 0,
|
position: 'inside',
|
||||||
|
formatter: '{c}',
|
||||||
|
fontSize: '9px',
|
||||||
},
|
},
|
||||||
{
|
data: [
|
||||||
// make a record to fill the bottom 50%
|
{
|
||||||
value:
|
name: res[c],
|
||||||
this.dangerousArtifact?.critical_cnt +
|
value:
|
||||||
this.dangerousArtifact?.high_cnt +
|
this.dangerousArtifact?.critical_cnt || 0,
|
||||||
this.dangerousArtifact?.medium_cnt || 0,
|
},
|
||||||
itemStyle: {
|
{
|
||||||
// stop the chart from rendering this piece
|
name: res[h],
|
||||||
color: 'none',
|
value: this.dangerousArtifact?.high_cnt || 0,
|
||||||
decal: {
|
},
|
||||||
symbol: 'none',
|
{
|
||||||
|
name: res[m],
|
||||||
|
value: this.dangerousArtifact?.medium_cnt || 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// make a record to fill the bottom 50%
|
||||||
|
value:
|
||||||
|
this.dangerousArtifact?.critical_cnt +
|
||||||
|
this.dangerousArtifact?.high_cnt +
|
||||||
|
this.dangerousArtifact?.medium_cnt || 0,
|
||||||
|
itemStyle: {
|
||||||
|
// stop the chart from rendering this piece
|
||||||
|
color: 'none',
|
||||||
|
decal: {
|
||||||
|
symbol: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
label: {
|
],
|
||||||
show: false,
|
},
|
||||||
},
|
],
|
||||||
},
|
});
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,3 +106,7 @@ $row-height: 48px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
min-width: 3rem;
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ import {
|
||||||
import { SecurityhubService } from '../../../../../../../ng-swagger-gen/services/securityhub.service';
|
import { SecurityhubService } from '../../../../../../../ng-swagger-gen/services/securityhub.service';
|
||||||
import { SecuritySummary } from '../../../../../../../ng-swagger-gen/models/security-summary';
|
import { SecuritySummary } from '../../../../../../../ng-swagger-gen/models/security-summary';
|
||||||
import { MessageHandlerService } from '../../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../../shared/services/message-handler.service';
|
||||||
import { getDigestLink, severityText, VUL_ID } from '../security-hub.interface';
|
import {
|
||||||
|
getDigestLink,
|
||||||
|
SeverityColors,
|
||||||
|
severityText,
|
||||||
|
VUL_ID,
|
||||||
|
} from '../security-hub.interface';
|
||||||
import { HAS_STYLE_MODE, StyleMode } from '../../../../../services/theme';
|
import { HAS_STYLE_MODE, StyleMode } from '../../../../../services/theme';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
|
@ -97,7 +102,14 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
|
||||||
];
|
];
|
||||||
this.translate.get([severity, c, h, m, l, n, u]).subscribe(res => {
|
this.translate.get([severity, c, h, m, l, n, u]).subscribe(res => {
|
||||||
this.chart.setOption({
|
this.chart.setOption({
|
||||||
color: ['red', '#e64524', 'orange', '#007CBB', 'grey', 'green'],
|
color: [
|
||||||
|
SeverityColors.CRITICAL,
|
||||||
|
SeverityColors.HIGH,
|
||||||
|
SeverityColors.MEDIUM,
|
||||||
|
SeverityColors.LOW,
|
||||||
|
SeverityColors.NA,
|
||||||
|
SeverityColors.NONE,
|
||||||
|
],
|
||||||
title: {
|
title: {
|
||||||
text: '',
|
text: '',
|
||||||
},
|
},
|
||||||
|
|
|
@ -58,3 +58,7 @@
|
||||||
.mt-5px {
|
.mt-5px {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
min-width: 3rem;
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import { BuildHistoryComponent } from './artifact-additions/build-history/build-
|
||||||
import { ArtifactVulnerabilitiesComponent } from './artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component';
|
import { ArtifactVulnerabilitiesComponent } from './artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component';
|
||||||
import { ArtifactDefaultService, ArtifactService } from './artifact.service';
|
import { ArtifactDefaultService, ArtifactService } from './artifact.service';
|
||||||
import { ArtifactDetailRoutingResolverService } from '../../../../services/routing-resolvers/artifact-detail-routing-resolver.service';
|
import { ArtifactDetailRoutingResolverService } from '../../../../services/routing-resolvers/artifact-detail-routing-resolver.service';
|
||||||
import { ResultTipComponent } from './vulnerability-scanning/result-tip.component';
|
|
||||||
import { ResultBarChartComponent } from './vulnerability-scanning/result-bar-chart.component';
|
import { ResultBarChartComponent } from './vulnerability-scanning/result-bar-chart.component';
|
||||||
import { ResultTipHistogramComponent } from './vulnerability-scanning/result-tip-histogram/result-tip-histogram.component';
|
import { ResultTipHistogramComponent } from './vulnerability-scanning/result-tip-histogram/result-tip-histogram.component';
|
||||||
import { HistogramChartComponent } from './vulnerability-scanning/histogram-chart/histogram-chart.component';
|
import { HistogramChartComponent } from './vulnerability-scanning/histogram-chart/histogram-chart.component';
|
||||||
|
@ -80,7 +79,6 @@ const routes: Routes = [
|
||||||
DependenciesComponent,
|
DependenciesComponent,
|
||||||
BuildHistoryComponent,
|
BuildHistoryComponent,
|
||||||
ArtifactVulnerabilitiesComponent,
|
ArtifactVulnerabilitiesComponent,
|
||||||
ResultTipComponent,
|
|
||||||
ResultBarChartComponent,
|
ResultBarChartComponent,
|
||||||
ResultTipHistogramComponent,
|
ResultTipHistogramComponent,
|
||||||
HistogramChartComponent,
|
HistogramChartComponent,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { ResultBarChartComponent } from './result-bar-chart.component';
|
import { ResultBarChartComponent } from './result-bar-chart.component';
|
||||||
import { ResultTipComponent } from './result-tip.component';
|
|
||||||
import { ResultTipHistogramComponent } from './result-tip-histogram/result-tip-histogram.component';
|
import { ResultTipHistogramComponent } from './result-tip-histogram/result-tip-histogram.component';
|
||||||
import { HistogramChartComponent } from './histogram-chart/histogram-chart.component';
|
import { HistogramChartComponent } from './histogram-chart/histogram-chart.component';
|
||||||
import {
|
import {
|
||||||
|
@ -35,7 +34,6 @@ describe('ResultBarChartComponent (inline template)', () => {
|
||||||
imports: [SharedTestingModule],
|
imports: [SharedTestingModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
ResultBarChartComponent,
|
ResultBarChartComponent,
|
||||||
ResultTipComponent,
|
|
||||||
ResultTipHistogramComponent,
|
ResultTipHistogramComponent,
|
||||||
HistogramChartComponent,
|
HistogramChartComponent,
|
||||||
],
|
],
|
||||||
|
|
|
@ -125,21 +125,10 @@ $twenty-two-pixel: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.label.label-medium {
|
|
||||||
background-color: #ffe4a9;
|
|
||||||
border: 1px solid orange;
|
|
||||||
color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip-icon-medium {
|
.tip-icon-medium {
|
||||||
color: orange;
|
color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label.label-low {
|
|
||||||
background: rgb(251 255 0 / 38%);
|
|
||||||
color: #c5c50b;
|
|
||||||
border: 1px solid #e6e63f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip-icon-low {
|
.tip-icon-low {
|
||||||
color: #007CBB;
|
color: #007CBB;
|
||||||
|
@ -178,30 +167,6 @@ hr {
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow-critical {
|
|
||||||
box-shadow: 1px -1px 1px red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-high {
|
|
||||||
box-shadow: 1px -1px 1px #e64524;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-medium {
|
|
||||||
box-shadow: 1px -1px 1px orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-low {
|
|
||||||
box-shadow: 1px -1px 1px #007CBB;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-none {
|
|
||||||
box-shadow: 1px -1px 1px green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-unknown {
|
|
||||||
box-shadow: 1px -1px 1px gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-360 {
|
.w-360 {
|
||||||
width: 360px !important;
|
width: 360px !important;
|
||||||
}
|
}
|
||||||
|
@ -248,23 +213,23 @@ hr {
|
||||||
}
|
}
|
||||||
|
|
||||||
.level-critical {
|
.level-critical {
|
||||||
background:red;
|
background:#FF4D2E;
|
||||||
color:red;
|
color:#FF4D2E;
|
||||||
}
|
}
|
||||||
|
|
||||||
.level-high {
|
.level-high {
|
||||||
background:#e64524;
|
background:#FF8F3D;
|
||||||
color:#e64524;
|
color:#FF8F3D;
|
||||||
}
|
}
|
||||||
|
|
||||||
.level-medium {
|
.level-medium {
|
||||||
background-color: orange;
|
background-color: #FFCE66;
|
||||||
color:orange;
|
color:#FFCE66;
|
||||||
}
|
}
|
||||||
|
|
||||||
.level-low {
|
.level-low {
|
||||||
background: #007CBB;
|
background: #FFF1AD;
|
||||||
color:#007CBB;
|
color:#FFF1AD;
|
||||||
}
|
}
|
||||||
|
|
||||||
.level-none {
|
.level-none {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
VULNERABILITY_SEVERITY,
|
VULNERABILITY_SEVERITY,
|
||||||
} from '../../../../../../shared/units/utils';
|
} from '../../../../../../shared/units/utils';
|
||||||
import { HAS_STYLE_MODE, StyleMode } from '../../../../../../services/theme';
|
import { HAS_STYLE_MODE, StyleMode } from '../../../../../../services/theme';
|
||||||
|
import { SeverityColors } from '../../../../../left-side-nav/interrogation-services/vulnerability-database/security-hub.interface';
|
||||||
|
|
||||||
const MIN = 60;
|
const MIN = 60;
|
||||||
const MIN_STR = 'min ';
|
const MIN_STR = 'min ';
|
||||||
|
@ -229,27 +230,27 @@ export class ResultTipHistogramComponent implements OnInit {
|
||||||
{
|
{
|
||||||
text: 'VULNERABILITY.SEVERITY.CRITICAL',
|
text: 'VULNERABILITY.SEVERITY.CRITICAL',
|
||||||
value: this.criticalCount ? this.criticalCount : 0,
|
value: this.criticalCount ? this.criticalCount : 0,
|
||||||
color: 'red',
|
color: SeverityColors.CRITICAL,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'VULNERABILITY.SEVERITY.HIGH',
|
text: 'VULNERABILITY.SEVERITY.HIGH',
|
||||||
value: this.highCount ? this.highCount : 0,
|
value: this.highCount ? this.highCount : 0,
|
||||||
color: '#e64524',
|
color: SeverityColors.HIGH,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'VULNERABILITY.SEVERITY.MEDIUM',
|
text: 'VULNERABILITY.SEVERITY.MEDIUM',
|
||||||
value: this.mediumCount ? this.mediumCount : 0,
|
value: this.mediumCount ? this.mediumCount : 0,
|
||||||
color: 'orange',
|
color: SeverityColors.MEDIUM,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'VULNERABILITY.SEVERITY.LOW',
|
text: 'VULNERABILITY.SEVERITY.LOW',
|
||||||
value: this.lowCount ? this.lowCount : 0,
|
value: this.lowCount ? this.lowCount : 0,
|
||||||
color: '#007CBB',
|
color: SeverityColors.LOW,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'VULNERABILITY.SEVERITY.NONE',
|
text: 'VULNERABILITY.SEVERITY.NONE',
|
||||||
value: this.noneCount ? this.noneCount : 0,
|
value: this.noneCount ? this.noneCount : 0,
|
||||||
color: 'grey',
|
color: SeverityColors.NA,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,176 +0,0 @@
|
||||||
<div class="tip-wrapper tip-position" [style.width]="maxWidth">
|
|
||||||
<clr-tooltip>
|
|
||||||
<div clrTooltipTrigger class="tip-block">
|
|
||||||
<div
|
|
||||||
class="tip-wrapper bar-block-high"
|
|
||||||
[style.width]="tipWidth(5)"></div>
|
|
||||||
<div
|
|
||||||
class="tip-wrapper bar-block-medium"
|
|
||||||
[style.width]="tipWidth(4)"></div>
|
|
||||||
<div
|
|
||||||
class="tip-wrapper bar-block-low"
|
|
||||||
[style.width]="tipWidth(3)"></div>
|
|
||||||
<div
|
|
||||||
class="tip-wrapper bar-block-unknown"
|
|
||||||
[style.width]="tipWidth(2)"></div>
|
|
||||||
<div
|
|
||||||
class="tip-wrapper bar-block-none"
|
|
||||||
[style.width]="tipWidth(1)"></div>
|
|
||||||
</div>
|
|
||||||
<clr-tooltip-content
|
|
||||||
[clrPosition]="'right'"
|
|
||||||
[clrSize]="'lg'"
|
|
||||||
*clrIfOpen>
|
|
||||||
<div [ngSwitch]="scanLevel" class="bar-tooltip-font-larger">
|
|
||||||
<ng-template [ngSwitchCase]="5">
|
|
||||||
<clr-icon
|
|
||||||
shape="exclamation-circle"
|
|
||||||
class="is-error"
|
|
||||||
size="32"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ 'VULNERABILITY.OVERALL_SEVERITY' | translate }}
|
|
||||||
<span class="font-weight-600">{{
|
|
||||||
'VULNERABILITY.SEVERITY.HIGH'
|
|
||||||
| translate
|
|
||||||
| titlecase
|
|
||||||
}}</span></span
|
|
||||||
>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template [ngSwitchCase]="4">
|
|
||||||
<clr-icon
|
|
||||||
*ngIf="hasMedium"
|
|
||||||
shape="exclamation-triangle"
|
|
||||||
class="tip-icon-medium"
|
|
||||||
size="30"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ 'VULNERABILITY.OVERALL_SEVERITY' | translate }}
|
|
||||||
<span class="font-weight-600">{{
|
|
||||||
'VULNERABILITY.SEVERITY.MEDIUM'
|
|
||||||
| translate
|
|
||||||
| titlecase
|
|
||||||
}}</span></span
|
|
||||||
>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template [ngSwitchCase]="3">
|
|
||||||
<clr-icon
|
|
||||||
shape="play"
|
|
||||||
class="tip-icon-low rotate-90"
|
|
||||||
size="28"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ 'VULNERABILITY.OVERALL_SEVERITY' | translate }}
|
|
||||||
<span class="font-weight-600">{{
|
|
||||||
'VULNERABILITY.SEVERITY.LOW' | translate | titlecase
|
|
||||||
}}</span></span
|
|
||||||
>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template [ngSwitchCase]="2">
|
|
||||||
<clr-icon
|
|
||||||
shape="help"
|
|
||||||
size="24"
|
|
||||||
class="help-icon"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ 'VULNERABILITY.OVERALL_SEVERITY' | translate }}
|
|
||||||
<span class="font-weight-600">{{
|
|
||||||
'VULNERABILITY.SEVERITY.UNKNOWN'
|
|
||||||
| translate
|
|
||||||
| titlecase
|
|
||||||
}}</span></span
|
|
||||||
>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template [ngSwitchCase]="1">
|
|
||||||
<clr-icon
|
|
||||||
shape="check-circle"
|
|
||||||
class="is-success"
|
|
||||||
size="32"></clr-icon>
|
|
||||||
<span>{{
|
|
||||||
'VULNERABILITY.NO_VULNERABILITY' | translate
|
|
||||||
}}</span>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div>
|
|
||||||
<span class="bar-tooltip-font bar-tooltip-font-title">{{
|
|
||||||
tipTitle
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="bar-summary bar-tooltip-fon">
|
|
||||||
<div *ngIf="hasHigh" class="bar-summary-item">
|
|
||||||
<span
|
|
||||||
><clr-icon
|
|
||||||
shape="exclamation-circle"
|
|
||||||
class="is-error"
|
|
||||||
size="24"></clr-icon
|
|
||||||
></span>
|
|
||||||
<span>{{ highCount }}</span
|
|
||||||
><span
|
|
||||||
>{{ getPackageText(highCount) | translate }}
|
|
||||||
{{ haveText(highCount) | translate }}
|
|
||||||
{{ 'VULNERABILITY.SEVERITY.HIGH' | translate }}
|
|
||||||
{{ unitText(highCount) | translate }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="hasMedium" class="bar-summary-item">
|
|
||||||
<span
|
|
||||||
><clr-icon
|
|
||||||
*ngIf="hasMedium"
|
|
||||||
shape="exclamation-triangle"
|
|
||||||
class="tip-icon-medium"
|
|
||||||
size="22"></clr-icon
|
|
||||||
></span>
|
|
||||||
<span>{{ mediumCount }}</span
|
|
||||||
><span
|
|
||||||
>{{ getPackageText(mediumCount) | translate }}
|
|
||||||
{{ haveText(mediumCount) | translate }}
|
|
||||||
{{ 'VULNERABILITY.SEVERITY.MEDIUM' | translate }}
|
|
||||||
{{ unitText(mediumCount) | translate }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="hasLow" class="bar-summary-item">
|
|
||||||
<span
|
|
||||||
><clr-icon
|
|
||||||
shape="play"
|
|
||||||
class="tip-icon-low rotate-90"
|
|
||||||
size="20"></clr-icon
|
|
||||||
></span>
|
|
||||||
<span>{{ lowCount }}</span
|
|
||||||
><span
|
|
||||||
>{{ getPackageText(lowCount) | translate }}
|
|
||||||
{{ haveText(lowCount) | translate }}
|
|
||||||
{{ 'VULNERABILITY.SEVERITY.LOW' | translate }}
|
|
||||||
{{ unitText(lowCount) | translate }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="hasUnknown" class="bar-summary-item">
|
|
||||||
<span><clr-icon shape="help" size="18"></clr-icon></span>
|
|
||||||
<span>{{ unknownCount }}</span
|
|
||||||
><span
|
|
||||||
>{{ getPackageText(unknownCount) | translate }}
|
|
||||||
{{ haveText(unknownCount) | translate }}
|
|
||||||
{{ 'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}
|
|
||||||
{{ unitText(unknownCount) | translate }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="hasNone" class="bar-summary-item">
|
|
||||||
<span
|
|
||||||
><clr-icon
|
|
||||||
shape="check-circle"
|
|
||||||
class="is-success"
|
|
||||||
size="24"></clr-icon
|
|
||||||
></span>
|
|
||||||
<span>{{ noneCount }}</span
|
|
||||||
><span>{{
|
|
||||||
'VULNERABILITY.SEVERITY.NONE' | translate
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="bar-scanning-time"
|
|
||||||
>{{ 'VULNERABILITY.CHART.SCANNING_TIME' | translate }}
|
|
||||||
</span>
|
|
||||||
<span>{{
|
|
||||||
completeTimestamp | harborDatetime : 'MM/dd/y HH:mm:ss'
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
</clr-tooltip-content>
|
|
||||||
</clr-tooltip>
|
|
||||||
</div>
|
|
|
@ -1,67 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { ResultTipComponent } from './result-tip.component';
|
|
||||||
import {
|
|
||||||
UserPermissionDefaultService,
|
|
||||||
UserPermissionService,
|
|
||||||
VulnerabilitySummary,
|
|
||||||
} from '../../../../../shared/services';
|
|
||||||
import { VULNERABILITY_SCAN_STATUS } from '../../../../../shared/units/utils';
|
|
||||||
import { SharedTestingModule } from '../../../../../shared/shared.module';
|
|
||||||
|
|
||||||
describe('ResultTipComponent (inline template)', () => {
|
|
||||||
let component: ResultTipComponent;
|
|
||||||
let fixture: ComponentFixture<ResultTipComponent>;
|
|
||||||
let mockData: VulnerabilitySummary = {
|
|
||||||
scan_status: VULNERABILITY_SCAN_STATUS.SUCCESS,
|
|
||||||
severity: 'High',
|
|
||||||
end_time: new Date(),
|
|
||||||
summary: {
|
|
||||||
total: 124,
|
|
||||||
fixable: 50,
|
|
||||||
summary: {
|
|
||||||
High: 5,
|
|
||||||
Low: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [SharedTestingModule],
|
|
||||||
declarations: [ResultTipComponent],
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: UserPermissionService,
|
|
||||||
useClass: UserPermissionDefaultService,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ResultTipComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.summary = mockData;
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reader the bar with different width', () => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
let el: HTMLElement =
|
|
||||||
fixture.nativeElement.querySelector('.bar-block-none');
|
|
||||||
expect(el).not.toBeNull();
|
|
||||||
expect(el.style.width).toEqual('0px');
|
|
||||||
let el2: HTMLElement =
|
|
||||||
fixture.nativeElement.querySelector('.bar-block-high');
|
|
||||||
expect(el2).not.toBeNull();
|
|
||||||
expect(el2.style.width).toEqual('0px');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,198 +0,0 @@
|
||||||
import { Component, Input } from '@angular/core';
|
|
||||||
import {
|
|
||||||
VulnerabilitySeverity,
|
|
||||||
VulnerabilitySummary,
|
|
||||||
} from '../../../../../shared/services';
|
|
||||||
import { VULNERABILITY_SCAN_STATUS } from '../../../../../shared/units/utils';
|
|
||||||
|
|
||||||
export const MIN_TIP_WIDTH = 5;
|
|
||||||
export const MAX_TIP_WIDTH = 100;
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'hbr-vulnerability-summary-chart',
|
|
||||||
templateUrl: './result-tip.component.html',
|
|
||||||
styleUrls: ['./scanning.scss'],
|
|
||||||
})
|
|
||||||
export class ResultTipComponent {
|
|
||||||
_tipTitle: string = '';
|
|
||||||
_highCount: number = 0;
|
|
||||||
_mediumCount: number = 0;
|
|
||||||
_lowCount: number = 0;
|
|
||||||
_unknownCount: number = 0;
|
|
||||||
_noneCount: number = 0;
|
|
||||||
|
|
||||||
totalPackages: number = 0;
|
|
||||||
packagesWithVul: number = 0;
|
|
||||||
|
|
||||||
@Input() summary: VulnerabilitySummary = {
|
|
||||||
scan_status: VULNERABILITY_SCAN_STATUS.NOT_SCANNED,
|
|
||||||
severity: '',
|
|
||||||
end_time: new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
get scanLevel() {
|
|
||||||
let level;
|
|
||||||
if (this._highCount && this._highCount >= 1) {
|
|
||||||
level = VulnerabilitySeverity.HIGH;
|
|
||||||
} else if (this._mediumCount && this._mediumCount >= 1) {
|
|
||||||
level = VulnerabilitySeverity.MEDIUM;
|
|
||||||
} else if (this._lowCount && this._lowCount >= 1) {
|
|
||||||
level = VulnerabilitySeverity.LOW;
|
|
||||||
} else if (this._unknownCount && this._unknownCount >= 1) {
|
|
||||||
level = VulnerabilitySeverity.UNKNOWN;
|
|
||||||
} else if (this.totalPackages === 0) {
|
|
||||||
level = VulnerabilitySeverity.UNKNOWN;
|
|
||||||
} else {
|
|
||||||
level = VulnerabilitySeverity.NONE;
|
|
||||||
}
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
tipWidth(severity: VulnerabilitySeverity): string {
|
|
||||||
let n: number = 0;
|
|
||||||
let m: number = this.totalPackages;
|
|
||||||
|
|
||||||
if (m === 0) {
|
|
||||||
// If no packages recognized, then show grey
|
|
||||||
if (severity === VulnerabilitySeverity.UNKNOWN) {
|
|
||||||
return MAX_TIP_WIDTH + 'px';
|
|
||||||
} else {
|
|
||||||
return 0 + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (severity) {
|
|
||||||
case VulnerabilitySeverity.HIGH:
|
|
||||||
n = this.highCount;
|
|
||||||
break;
|
|
||||||
case VulnerabilitySeverity.MEDIUM:
|
|
||||||
n = this.mediumCount;
|
|
||||||
break;
|
|
||||||
case VulnerabilitySeverity.LOW:
|
|
||||||
n = this.lowCount;
|
|
||||||
break;
|
|
||||||
case VulnerabilitySeverity.UNKNOWN:
|
|
||||||
n = this.unknownCount;
|
|
||||||
break;
|
|
||||||
case VulnerabilitySeverity.NONE:
|
|
||||||
// Show all the left as green bar
|
|
||||||
n =
|
|
||||||
m -
|
|
||||||
(this.highCount +
|
|
||||||
this.mediumCount +
|
|
||||||
this.lowCount +
|
|
||||||
this.unknownCount);
|
|
||||||
if (n < 0) {
|
|
||||||
n = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
n = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let width: number = Math.round((n / m) * MAX_TIP_WIDTH);
|
|
||||||
if (width > 0 && width < MIN_TIP_WIDTH) {
|
|
||||||
width = MIN_TIP_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
return width + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
unitText(count: number): string {
|
|
||||||
if (count > 1) {
|
|
||||||
return 'VULNERABILITY.PLURAL';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'VULNERABILITY.SINGULAR';
|
|
||||||
}
|
|
||||||
|
|
||||||
packageText(count: number): string {
|
|
||||||
return count > 1 ? 'VULNERABILITY.PACKAGES' : 'VULNERABILITY.PACKAGE';
|
|
||||||
}
|
|
||||||
|
|
||||||
getPackageText(count: number): string {
|
|
||||||
return count > 1
|
|
||||||
? 'VULNERABILITY.GRID.COLUMN_PACKAGES'
|
|
||||||
: 'VULNERABILITY.GRID.COLUMN_PACKAGE';
|
|
||||||
}
|
|
||||||
|
|
||||||
haveText(count: number): string {
|
|
||||||
return count > 1 ? 'TAG.HAVE' : 'TAG.HAS';
|
|
||||||
}
|
|
||||||
|
|
||||||
public get completeTimestamp(): Date {
|
|
||||||
return this.summary && this.summary.end_time
|
|
||||||
? this.summary.end_time
|
|
||||||
: new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get hasHigh(): boolean {
|
|
||||||
return this.highCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get hasMedium(): boolean {
|
|
||||||
return this.mediumCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get hasLow(): boolean {
|
|
||||||
return this.lowCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get hasUnknown(): boolean {
|
|
||||||
return this.unknownCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get hasNone(): boolean {
|
|
||||||
return this.noneCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get tipTitle(): string {
|
|
||||||
return this._tipTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get highCount(): number {
|
|
||||||
return this._highCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get mediumCount(): number {
|
|
||||||
return this._mediumCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get lowCount(): number {
|
|
||||||
return this._lowCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get unknownCount(): number {
|
|
||||||
return this._unknownCount;
|
|
||||||
}
|
|
||||||
public get noneCount(): number {
|
|
||||||
return this._noneCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get highSuffix(): string {
|
|
||||||
return this.unitText(this.highCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get mediumSuffix(): string {
|
|
||||||
return this.unitText(this.mediumCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get lowSuffix(): string {
|
|
||||||
return this.unitText(this.lowCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get unknownSuffix(): string {
|
|
||||||
return this.unitText(this.unknownCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get noneSuffix(): string {
|
|
||||||
return this.unitText(this.noneCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get maxWidth(): string {
|
|
||||||
return MAX_TIP_WIDTH + 20 + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,30 +44,6 @@
|
||||||
max-width: 120px;
|
max-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar-block-high {
|
|
||||||
background-color: #e64524;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-block-medium {
|
|
||||||
background-color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-block-low {
|
|
||||||
background-color: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-block-none {
|
|
||||||
background-color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-block-unknown {
|
|
||||||
background-color: grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-tooltip-font {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-tooltip-font-title {
|
.bar-tooltip-font-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user