You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by rm...@apache.org on 2018/08/08 21:37:00 UTC

metron git commit: METRON-1712 PCAP UI - Input validation (tiborm via merrimanr) closes apache/metron#1142

Repository: metron
Updated Branches:
  refs/heads/feature/METRON-1554-pcap-query-panel afb9607df -> 52de126fc


METRON-1712 PCAP UI - Input validation (tiborm via merrimanr) closes apache/metron#1142


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/52de126f
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/52de126f
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/52de126f

Branch: refs/heads/feature/METRON-1554-pcap-query-panel
Commit: 52de126fc746181d6aac8ce002d6b814fd1e6cb5
Parents: afb9607
Author: tiborm <ti...@gmail.com>
Authored: Wed Aug 8 16:36:35 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Wed Aug 8 16:36:35 2018 -0500

----------------------------------------------------------------------
 .../src/app/pcap/model/pcap.mock.ts             |   4 +-
 .../src/app/pcap/model/pcap.request.ts          |   4 +-
 .../pcap-filters/pcap-filters.component.html    |  12 +-
 .../pcap-filters/pcap-filters.component.spec.ts | 228 ++++++++++++++++++-
 .../pcap/pcap-filters/pcap-filters.component.ts |   5 +
 5 files changed, 232 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
index c867fe9..bf02da8 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.mock.ts
@@ -22,9 +22,9 @@ export const fakePcapRequest = {
   startTimeMs: 0,
   endTimeMs: 0,
   ipSrcAddr: '0.0.0.0',
-  ipSrcPort: '80',
+  ipSrcPort: 80,
   ipDstAddr: '0.0.0.0',
-  ipDstPort: '80',
+  ipDstPort: 80,
   protocol: '*',
   packetFilter: '*',
   includeReverse: false

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
index 91c2287..d00a6ac 100644
--- a/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/model/pcap.request.ts
@@ -20,9 +20,9 @@ export class PcapRequest {
   startTimeMs: number = 0;
   endTimeMs: number = 150000000000000000;
   ipSrcAddr: string = '';
-  ipSrcPort: string = '';
+  ipSrcPort: number = 0;
   ipDstAddr: string = '';
-  ipDstPort: string = '';
+  ipDstPort: number = 0;
   protocol: string = '';
   packetFilter: string = '';
   includeReverse: boolean = false;

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
index f4133df..039307a 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html
@@ -24,21 +24,21 @@
 
   <div class="form-group">
     <label for="ipSrcAddr">IP Source Address</label>
-    <input name="ipSrcAddr" #ipSrcAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipSrcAddr">
+    <input name="ipSrcAddr" #ipSrcAddr="ngModel" class="form-control" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$" [(ngModel)]="model.ipSrcAddr" data-qe-id="ip-src-addr">
   </div>
-  
+
   <div class="form-group">
     <label for="ipSrcPort">IP Source Port</label>
-    <input name="ipSrcPort" #ipSrcPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipSrcPort">
+    <input name="ipSrcPort" class="form-control" pattern="^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$" [(ngModel)]="ipSrcPort" data-qe-id="ip-src-port">
   </div>
 
   <div class="form-group"><label for="ipDstAddr">IP Dest Address</label>
-    <input name="ipDstAddr" #ipDstAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipDstAddr">
+    <input name="ipDstAddr" #ipDstAddr="ngModel" class="form-control" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$" [(ngModel)]="model.ipDstAddr" data-qe-id="ip-dst-addr">
   </div>
 
   <div class="form-group">
     <label for="ipDstPort">IP Dest Port</label>
-    <input id="ipDstPort" name="ipDstPort" #ipDstPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipDstPort">
+    <input id="ipDstPort" name="ipDstPort" class="form-control" pattern="^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$" [(ngModel)]="ipDstPort" data-qe-id="ip-dest-port">
   </div>
 
     <div class="form-group">
@@ -57,6 +57,6 @@
   </div>
 
   <div class="form-group">
-    <button type="submit" [ngClass]="{'disabled':!f.form.valid || queryRunning}" class="btn btn-primary btn-search" [disabled]="!f.form.valid || queryRunning"></button>
+    <button type="submit" [ngClass]="{'disabled':!f.form.valid || queryRunning}" class="btn btn-primary btn-search" [disabled]="!f.form.valid || queryRunning" data-qe-id="submit-button"></button>
   </div>
 </form>

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
index c0f9c3b..4336b22 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts
@@ -16,20 +16,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { By } from '@angular/platform-browser';
 
 import { PcapFiltersComponent } from './pcap-filters.component';
 import { FormsModule } from '../../../../node_modules/@angular/forms';
-import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { Component, Input, Output, EventEmitter, DebugElement } from '@angular/core';
 import { PcapRequest } from '../model/pcap.request';
-import { emit } from 'cluster';
 
 @Component({
   selector: 'app-date-picker',
   template: '<input type="text" [(value)]="date">',
 })
-class FakeDatePicker {
+class FakeDatePickerComponent {
   @Input() date: string;
   @Output() dateChange = new EventEmitter<string>();
 }
@@ -44,7 +43,7 @@ describe('PcapFiltersComponent', () => {
         FormsModule
       ],
       declarations: [
-        FakeDatePicker,
+        FakeDatePickerComponent,
         PcapFiltersComponent,
       ]
     })
@@ -86,13 +85,21 @@ describe('PcapFiltersComponent', () => {
     expect(component.model.ipSrcAddr).toBe('192.168.0.1');
   });
 
-  it('IP Source Port should be bound to the model', () => {
+  it('IP Source Port should be bound to the property', () => {
     let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcPort"]');
     input.value = '9345';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.ipSrcPort).toBe(9345);
+    expect(component.ipSrcPort).toBe('9345');
+  });
+
+  it('IP Source Port should be converted to number on submit', () => {
+    component.ipSrcPort = '42';
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.ipSrcPort).toBe(42);
+    };
+    component.onSubmit();
   });
 
   it('IP Dest Address should be bound to the model', () => {
@@ -104,13 +111,21 @@ describe('PcapFiltersComponent', () => {
     expect(component.model.ipDstAddr).toBe('256.0.0.7');
   });
 
-  it('IP Dest Port should be bound to the model', () => {
+  it('IP Dest Port should be bound to the property', () => {
     let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstPort"]');
     input.value = '8989';
     input.dispatchEvent(new Event('input'));
     fixture.detectChanges();
 
-    expect(component.model.ipDstPort).toBe(8989);
+    expect(component.ipDstPort).toBe('8989');
+  });
+
+  it('IP Dest Port should be converted to number on submit', () => {
+    component.ipDstPort = '42';
+    component.search.emit = (model: PcapRequest) => {
+      expect(model.ipDstPort).toBe(42);
+    };
+    component.onSubmit();
   });
 
   it('Protocol should be bound to the model', () => {
@@ -144,7 +159,7 @@ describe('PcapFiltersComponent', () => {
     component.startTimeStr = '2220-12-12 12:12:12';
     component.search.emit = (model: PcapRequest) => {
       expect(model.startTimeMs).toBe(new Date(component.startTimeStr).getTime());
-    }
+    };
     component.onSubmit();
   });
 
@@ -152,7 +167,7 @@ describe('PcapFiltersComponent', () => {
     component.endTimeStr = '2320-03-13 13:13:13';
     component.search.emit = (model: PcapRequest) => {
       expect(model.endTimeMs).toBe(new Date(component.endTimeStr).getTime());
-    }
+    };
     component.onSubmit();
   });
 
@@ -187,4 +202,195 @@ describe('PcapFiltersComponent', () => {
     expect(fixture.componentInstance.model.hasOwnProperty('includeReverse')).toBeTruthy();
   });
 
+  describe('Filter validation', () => {
+
+    function setup() {
+      component.queryRunning = false;
+      fixture.detectChanges();
+    }
+
+    function getFieldWithSubmit(fieldId: string): { field: DebugElement, submit: DebugElement } {
+      const field = fixture.debugElement.query(By.css('[data-qe-id="' + fieldId  + '"]'));
+      const submit = fixture.debugElement.query(By.css('[data-qe-id="submit-button"]'));
+      return {
+        field,
+        submit
+      };
+    }
+
+    function setFieldValue(field: DebugElement, value: any) {
+      field.nativeElement.value = value;
+      field.nativeElement.dispatchEvent(new Event('input'));
+      fixture.detectChanges();
+    }
+
+    function isSubmitDisabled(submit: DebugElement): boolean {
+      return submit.classes['disabled'] && submit.nativeElement.disabled;
+    }
+
+    function isFieldInvalid(field: DebugElement): boolean {
+      return field.classes['ng-invalid'];
+    }
+
+    function tearDown(field: DebugElement) {
+      setFieldValue(field, '');
+    };
+
+    beforeEach(setup);
+
+    it('should disable the form if the ip source port is invalid', () => {
+      const invalidValues = [
+        '-42',
+        '-1',
+        'foobar',
+        '.',
+        '-',
+        '+',
+        'e',
+        'E',
+        '3.14',
+        '123456',
+        '65536',
+        '99999',
+        '2352363474576',
+        '1e3',
+      ];
+
+      invalidValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(true, 'the field should be invalid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(true, 'the submit button should be disabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should keep the form enabled if the ip source port is valid', () => {
+      const validValues = [
+        '8080',
+        '1024',
+        '3000',
+        '1',
+        '0',
+        '12345',
+        '65535',
+      ];
+
+      validValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should disable the form if the ip destination port is invalid', () => {
+      const invalidValues = [
+        '-42',
+        '-1',
+        'foobar',
+        '.',
+        '-',
+        '+',
+        'e',
+        'E',
+        '3.14',
+        '123456',
+        '65536',
+        '99999',
+        '2352363474576',
+        '1e3',
+      ];
+
+      invalidValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-dest-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(true, 'the field should be invalid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(true, 'the submit button should be disabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should keep the form enabled if the ip destination port is valid', () => {
+      const validValues = [
+        '8080',
+        '1024',
+        '3000',
+        '1',
+        '0',
+        '12345',
+        '65535',
+      ];
+
+      validValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-dest-port');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+
+    it('should disable the form if the ip source field is invalid', () => {
+      const invalidValues = [
+        'tst',
+        0o0,
+        0,
+        '111.111.111',
+        '222.222.222.222.222',
+        '333.333.333.333',
+      ];
+
+      invalidValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-addr');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(true, 'the field should be invalid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(true, 'the submit button should be disabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+    it('should keep the form enabled if the ip source field is valid', () => {
+      const validValues = [
+        '0.0.0.0',
+        '222.222.222.222',
+        '255.255.255.255',
+      ];
+
+      validValues.forEach((value) => {
+        const els = getFieldWithSubmit('ip-src-addr');
+        expect(isFieldInvalid(els.field)).toBe(false, 'the field should be valid without ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled without ' + value);
+
+        setFieldValue(els.field, value);
+
+        expect(isFieldInvalid(els.field)).toBe(false, 'tthe field should be valid with ' + value);
+        expect(isSubmitDisabled(els.submit)).toBe(false, 'the submit button should be enabled with ' + value);
+        tearDown(els.field);
+      });
+    });
+
+  });
 });

http://git-wip-us.apache.org/repos/asf/metron/blob/52de126f/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
index 4f6a3dd..5bbdb67 100644
--- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
+++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts
@@ -33,6 +33,8 @@ export class PcapFiltersComponent implements OnInit {
 
   startTimeStr: string;
   endTimeStr: string;
+  ipSrcPort: string = '';
+  ipDstPort: string = '';
 
   model = new PcapRequest();
 
@@ -49,6 +51,9 @@ export class PcapFiltersComponent implements OnInit {
   onSubmit() {
     this.model.startTimeMs = new Date(this.startTimeStr).getTime();
     this.model.endTimeMs = new Date(this.endTimeStr).getTime();
+    this.model.ipSrcPort = +this.ipSrcPort;
+    this.model.ipDstPort = +this.ipDstPort;
+
     this.search.emit(this.model);
   }
 }