Fix bugs for scanner UI testing round 1

Signed-off-by: sshijun <sshijun@vmware.com>
This commit is contained in:
sshijun 2019-11-07 13:49:21 +08:00
parent 148cb95363
commit 06013065ff
22 changed files with 111 additions and 57 deletions

View File

@ -104,7 +104,7 @@
</clr-dg-cell>
<clr-dg-cell class="truncated" title="docker pull {{registryUrl}}/{{repoName}}:{{t.name}}">
<div class="cell">
<hbr-copy-input class="margin-top-m4" #copyInput (onCopyError)="onCpError($event)" iconMode="true" defaultValue="docker pull {{registryUrl}}/{{repoName}}:{{t.name}}"></hbr-copy-input>
<hbr-copy-input #copyInput (onCopyError)="onCpError($event)" iconMode="true" defaultValue="docker pull {{registryUrl}}/{{repoName}}:{{t.name}}"></hbr-copy-input>
</div>
</clr-dg-cell>
<clr-dg-cell>

View File

@ -252,7 +252,13 @@ clr-datagrid {
width: 100%;
height: 100%;
}
.margin-top-m4{
margin-top: -4px;
// todo :can be improved
:host::ng-deep clr-dg-row {
.datagrid-select {
.clr-checkbox-wrapper {
input[type=checkbox] + label {
margin: 0;
}
}
}
}

View File

@ -256,6 +256,18 @@ export const VULNERABILITY_SEVERITY = {
CRITICAL: "Critical",
NONE: "None"
};
/**
* The level of vulnerability severity for comparing
*/
export const SEVERITY_LEVEL_MAP = {
"Critical": 6,
"High": 5,
"Medium": 4,
"Low": 3,
"Negligible": 2,
"Unknown": 1,
"None": 0
};
/**
* Calculate page number by state

View File

@ -12,7 +12,7 @@
<div>{{'VULNERABILITY.STATE.SCANNING' | translate}}</div>
<div class="progress loop loop-height"><progress></progress></div>
</div>
<div *ngIf="completed" class="bar-state bar-state-chart margin-top-m15">
<div *ngIf="completed" class="bar-state bar-state-chart">
<hbr-result-tip-histogram [vulnerabilitySummary]="summary"></hbr-result-tip-histogram>
</div>
<div *ngIf="otherStatus" class="bar-state">

View File

@ -13,7 +13,7 @@
<button type="button" class="btn btn-sm btn-secondary" [clrLoading]="scanBtnState" [disabled]="!hasScanImagePermission || !hasEnabledScanner" (click)="scanNow()"><clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}}</button>
</clr-dg-action-bar>
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'severity'">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="severitySort">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'package'">{{'VULNERABILITY.GRID.COLUMN_PACKAGE' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'version'">{{'VULNERABILITY.GRID.COLUMN_VERSION' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'fix_version'">{{'VULNERABILITY.GRID.COLUMN_FIXED' | translate}}</clr-dg-column>
@ -21,7 +21,7 @@
<clr-dg-placeholder>{{'VULNERABILITY.CHART.TOOLTIPS_TITLE_ZERO' | translate}}</clr-dg-placeholder>
<clr-dg-row *clrDgItems="let res of scanningResults">
<clr-dg-cell>
<span *ngIf="!res.links">{{res.id}}</span>
<span *ngIf="!res.links || res.links.length === 0">{{res.id}}</span>
<a *ngIf="res.links && res.links.length === 1" href="{{res.links[0]}}" target="_blank">{{res.id}}</a>
<span *ngIf="res.links && res.links.length > 1">
{{res.id}}

View File

@ -9,10 +9,9 @@ import { forkJoin } from "rxjs";
import { ChannelService } from "../channel/channel.service";
import { UserPermissionService } from "../service/permission.service";
import { USERSTATICPERMISSION } from "../service/permission-static";
import { DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SEVERITY } from '../utils';
import { finalize, map } from "rxjs/operators";
import { ClrLoadingState } from "@clr/angular";
import { DEFAULT_SUPPORTED_MIME_TYPE, SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from '../utils';
import { finalize } from "rxjs/operators";
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
@Component({
selector: 'hbr-vulnerabilities-grid',
@ -30,12 +29,20 @@ export class ResultGridComponent implements OnInit {
hasScanImagePermission: boolean;
hasEnabledScanner: boolean = false;
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
constructor(
private scanningService: ScanningResultService,
private channel: ChannelService,
private userPermissionService: UserPermissionService,
private errorHandler: ErrorHandler,
) { }
) {
const that = this;
this.severitySort = {
compare(a: VulnerabilityItem, b: VulnerabilityItem): number {
return that.getLevel(a) - that.getLevel(b);
}
};
}
ngOnInit(): void {
this.loadResults(this.repositoryId, this.tagId);
@ -44,6 +51,12 @@ export class ResultGridComponent implements OnInit {
this.loadResults(this.repositoryId, this.tagId);
});
}
getLevel(v: VulnerabilityItem): number {
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
return SEVERITY_LEVEL_MAP[v.severity];
}
return 0;
}
getProjectScanner(): void {
this.hasEnabledScanner = false;
this.scanBtnState = ClrLoadingState.LOADING;
@ -78,7 +91,7 @@ export class ResultGridComponent implements OnInit {
return;
}
}
}, error => { this.errorHandler.error(error); });
});
}
// TODO: Should query from back-end service

View File

@ -18,13 +18,13 @@
<div class="black-point-container margin-left-10">
<div class="black-point"></div>
</div>
<span class="margin-left-10 font-weight-800">{{total}}</span>
<span class="margin-left-5">{{'SCANNER.TOTAL' | translate}}</span>
<span class="margin-left-10 font-weight-800 font-size-14">{{total}}</span>
<span class="margin-left-5 font-size-14">{{'SCANNER.TOTAL' | translate}}</span>
<div class="black-point-container margin-left-10">
<div class="black-point "></div>
</div>
<span class="margin-left-10 font-weight-800 color-green">{{fixableCount}}</span>
<span class="margin-left-5 color-green">{{'SCANNER.FIXABLE' | translate}}</span>
<span class="margin-left-10 font-weight-800 color-green font-size-12">{{fixableCount}}</span>
<span class="margin-left-5 color-green font-size-12">{{'SCANNER.FIXABLE' | translate}}</span>
</div>
<div *ngIf="isNone" class="margin-left-5 tip-wrapper bar-block-none shadow-none width-150">{{'VULNERABILITY.NO_VULNERABILITY' | translate }}</div>
</div>

View File

@ -42,7 +42,8 @@ $twenty-two-pixel: 22px;
}
.tip-wrapper {
display: inline-block;
display: flex;
align-items: center;
color: #fff;
text-align: center;
font-size: 10px;
@ -291,3 +292,9 @@ hr {
line-height: $thirty-pixel;
position: relative;
}
.font-size-14 {
font-size: 14px;
}
.font-size-12 {
font-size: 12px;
}

View File

@ -98,11 +98,11 @@ export class ResultTipHistogramComponent implements OnInit {
let str = '';
const min = Math.floor(this.vulnerabilitySummary.duration / MIN);
if (min) {
str += min + MIN_STR;
str += min + ' ' + MIN_STR;
}
const sec = this.vulnerabilitySummary.duration % MIN;
if (sec) {
str += sec + SEC_STR;
str += sec + ' ' + SEC_STR;
}
return str;
}

View File

@ -161,9 +161,6 @@ hr{
background-color: grey;
color:#bad7ba;
}
.margin-top-m15{
margin-top: -15px;
}
.no-border {
border: none;
}

View File

@ -115,7 +115,7 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy {
if (this.selectedRow) {
// Confirm deletion
let msg: ConfirmationMessage = new ConfirmationMessage(
"Confirm Scanner deletion",
"SCANNER.CONFIRM_DELETION",
"SCANNER.DELETION_SUMMARY",
this.selectedRow.name,
[this.selectedRow],

View File

@ -110,10 +110,10 @@
<clr-checkbox-wrapper>
<input name="scanner-skipCertVerify" clrCheckbox formControlName="skipCertVerify"
type="checkbox" id="scanner-skipCertVerify">
<label class="width-10rem" for="scanner-skipCertVerify">{{"SCANNER.SKIP" | translate}}
<label for="scanner-skipCertVerify">{{"SCANNER.SKIP" | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content class="width-14rem" clrPosition="top-left" clrSize="lg" *clrIfOpen>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
{{'SCANNER.SKIP_CERT_VERIFY' | translate}}
</clr-tooltip-content>
</clr-tooltip>
@ -122,10 +122,10 @@
<clr-checkbox-wrapper>
<input name="scanner-use-inner" clrCheckbox formControlName="useInner"
type="checkbox" id="scanner-use-inner">
<label class="width-10rem" for="scanner-use-inner">{{"SCANNER.USE_INNER" | translate}}
<label for="scanner-use-inner">{{"SCANNER.USE_INNER" | translate}}
<clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
<clr-tooltip-content class="width-14rem" clrPosition="top-left" clrSize="lg" *clrIfOpen>
<clr-tooltip-content clrPosition="top-left" clrSize="md" *clrIfOpen>
{{"SCANNER.USE_INNER_TIP" | translate}}
</clr-tooltip-content>
</clr-tooltip>

View File

@ -3,11 +3,4 @@
}
.padding-top-3 {
padding-top: 3px;
}
.width-10rem {
width: 10rem;
}
.width-14rem {
width: 14rem;
}
}

View File

@ -6,6 +6,7 @@ import { ClrLoadingState } from "@clr/angular";
import { finalize } from "rxjs/operators";
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
import { MessageHandlerService } from "../../../shared/message-handler/message-handler.service";
import { TranslateService } from "@ngx-translate/core";
@Component({
selector: "new-scanner-modal",
@ -29,7 +30,8 @@ export class NewScannerModalComponent {
@ViewChild(InlineAlertComponent, { static: false }) inlineAlert: InlineAlertComponent;
constructor(
private configScannerService: ConfigScannerService,
private msgHandler: MessageHandlerService
private msgHandler: MessageHandlerService,
private translate: TranslateService,
) {}
open(): void {
// reset
@ -192,8 +194,12 @@ export class NewScannerModalComponent {
this.checkBtnState = ClrLoadingState.SUCCESS;
this.testMap[this.newScannerFormComponent.newScannerForm.get('url').value] = true;
}, error => {
this.inlineAlert.showInlineError({
message: "SCANNER.TEST_FAILED"
this.translate.get("SCANNER.TEST_FAILED",
{
name: this.newScannerFormComponent.newScannerForm.get('name').value,
url: this.newScannerFormComponent.newScannerForm.get('url').value
}).subscribe((res: string) => {
this.inlineAlert.showInlineError(res);
});
this.checkBtnState = ClrLoadingState.ERROR;
});

View File

@ -135,9 +135,16 @@ export class AddRuleComponent implements OnInit, OnDestroy {
this.rule.scope_selectors.repository[0].pattern = this.rule.scope_selectors.repository[0].pattern.replace(/\s+/g, "");
this.rule.tag_selectors[0].pattern = this.rule.tag_selectors[0].pattern.replace(/\s+/g, "");
if (this.rule.scope_selectors.repository[0].decoration !== "repoMatches"
&& this.rule.scope_selectors.repository[0].pattern.indexOf("**") !== -1) {
this.inlineAlert.showInlineError(INVALID_RULE);
return;
&& this.rule.scope_selectors.repository[0].pattern) {
let str = this.rule.scope_selectors.repository[0].pattern;
str = str.replace(/[{}]/g, "");
const arr = str.split(',');
for (let i = 0; i < arr.length; i++) {
if (arr[i] && arr[i].trim() && arr[i] === "**") {
this.inlineAlert.showInlineError(INVALID_RULE);
return;
}
}
}
if (this.isExistingRule()) {
this.inlineAlert.showInlineError(EXISTING_RULE);

View File

@ -184,9 +184,16 @@ export class AddRuleComponent implements OnInit, OnDestroy {
this.rule.scope_selectors.repository[0].pattern = this.rule.scope_selectors.repository[0].pattern.replace(/\s+/g, "");
this.rule.tag_selectors[0].pattern = this.rule.tag_selectors[0].pattern.replace(/\s+/g, "");
if (this.rule.scope_selectors.repository[0].decoration !== "repoMatches"
&& this.rule.scope_selectors.repository[0].pattern.indexOf("**") !== -1) {
this.inlineAlert.showInlineError(INVALID_RULE);
return;
&& this.rule.scope_selectors.repository[0].pattern) {
let str = this.rule.scope_selectors.repository[0].pattern;
str = str.replace(/[{}]/g, "");
const arr = str.split(',');
for (let i = 0; i < arr.length; i++) {
if (arr[i] && arr[i].trim() && arr[i] === "**") {
this.inlineAlert.showInlineError(INVALID_RULE);
return;
}
}
}
if (this.isExistingRule()) {
this.inlineAlert.showInlineError(EXISTING_RULE);

View File

@ -1291,7 +1291,7 @@
"TEST_CONNECTION": "TEST CONNECTION",
"ADD_SUCCESS": "Successfully added ",
"TEST_PASS": "Test passed",
"TEST_FAILED": "Test failed",
"TEST_FAILED": "Ping: registration {{name}}:{{url}} is unreachable",
"UPDATE_SUCCESS": "Successfully updated",
"SCANNER_COLON": "Scanner:",
"NAME_COLON": "Name:",
@ -1327,6 +1327,7 @@
"OPTIONS": "Options",
"USE_INNER": "Use internal registry address",
"USE_INNER_TIP": "If the option is checked, the scanner will be forced to use the internal registry address to access the related contents.",
"VULNERABILITY_SEVERITY": "Vulnerability severity:"
"VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion"
}
}

View File

@ -1288,7 +1288,7 @@
"TEST_CONNECTION": "TEST CONNECTION",
"ADD_SUCCESS": "Successfully added ",
"TEST_PASS": "Test passed",
"TEST_FAILED": "Test failed",
"TEST_FAILED": "Ping: registration {{name}}:{{url}} is unreachable",
"UPDATE_SUCCESS": "Successfully updated",
"SCANNER_COLON": "Scanner:",
"NAME_COLON": "Name:",
@ -1324,6 +1324,7 @@
"OPTIONS": "Options",
"USE_INNER": "Use internal registry address",
"USE_INNER_TIP": "If the option is checked, the scanner will be forced to use the internal registry address to access the related contents.",
"VULNERABILITY_SEVERITY": "Vulnerability severity:"
"VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion"
}
}

View File

@ -1260,7 +1260,7 @@
"TEST_CONNECTION": "TEST CONNECTION",
"ADD_SUCCESS": "Successfully added ",
"TEST_PASS": "Test passed",
"TEST_FAILED": "Test failed",
"TEST_FAILED": "Ping: registration {{name}}:{{url}} is unreachable",
"UPDATE_SUCCESS": "Successfully updated",
"SCANNER_COLON": "Scanner:",
"NAME_COLON": "Name:",
@ -1296,6 +1296,7 @@
"OPTIONS": "Options",
"USE_INNER": "Use internal registry address",
"USE_INNER_TIP": "If the option is checked, the scanner will be forced to use the internal registry address to access the related contents.",
"VULNERABILITY_SEVERITY": "Vulnerability severity:"
"VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion"
}
}

View File

@ -1285,7 +1285,7 @@
"TEST_CONNECTION": "TEST CONNECTION",
"ADD_SUCCESS": "Successfully added ",
"TEST_PASS": "Test passed",
"TEST_FAILED": "Test failed",
"TEST_FAILED": "Ping: registration {{name}}:{{url}} is unreachable",
"UPDATE_SUCCESS": "Successfully updated",
"SCANNER_COLON": "Scanner:",
"NAME_COLON": "Name:",
@ -1321,7 +1321,8 @@
"OPTIONS": "Options",
"USE_INNER": "Use internal registry address",
"USE_INNER_TIP": "If the option is checked, the scanner will be forced to use the internal registry address to access the related contents.",
"VULNERABILITY_SEVERITY": "Vulnerability severity:"
"VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion"
}
}

View File

@ -1290,7 +1290,7 @@
"TEST_CONNECTION": "TEST CONNECTION",
"ADD_SUCCESS": "Successfully added ",
"TEST_PASS": "Test passed",
"TEST_FAILED": "Test failed",
"TEST_FAILED": "Ping: registration {{name}}:{{url}} is unreachable",
"UPDATE_SUCCESS": "Successfully updated",
"SCANNER_COLON": "Scanner:",
"NAME_COLON": "Name:",
@ -1326,6 +1326,7 @@
"OPTIONS": "Options",
"USE_INNER": "Use internal registry address",
"USE_INNER_TIP": "If the option is checked, the scanner will be forced to use the internal registry address to access the related contents.",
"VULNERABILITY_SEVERITY": "Vulnerability severity:"
"VULNERABILITY_SEVERITY": "Vulnerability severity:",
"CONFIRM_DELETION": "Confirm Scanner deletion"
}
}

View File

@ -1287,7 +1287,7 @@
"TEST_CONNECTION": "测试连接",
"ADD_SUCCESS": "添加成功",
"TEST_PASS": "测试成功",
"TEST_FAILED": "测试失败",
"TEST_FAILED": "Ping: 目标地址{{name}}:{{url}}连接失败",
"UPDATE_SUCCESS": "更新成功",
"SCANNER_COLON": "扫描器:",
"NAME_COLON": "Name:",
@ -1323,6 +1323,7 @@
"OPTIONS": "选项",
"USE_INNER": "使用仓库内部地址",
"USE_INNER_TIP": "选中此项,扫描器将使用仓库内部地址访问其相关内容",
"VULNERABILITY_SEVERITY": "漏洞严重度:"
"VULNERABILITY_SEVERITY": "漏洞严重度:",
"CONFIRM_DELETION": "删除扫描器确认"
}
}