You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by sa...@apache.org on 2019/05/16 13:14:09 UTC
[metron] branch master updated: METRON-1997 Replace Threat Triage
Score Field Slider with Text Box (ruffle1986 via sardell) closes
apache/metron#1334
This is an automated email from the ASF dual-hosted git repository.
sardell pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/metron.git
The following commit(s) were added to refs/heads/master by this push:
new fcd681d METRON-1997 Replace Threat Triage Score Field Slider with Text Box (ruffle1986 via sardell) closes apache/metron#1334
fcd681d is described below
commit fcd681d10256c3cc44722cff694d08649ea27673
Author: ruffle1986 <ft...@gmail.com>
AuthorDate: Thu May 16 15:13:25 2019 +0200
METRON-1997 Replace Threat Triage Score Field Slider with Text Box (ruffle1986 via sardell) closes apache/metron#1334
---
.../metron-config/src/app/model/risk-level-rule.ts | 8 +-
...sensor-parser-config-readonly.component.spec.ts | 8 +-
.../rule-editor/sensor-rule-editor.component.html | 50 +++++--
.../rule-editor/sensor-rule-editor.component.scss | 21 +++
.../sensor-rule-editor.component.spec.ts | 90 +++++++++++--
.../rule-editor/sensor-rule-editor.component.ts | 22 +++-
.../rule-editor/sensor-rule-editor.module.ts | 3 +-
.../sensor-threat-triage.component.html | 42 ++----
.../sensor-threat-triage.component.scss | 63 +++------
.../sensor-threat-triage.component.spec.ts | 145 +--------------------
.../sensor-threat-triage.component.ts | 105 +--------------
.../app/shared/ace-editor/ace-editor.component.ts | 24 +++-
.../src/assets/ace/mode-javascript.js | 1 +
13 files changed, 223 insertions(+), 359 deletions(-)
diff --git a/metron-interface/metron-config/src/app/model/risk-level-rule.ts b/metron-interface/metron-config/src/app/model/risk-level-rule.ts
index 1cd4e8a..5d728d4 100644
--- a/metron-interface/metron-config/src/app/model/risk-level-rule.ts
+++ b/metron-interface/metron-config/src/app/model/risk-level-rule.ts
@@ -16,8 +16,8 @@
* limitations under the License.
*/
export class RiskLevelRule {
- name: string = '';
- comment: string = '';
- rule: string = '';
- score: number = 0;
+ name = '';
+ comment = '';
+ rule = '';
+ score = '';
}
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts
index 1a027f5..a6b540f 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts
@@ -462,13 +462,13 @@ describe('Component: SensorParserConfigReadonly', () => {
riskLevelRules: [
{
rule: "IN_SUBNET(ip_dst_addr, '192.168.0.0/24')",
- score: 3,
+ score: '3',
name: 'test1',
comment: 'This is a comment'
},
{
rule: "user.type in [ 'admin', 'power' ] and asset.type == 'web'",
- score: 3,
+ score: '3',
name: 'test2',
comment: 'This is another comment'
}
@@ -480,13 +480,13 @@ describe('Component: SensorParserConfigReadonly', () => {
let expected: RiskLevelRule[] = [
{
rule: "IN_SUBNET(ip_dst_addr, '192.168.0.0/24')",
- score: 3,
+ score: '3',
name: 'test1',
comment: 'This is a comment'
},
{
rule: "user.type in [ 'admin', 'power' ] and asset.type == 'web'",
- score: 3,
+ score: '3',
name: 'test2',
comment: 'This is another comment'
}
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html
index 512a990..21d0552 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html
@@ -25,24 +25,56 @@
<input type="text" class="form-control" name="ruleName" [(ngModel)]="newRiskLevelRule.name">
</div>
<div class="form-group">
- <label attr.for="statement">TEXT</label>
- <textarea rows="30" class="form-control" name="ruleValue" [(ngModel)]="newRiskLevelRule.rule" style="height: inherit"></textarea>
+ <label attr.for="statement">RULE</label>
+ <metron-config-ace-editor
+ name="ruleValue"
+ [(ngModel)]="newRiskLevelRule.rule"
+ [liveAutocompletion]="false"
+ [enableSnippets]="false"
+ [useWorker]="false"
+ [type]="'STELLAR'"
+ (onChange)="onRuleChange()"
+ [placeHolder]="'Enter a valid Stellar expression'"
+ [wrapLimitRangeMin]="null"
+ [wrapLimitRangeMax]="null"
+ data-qe-id="score-editor"
+ >
+ </metron-config-ace-editor>
+ <span *ngIf="!isRuleValid" class="warning-text">Invalid Stellar expression</span>
</div>
</form>
<form class="form-inline">
- <div class="form-group">
- <label attr.for="statement" style="width: 100%">SCORE ADJUSTMENT</label>
- <input class="score-slider" name="scoreSlider" type="range" min="0" max="100" [(ngModel)]="newRiskLevelRule.score" style="width: 72%; margin-right: 4px">
- <div style="width: 25%; display: inline-block">
- <metron-config-number-spinner name="scoreText" [(ngModel)]="newRiskLevelRule.score" [min]="0" [max]="100"> </metron-config-number-spinner>
- </div>
+ <div class="form-group score">
+ <label class="score-label" attr.for="statement">SCORE ADJUSTMENT</label>
+ <metron-config-ace-editor
+ name="scoreExpression"
+ [(ngModel)]="newRiskLevelRule.score"
+ [liveAutocompletion]="false"
+ [enableSnippets]="false"
+ [useWorker]="false"
+ [type]="'STELLAR'"
+ (onChange)="onScoreChange()"
+ [placeHolder]="'Enter a valid Stellar expression'"
+ [wrapLimitRangeMin]="null"
+ [wrapLimitRangeMax]="null"
+ data-qe-id="score-editor"
+ >
+ </metron-config-ace-editor>
+ <span *ngIf="!isScoreValid" class="warning-text">Invalid Stellar expression</span>
</div>
</form>
<div class="form-group">
<div class="form-seperator-edit"></div>
<div class="button-row">
- <button type="submit" class="btn form-enable-disable-button" (click)="onSave()">SAVE</button>
+ <button
+ type="submit"
+ class="btn form-enable-disable-button"
+ (click)="onSave()"
+ data-qe-id="save-score"
+ >
+ SAVE
+ </button>
<button class="btn form-enable-disable-button ml-1" (click)="onCancel()" >CANCEL</button>
</div>
</div>
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss
index ca6c7d7..19d92c5 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss
@@ -88,3 +88,24 @@ input[type=range]::-moz-range-thumb
background-color: $range-gradient-start;
}
+.score {
+
+ &.form-group {
+ display: block;
+ width: 100%;
+ }
+ .score-label {
+ display: block;
+ margin-bottom: 5px;
+ }
+ .score-input {
+ width: 100%;
+ }
+}
+
+.warning-text {
+ color: #C0661D;
+ font-size: 12px;
+ font-family: Roboto-Medium;
+}
+
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts
index 2f8cd24..d26a526 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts
@@ -16,29 +16,44 @@
* limitations under the License.
*/
-import {async, TestBed, ComponentFixture} from '@angular/core/testing';
+import {async, TestBed, ComponentFixture, inject} from '@angular/core/testing';
import {SensorRuleEditorComponent} from './sensor-rule-editor.component';
import {SharedModule} from '../../../shared/shared.module';
import {NumberSpinnerComponent} from '../../../shared/number-spinner/number-spinner.component';
import {RiskLevelRule} from '../../../model/risk-level-rule';
+import {AceEditorModule} from '../../../shared/ace-editor/ace-editor.module';
+import {StellarService} from '../../../service/stellar.service';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { AppConfigService } from 'app/service/app-config.service';
+import { By } from '@angular/platform-browser';
+import { Observable } from 'rxjs';
describe('Component: SensorRuleEditorComponent', () => {
let fixture: ComponentFixture<SensorRuleEditorComponent>;
let component: SensorRuleEditorComponent;
+ let stellarService: StellarService;
beforeEach(async(() => {
TestBed.configureTestingModule({
- imports: [SharedModule
- ],
+ imports: [SharedModule, AceEditorModule, HttpClientTestingModule],
declarations: [ SensorRuleEditorComponent, NumberSpinnerComponent ],
providers: [
- SensorRuleEditorComponent
- ]
+ SensorRuleEditorComponent,
+ StellarService,
+ {
+ provide: AppConfigService,
+ useValue: {
+ appConfigStatic: {},
+ getApiRoot: () => '/api/v1'
+ }
+ }]
});
fixture = TestBed.createComponent(SensorRuleEditorComponent);
component = fixture.componentInstance;
+ stellarService = TestBed.get(StellarService);
+ fixture.detectChanges();
}));
it('should create an instance', () => {
@@ -48,6 +63,17 @@ describe('Component: SensorRuleEditorComponent', () => {
it('should edit rules', async(() => {
let numCancelled = 0;
let savedRule = new RiskLevelRule();
+
+ stellarService.validateRules = (expressions: string[]) => {
+ return new Observable((observer) => {
+ const response = {
+ [expressions[0]]: true,
+ [expressions[1]]: true,
+ };
+ observer.next(response);
+ });
+ }
+
component.onCancelTextEditor.subscribe((cancelled: boolean) => {
numCancelled++;
});
@@ -55,20 +81,66 @@ describe('Component: SensorRuleEditorComponent', () => {
savedRule = rule;
});
- component.riskLevelRule = {name: 'rule1', rule: 'initial rule', score: 1, comment: ''};
+ component.riskLevelRule = {name: 'rule1', rule: 'initial rule', score: '1', comment: ''};
component.ngOnInit();
component.onSave();
- let rule1 = Object.assign(new RiskLevelRule(), {name: 'rule1', rule: 'initial rule', score: 1, comment: ''});
+ let rule1 = Object.assign(new RiskLevelRule(), {name: 'rule1', rule: 'initial rule', score: '1', comment: ''});
expect(savedRule).toEqual(rule1);
- component.riskLevelRule = {name: 'rule2', rule: 'new rule', score: 2, comment: ''};
+ component.riskLevelRule = {name: 'rule2', rule: 'new rule', score: '2', comment: ''};
component.ngOnInit();
component.onSave();
- let rule2 = Object.assign(new RiskLevelRule(), {name: 'rule2', rule: 'new rule', score: 2, comment: ''});
+ let rule2 = Object.assign(new RiskLevelRule(), {name: 'rule2', rule: 'new rule', score: '2', comment: ''});
expect(savedRule).toEqual(rule2);
expect(numCancelled).toEqual(0);
component.onCancel();
expect(numCancelled).toEqual(1);
}));
+
+ it('should warn if either the rule or the score is invalid', inject(
+ [HttpTestingController],
+ (httpMock: HttpTestingController) => {
+ const saveButton = fixture.debugElement.query(By.css('[data-qe-id="save-score"]'));
+
+ component.newRiskLevelRule.rule = 'value > 10';
+ component.newRiskLevelRule.score = 'value &&&&';
+ fixture.detectChanges();
+ saveButton.nativeElement.click();
+ let validateRequest = httpMock.expectOne('/api/v1/stellar/validate/rules');
+ validateRequest.flush({
+ [component.newRiskLevelRule.rule]: true,
+ [component.newRiskLevelRule.score]: false
+ });
+ fixture.detectChanges();
+ let warning = fixture.debugElement.queryAll(By.css('.warning-text'));
+ expect(warning.length).toBe(1);
+
+ component.newRiskLevelRule.rule = 'value > 10';
+ component.newRiskLevelRule.score = 'value * 10';
+ fixture.detectChanges();
+ saveButton.nativeElement.click();
+ validateRequest = httpMock.expectOne('/api/v1/stellar/validate/rules');
+ validateRequest.flush({
+ [component.newRiskLevelRule.score]: true,
+ [component.newRiskLevelRule.rule]: true
+ });
+ fixture.detectChanges();
+ warning = fixture.debugElement.queryAll(By.css('.warning-text'));
+ expect(warning.length).toBe(0);
+
+ component.newRiskLevelRule.rule = 'value &&&&';
+ component.newRiskLevelRule.score = 'value &&& &&&&';
+ fixture.detectChanges();
+ saveButton.nativeElement.click();
+ validateRequest = httpMock.expectOne('/api/v1/stellar/validate/rules');
+ validateRequest.flush({
+ [component.newRiskLevelRule.score]: false,
+ [component.newRiskLevelRule.rule]: false
+ });
+ fixture.detectChanges();
+ warning = fixture.debugElement.queryAll(By.css('.warning-text'));
+ expect(warning.length).toBe(2);
+ }
+ ));
});
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts
index 1bdfea1..260e1b0 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts
@@ -17,6 +17,7 @@
*/
import {Component, Input, EventEmitter, Output, OnInit} from '@angular/core';
import {RiskLevelRule} from '../../../model/risk-level-rule';
+import {StellarService} from '../../../service/stellar.service';
@Component({
selector: 'metron-config-sensor-rule-editor',
@@ -31,19 +32,36 @@ export class SensorRuleEditorComponent implements OnInit {
@Output() onCancelTextEditor: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output() onSubmitTextEditor: EventEmitter<RiskLevelRule> = new EventEmitter<RiskLevelRule>();
newRiskLevelRule = new RiskLevelRule();
+ isScoreValid = true;
+ isRuleValid = true;
- constructor() { }
+ constructor(private stellarService: StellarService) { }
ngOnInit() {
Object.assign(this.newRiskLevelRule, this.riskLevelRule);
}
onSave(): void {
- this.onSubmitTextEditor.emit(this.newRiskLevelRule);
+ const score = this.newRiskLevelRule.score;
+ const rule = this.newRiskLevelRule.rule;
+ this.stellarService.validateRules([rule, score]).subscribe((response) => {
+ this.isScoreValid = !!response[score];
+ this.isRuleValid = !!response[rule];
+ if (this.isRuleValid && this.isScoreValid) {
+ this.onSubmitTextEditor.emit(this.newRiskLevelRule);
+ }
+ });
}
onCancel(): void {
this.onCancelTextEditor.emit(true);
}
+ onRuleChange = () => {
+ this.isRuleValid = true;
+ }
+
+ onScoreChange = () => {
+ this.isScoreValid = true;
+ }
}
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts
index a99c8cf..16b059f 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts
@@ -19,9 +19,10 @@ import { NgModule } from '@angular/core';
import {SharedModule} from '../../../shared/shared.module';
import {SensorRuleEditorComponent} from './sensor-rule-editor.component';
import {NumberSpinnerModule} from '../../../shared/number-spinner/number-spinner.module';
+import {AceEditorModule} from '../../../shared/ace-editor/ace-editor.module';
@NgModule ({
- imports: [ SharedModule, NumberSpinnerModule ],
+ imports: [ SharedModule, NumberSpinnerModule, AceEditorModule ],
declarations: [ SensorRuleEditorComponent ],
exports: [ SensorRuleEditorComponent ]
})
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.html b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.html
index 96892f7..c3c72c6 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.html
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.html
@@ -34,47 +34,21 @@
<div class="threat-triage-summary">
<div class="form-group">
<div class="rules-summary-title">Rules</div>
- <div class="row mx-0">
- <div class="btn" (click)="onFilterChange(threatTriageFilter.HIGH)" [ngClass]="{'filter-button': filter != threatTriageFilter.HIGH, 'filter-button-selected': filter == threatTriageFilter.HIGH}">
- <i aria-hidden="true" class="fa fa-circle" style="color: red"></i> {{highAlerts}}
- </div>
- <div class="btn" (click)="onFilterChange(threatTriageFilter.MEDIUM)" [ngClass]="{'filter-button': filter != threatTriageFilter.MEDIUM, 'filter-button-selected': filter == threatTriageFilter.MEDIUM}">
- <i aria-hidden="true" class="fa fa-circle" style="color: orange"></i> {{mediumAlerts}}
- </div>
- <div class="btn" (click)="onFilterChange(threatTriageFilter.LOW)" [ngClass]="{'filter-button': filter != threatTriageFilter.LOW, 'filter-button-selected': filter == threatTriageFilter.LOW}">
- <i aria-hidden="true" class="fa fa-circle" style="color: khaki"></i> {{lowAlerts}}
- </div>
- </div>
- <div class="row mx-0 threat-triage-rules-sort">
- <span class="label">Sort by </span>
- <li class="nav-item dropdown">
- <span class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{{sortOrderOption[sortOrder].replace('_', ' ')}}</span>
- <div class="dropdown-menu bg-inverse">
- <span class="dropdown-item" (click)="onSortOrderChange(sortOrderOption.Highest_Score)">Highest Score</span>
- <span class="dropdown-item" (click)="onSortOrderChange(sortOrderOption.Lowest_Score)">Lowest Score</span>
- <span class="dropdown-item" (click)="onSortOrderChange(sortOrderOption.Highest_Name)">Highest Name</span>
- <span class="dropdown-item" (click)="onSortOrderChange(sortOrderOption.Lowest_Name)">Lowest Name</span>
- </div>
- </li>
- </div>
</div>
</div>
<div class="form-group threat-triage-rules-list">
- <div *ngFor="let riskLevelRule of this.visibleRules">
- <div class="row mx-0 py-0">
- <div class="threat-triage-rule-row" style="color: khaki; font-size: 30px; margin-top: -7px" [style.color]="getRuleColor(riskLevelRule)">
- <b>I</b>
- </div>
- <div class="threat-triage-rule-row" style="font-size: small; width: 8%">
+ <div class="threat-triage-rule" *ngFor="let riskLevelRule of this.visibleRules">
+ <div class="row mx-0 py-0 threat-triage-rule-wrapper">
+ <div class="threat-triage-rule-row" style="font-size: small;" [title]="riskLevelRule.score">
{{ riskLevelRule.score }}
</div>
- <div class="threat-triage-rule-row threat-triage-rule-str">
+ <div class="threat-triage-rule-row threat-triage-rule-str" [title]="getDisplayName(riskLevelRule)">
{{ getDisplayName(riskLevelRule) }}
</div>
- <div class="threat-triage-rule-row" style=""><i class="fa fa-i-cursor" aria-hidden="true"
- style="cursor: pointer;" (click)="onEditRule(riskLevelRule)"></i></div>
-
- <div class="threat-triage-rule-row" style=""><i class="fa fa-trash-o" aria-hidden="true" (click)="onDeleteRule(riskLevelRule)" style="cursor: pointer"></i></div>
+ <div class="threat-triage-rule-row" style="">
+ <i class="fa fa-i-cursor" aria-hidden="true" (click)="onEditRule(riskLevelRule)"></i>
+ <i class="fa fa-trash-o" aria-hidden="true" (click)="onDeleteRule(riskLevelRule)"></i>
+ </div>
</div>
<div class="form-seperator-edit"></div>
</div>
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.scss b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.scss
index bbc17a0..e2ce39b 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.scss
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.scss
@@ -46,49 +46,11 @@ textarea
padding-bottom: 5px;
}
-.filter-button
-{
- width: 32%;
- background: $field-background;
- border: 1px solid $form-button-border;
- border-radius: .25em;
- cursor: default;
- color: $nav-active-color;
- i {
- font-size: smaller;
- }
-}
-
-.filter-button-selected
-{
- @extend .filter-button;
- background-color: #006ea0;
- color: #bdbdbd;
-}
-
.threat-triage-aggregator
{
padding-bottom: 10px;
}
-.threat-triage-rules-sort
-{
- padding-top: 5px;
- font-size: 12px;
- font-family: Roboto-Regular;
- .label
- {
- display: inline-block;
- padding-right: 8px;
- }
- .dropdown
- {
- list-style: none;
- display: inline-block;
- color: $field-button-color;
- }
-}
-
.rules-summary-title
{
font-size: 15px;
@@ -104,10 +66,26 @@ textarea
display: inline-block;
position: relative;
vertical-align: top;
+ padding: 0 10px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ &:first-child {
+ padding-left: 0;
+ }
+
+ &:last-child {
+ padding-right: 0;
+ overflow: visible;
+ text-overflow: initial;
+ }
.fa {
color: $nav-active-color;
font-size: large;
+ cursor: pointer;
+ margin: 0 3px;
}
}
@@ -124,14 +102,15 @@ textarea
.threat-triage-rule-str
{
- width: 64%;
padding-right: 10px;
font-size: small;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
}
metron-config-sensor-rule-editor
{
@extend .flexbox-row-reverse;
}
+
+.threat-triage-rule-wrapper {
+ display: flex;
+ flex-wrap: nowrap;
+}
\ No newline at end of file
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.spec.ts
index 43e8e6b..8702ade 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.spec.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.spec.ts
@@ -18,16 +18,7 @@
import { SimpleChange, SimpleChanges } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { async, TestBed, ComponentFixture } from '@angular/core/testing';
-import {
- SensorThreatTriageComponent,
- SortOrderOption,
- ThreatTriageFilter
-} from './sensor-threat-triage.component';
-import {
- SensorEnrichmentConfig,
- ThreatIntelConfig
-} from '../../model/sensor-enrichment-config';
-import { RiskLevelRule } from '../../model/risk-level-rule';
+import { SensorThreatTriageComponent } from './sensor-threat-triage.component';
import { SensorEnrichmentConfigService } from '../../service/sensor-enrichment-config.service';
import { Observable } from 'rxjs';
import { SensorThreatTriageModule } from './sensor-threat-triage.module';
@@ -99,138 +90,4 @@ describe('Component: SensorThreatTriageComponent', () => {
fixture.destroy();
}));
-
- it('should get color', async(() => {
- let sensorEnrichmentConfig = new SensorEnrichmentConfig();
- sensorEnrichmentConfig.threatIntel = Object.assign(
- new ThreatIntelConfig(),
- {
- triageConfig: {
- riskLevelRules: {
- ruleA: 15,
- ruleB: 95,
- ruleC: 50
- },
- aggregator: 'MAX',
- aggregationConfig: {}
- }
- }
- );
- component.sensorEnrichmentConfig = sensorEnrichmentConfig;
-
- let ruleA = { name: 'ruleA', rule: 'rule A', score: 15, comment: '' };
- let ruleB = { name: 'ruleB', rule: 'rule B', score: 95, comment: '' };
- let ruleC = { name: 'ruleC', rule: 'rule C', score: 50, comment: '' };
-
- expect(component.getRuleColor(ruleA)).toEqual('khaki');
- expect(component.getRuleColor(ruleB)).toEqual('red');
- expect(component.getRuleColor(ruleC)).toEqual('orange');
-
- fixture.destroy();
- }));
-
- it('should edit rules', async(() => {
- let ruleA = { name: 'ruleA', rule: 'rule A', score: 15, comment: '' };
- let ruleB = { name: 'ruleB', rule: 'rule B', score: 95, comment: '' };
- let ruleC = { name: 'ruleC', rule: 'rule C', score: 50, comment: '' };
- let ruleD = { name: 'ruleD', rule: 'rule D', score: 85, comment: '' };
- let ruleE = { name: 'ruleE', rule: 'rule E', score: 5, comment: '' };
- let ruleF = { name: 'ruleF', rule: 'rule F', score: 21, comment: '' };
- let ruleG = { name: 'ruleG', rule: 'rule G', score: 100, comment: '' };
-
- let sensorEnrichmentConfig = new SensorEnrichmentConfig();
- sensorEnrichmentConfig.threatIntel = Object.assign(
- new ThreatIntelConfig(),
- {
- triageConfig: {
- riskLevelRules: [ruleA, ruleB, ruleC, ruleD, ruleE],
- aggregator: 'MAX',
- aggregationConfig: {}
- }
- }
- );
- component.sensorEnrichmentConfig = sensorEnrichmentConfig;
-
- let changes: SimpleChanges = {
- showThreatTriage: new SimpleChange(false, true, true)
- };
- component.ngOnChanges(changes);
-
- // sorted by score high to low
- expect(component.visibleRules).toEqual([ruleB, ruleD, ruleC, ruleA, ruleE]);
- expect(component.lowAlerts).toEqual(2);
- expect(component.mediumAlerts).toEqual(1);
- expect(component.highAlerts).toEqual(2);
-
- // sorted by name high to low
- component.onSortOrderChange(SortOrderOption.Highest_Name);
- expect(component.visibleRules).toEqual([ruleE, ruleD, ruleC, ruleB, ruleA]);
-
- // sorted by score low to high
- component.onSortOrderChange(SortOrderOption.Lowest_Score);
- expect(component.visibleRules).toEqual([ruleE, ruleA, ruleC, ruleD, ruleB]);
-
- // sorted by name low to high
- component.onSortOrderChange(SortOrderOption.Lowest_Name);
- expect(component.visibleRules).toEqual([ruleA, ruleB, ruleC, ruleD, ruleE]);
-
- component.onNewRule();
- expect(component.currentRiskLevelRule.name).toEqual('');
- expect(component.currentRiskLevelRule.rule).toEqual('');
- expect(component.currentRiskLevelRule.score).toEqual(0);
- expect(component.showTextEditor).toEqual(true);
-
- component.currentRiskLevelRule = new RiskLevelRule();
- component.onCancelTextEditor();
- expect(component.showTextEditor).toEqual(false);
- expect(component.visibleRules).toEqual([ruleA, ruleB, ruleC, ruleD, ruleE]);
-
- component.sortOrder = SortOrderOption.Lowest_Score;
- component.onNewRule();
- component.currentRiskLevelRule = ruleF;
- expect(component.showTextEditor).toEqual(true);
- component.onSubmitTextEditor(ruleF);
- expect(component.visibleRules).toEqual([
- ruleE,
- ruleA,
- ruleF,
- ruleC,
- ruleD,
- ruleB
- ]);
- expect(component.lowAlerts).toEqual(2);
- expect(component.mediumAlerts).toEqual(2);
- expect(component.highAlerts).toEqual(2);
- expect(component.showTextEditor).toEqual(false);
-
- component.onDeleteRule(ruleE);
- expect(component.visibleRules).toEqual([ruleA, ruleF, ruleC, ruleD, ruleB]);
- expect(component.lowAlerts).toEqual(1);
- expect(component.mediumAlerts).toEqual(2);
- expect(component.highAlerts).toEqual(2);
-
- component.onFilterChange(ThreatTriageFilter.LOW);
- expect(component.visibleRules).toEqual([ruleA]);
-
- component.onFilterChange(ThreatTriageFilter.MEDIUM);
- expect(component.visibleRules).toEqual([ruleF, ruleC]);
-
- component.onFilterChange(ThreatTriageFilter.HIGH);
- expect(component.visibleRules).toEqual([ruleD, ruleB]);
-
- component.onFilterChange(ThreatTriageFilter.HIGH);
- expect(component.visibleRules).toEqual([ruleA, ruleF, ruleC, ruleD, ruleB]);
-
- component.onEditRule(ruleC);
- expect(component.currentRiskLevelRule).toEqual(ruleC);
- expect(component.showTextEditor).toEqual(true);
- component.onSubmitTextEditor(ruleG);
- expect(component.visibleRules).toEqual([ruleA, ruleF, ruleD, ruleB, ruleG]);
- expect(component.lowAlerts).toEqual(1);
- expect(component.mediumAlerts).toEqual(1);
- expect(component.highAlerts).toEqual(3);
- expect(component.showTextEditor).toEqual(false);
-
- fixture.destroy();
- }));
});
diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.ts
index db32b04..c894c0c 100644
--- a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.ts
+++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/sensor-threat-triage.component.ts
@@ -21,14 +21,6 @@ import {SensorEnrichmentConfig } from '../../model/sensor-enrichment-config';
import {RiskLevelRule} from '../../model/risk-level-rule';
import {SensorEnrichmentConfigService} from '../../service/sensor-enrichment-config.service';
-export enum SortOrderOption {
- Lowest_Score, Highest_Score, Lowest_Name, Highest_Name
-}
-
-export enum ThreatTriageFilter {
- NONE, LOW, MEDIUM, HIGH
-}
-
@Component({
selector: 'metron-config-sensor-threat-triage',
templateUrl: './sensor-threat-triage.component.html',
@@ -39,23 +31,16 @@ export class SensorThreatTriageComponent implements OnChanges {
@Input() showThreatTriage: boolean;
@Input() sensorEnrichmentConfig: SensorEnrichmentConfig;
-
@Output() hideThreatTriage: EventEmitter<boolean> = new EventEmitter<boolean>();
+
availableAggregators = [];
visibleRules: RiskLevelRule[] = [];
-
showTextEditor = false;
currentRiskLevelRule: RiskLevelRule;
-
lowAlerts = 0;
mediumAlerts = 0;
highAlerts = 0;
- sortOrderOption = SortOrderOption;
- sortOrder = SortOrderOption.Highest_Score;
- threatTriageFilter = ThreatTriageFilter;
- filter: ThreatTriageFilter = ThreatTriageFilter.NONE;
-
constructor(private sensorEnrichmentConfigService: SensorEnrichmentConfigService) { }
ngOnChanges(changes: SimpleChanges) {
@@ -69,8 +54,6 @@ export class SensorThreatTriageComponent implements OnChanges {
this.sensorEnrichmentConfigService.getAvailableThreatTriageAggregators().subscribe(results => {
this.availableAggregators = results;
});
- this.updateBuckets();
- this.onSortOrderChange(null);
}
onClose(): void {
@@ -111,92 +94,6 @@ export class SensorThreatTriageComponent implements OnChanges {
}
}
- updateBuckets() {
- this.lowAlerts = 0;
- this.mediumAlerts = 0;
- this.highAlerts = 0;
- for (let riskLevelRule of this.visibleRules) {
- if (riskLevelRule.score <= 20) {
- this.lowAlerts++;
- } else if (riskLevelRule.score >= 80) {
- this.highAlerts++;
- } else {
- this.mediumAlerts++;
- }
- }
- }
-
- getRuleColor(riskLevelRule: RiskLevelRule): string {
- let color: string;
- if (riskLevelRule.score <= 20) {
- color = 'khaki';
- } else if (riskLevelRule.score >= 80) {
- color = 'red';
- } else {
- color = 'orange';
- }
- return color;
- }
-
- onSortOrderChange(sortOrder: any) {
- if (sortOrder !== null) {
- this.sortOrder = sortOrder;
- }
-
- // all comparisons with enums must be == and not ===
- if (this.sortOrder == this.sortOrderOption.Highest_Score) {
- this.visibleRules.sort((a, b) => {
- return b.score - a.score;
- });
- } else if (this.sortOrder == SortOrderOption.Lowest_Score) {
- this.visibleRules.sort((a, b) => {
- return a.score - b.score;
- });
- } else if (this.sortOrder == SortOrderOption.Lowest_Name) {
- this.visibleRules.sort((a, b) => {
- let aName = a.name ? a.name : '';
- let bName = b.name ? b.name : '';
- if (aName.toLowerCase() >= bName.toLowerCase()) {
- return 1;
- } else if (aName.toLowerCase() < bName.toLowerCase()) {
- return -1;
- }
- });
- } else {
- this.visibleRules.sort((a, b) => {
- let aName = a.name ? a.name : '';
- let bName = b.name ? b.name : '';
- if (aName.toLowerCase() >= bName.toLowerCase()) {
- return -1;
- } else if (aName.toLowerCase() < bName.toLowerCase()) {
- return 1;
- }
- });
- }
- }
-
- onFilterChange(filter: ThreatTriageFilter) {
- if (filter === this.filter) {
- this.filter = ThreatTriageFilter.NONE;
- } else {
- this.filter = filter;
- }
- this.visibleRules = this.sensorEnrichmentConfig.threatIntel.triageConfig.riskLevelRules.filter(riskLevelRule => {
- if (this.filter === ThreatTriageFilter.NONE) {
- return true;
- } else {
- if (this.filter === ThreatTriageFilter.HIGH) {
- return riskLevelRule.score >= 80;
- } else if (this.filter === ThreatTriageFilter.LOW) {
- return riskLevelRule.score <= 20;
- } else {
- return riskLevelRule.score < 80 && riskLevelRule.score > 20;
- }
- }
- });
- this.onSortOrderChange(null);
- }
-
getDisplayName(riskLevelRule: RiskLevelRule): string {
if (riskLevelRule.name) {
return riskLevelRule.name;
diff --git a/metron-interface/metron-config/src/app/shared/ace-editor/ace-editor.component.ts b/metron-interface/metron-config/src/app/shared/ace-editor/ace-editor.component.ts
index 8422544..21c62f1 100644
--- a/metron-interface/metron-config/src/app/shared/ace-editor/ace-editor.component.ts
+++ b/metron-interface/metron-config/src/app/shared/ace-editor/ace-editor.component.ts
@@ -16,7 +16,7 @@
* limitations under the License.
*/
/// <reference path="../../../../node_modules/@types/ace/index.d.ts" />
-import { Component, AfterViewInit, ViewChild, ElementRef, forwardRef, Input} from '@angular/core';
+import { Component, AfterViewInit, ViewChild, ElementRef, forwardRef, Input, Output, EventEmitter} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import {AutocompleteOption} from '../../model/autocomplete-option';
@@ -38,9 +38,15 @@ export class AceEditorComponent implements AfterViewInit, ControlValueAccessor {
inputJson: any = '';
aceConfigEditor: AceAjax.Editor;
- @Input() type = 'JSON';
+ @Input() type: 'GROK' | 'JSON' | 'STELLAR' = 'JSON';
@Input() placeHolder = 'Enter text here';
@Input() options: AutocompleteOption[] = [];
+ @Input() liveAutocompletion = true;
+ @Input() enableSnippets = true;
+ @Input() useWorker = true;
+ @Input() wrapLimitRangeMin: number | null = 72;
+ @Input() wrapLimitRangeMax: number | null = 72;
+ @Output() onChange = new EventEmitter();
@ViewChild('aceEditor') aceEditorEle: ElementRef;
private onTouchedCallback;
@@ -101,7 +107,7 @@ export class AceEditorComponent implements AfterViewInit, ControlValueAccessor {
parserConfigEditor.getSession().setMode(this.getEditorType());
parserConfigEditor.getSession().setTabSize(2);
parserConfigEditor.getSession().setUseWrapMode(true);
- parserConfigEditor.getSession().setWrapLimitRange(72, 72);
+ parserConfigEditor.getSession().setWrapLimitRange(this.wrapLimitRangeMin, this.wrapLimitRangeMax);
parserConfigEditor.$blockScrolling = Infinity;
parserConfigEditor.setTheme('ace/theme/monokai');
@@ -110,12 +116,16 @@ export class AceEditorComponent implements AfterViewInit, ControlValueAccessor {
highlightActiveLine: false,
maxLines: Infinity,
enableBasicAutocompletion: true,
- enableSnippets: true,
- enableLiveAutocompletion: true
+ enableSnippets: this.enableSnippets,
+ useWorker: this.useWorker,
+ enableLiveAutocompletion: this.liveAutocompletion
});
parserConfigEditor.on('change', (e: any) => {
this.inputJson = this.aceConfigEditor.getValue();
- this.onChangeCallback(this.aceConfigEditor.getValue());
+ this.onChange.emit(this.inputJson);
+ if (typeof this.onChangeCallback === 'function') {
+ this.onChangeCallback(this.inputJson);
+ }
});
if (this.type === 'GROK') {
@@ -184,6 +194,8 @@ export class AceEditorComponent implements AfterViewInit, ControlValueAccessor {
private getEditorType() {
if (this.type === 'GROK') {
return 'ace/mode/grok';
+ } else if (this.type === 'STELLAR') {
+ return 'ace/mode/javascript';
}
return 'ace/mode/json';
diff --git a/metron-interface/metron-config/src/assets/ace/mode-javascript.js b/metron-interface/metron-config/src/assets/ace/mode-javascript.js
new file mode 100644
index 0000000..ca37d3e
--- /dev/null
+++ b/metron-interface/metron-config/src/assets/ace/mode-javascript.js
@@ -0,0 +1 @@
+ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\ [...]
\ No newline at end of file