| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
| import { Inject } from '@angular/core'; |
| import { Observable, throwError } from 'rxjs'; |
| import { Router, ActivatedRoute, Params } from '@angular/router'; |
| import { HttpClient, HttpResponse } from '@angular/common/http'; |
| import { |
| SensorParserConfigComponent, |
| Pane, |
| KafkaStatus |
| } from './sensor-parser-config.component'; |
| import { StellarService } from '../../service/stellar.service'; |
| import { SensorParserConfigService } from '../../service/sensor-parser-config.service'; |
| import { KafkaService } from '../../service/kafka.service'; |
| import { KafkaTopic } from '../../model/kafka-topic'; |
| import { GrokValidationService } from '../../service/grok-validation.service'; |
| import { MetronAlerts } from '../../shared/metron-alerts'; |
| import { SensorParserConfig } from '../../model/sensor-parser-config'; |
| import { ParseMessageRequest } from '../../model/parse-message-request'; |
| import { SensorParserContext } from '../../model/sensor-parser-context'; |
| import { AuthenticationService } from '../../service/authentication.service'; |
| import { FieldTransformer } from '../../model/field-transformer'; |
| import { SensorParserConfigModule } from './sensor-parser-config.module'; |
| import { SensorEnrichmentConfigService } from '../../service/sensor-enrichment-config.service'; |
| import { SensorEnrichmentConfig } from '../../model/sensor-enrichment-config'; |
| import { SensorIndexingConfigService } from '../../service/sensor-indexing-config.service'; |
| import { IndexingConfigurations } from '../../model/sensor-indexing-config'; |
| import { of } from 'rxjs'; |
| import { HdfsService } from '../../service/hdfs.service'; |
| import { RestError } from '../../model/rest-error'; |
| import { RiskLevelRule } from '../../model/risk-level-rule'; |
| import {AppConfigService} from '../../service/app-config.service'; |
| import {MockAppConfigService} from '../../service/mock.app-config.service'; |
| |
| class MockRouter { |
| navigateByUrl(url: string) {} |
| } |
| |
| class MockActivatedRoute { |
| private name: string; |
| params: Observable<Params>; |
| |
| setNameForTest(name: string) { |
| this.name = name; |
| this.params = Observable.create(observer => { |
| observer.next({ id: this.name }); |
| observer.complete(); |
| }); |
| } |
| } |
| |
| class MockSensorParserConfigService extends SensorParserConfigService { |
| private name: string; |
| private sensorParserConfig: SensorParserConfig; |
| private parsedMessage: any; |
| private postedSensorParserConfig: SensorParserConfig; |
| private throwError: boolean; |
| |
| public post( |
| name: string, |
| sensorParserConfig: SensorParserConfig |
| ): Observable<SensorParserConfig> { |
| if (this.throwError) { |
| let error = new RestError(); |
| error.message = 'SensorParserConfig post error'; |
| return throwError(error); |
| } |
| this.postedSensorParserConfig = sensorParserConfig; |
| return Observable.create(observer => { |
| observer.next(sensorParserConfig); |
| observer.complete(); |
| }); |
| } |
| |
| public get(name: string): Observable<SensorParserConfig> { |
| return Observable.create(observer => { |
| observer.next(this.sensorParserConfig); |
| observer.complete(); |
| }); |
| } |
| |
| public getAll(): Observable<{}> { |
| return Observable.create(observer => { |
| let results = {}; |
| results[this.name] = this.sensorParserConfig; |
| observer.next(results); |
| observer.complete(); |
| }); |
| } |
| |
| public getAvailableParsers(): Observable<{}> { |
| return Observable.create(observer => { |
| observer.next({ |
| Bro: 'org.apache.metron.parsers.bro.BasicBroParser', |
| Grok: 'org.apache.metron.parsers.GrokParser' |
| }); |
| observer.complete(); |
| }); |
| } |
| |
| public parseMessage( |
| parseMessageRequest: ParseMessageRequest |
| ): Observable<{}> { |
| return Observable.create(observer => { |
| observer.next(this.parsedMessage); |
| observer.complete(); |
| }); |
| } |
| |
| public setSensorParserConfig( |
| name: string, |
| sensorParserConfig: SensorParserConfig |
| ) { |
| this.name = name; |
| this.sensorParserConfig = sensorParserConfig; |
| } |
| |
| public setParsedMessage(parsedMessage: any) { |
| this.parsedMessage = parsedMessage; |
| } |
| |
| public setThrowError(throwError: boolean) { |
| this.throwError = throwError; |
| } |
| |
| public getPostedSensorParserConfig() { |
| return this.postedSensorParserConfig; |
| } |
| } |
| |
| class MockSensorIndexingConfigService extends SensorIndexingConfigService { |
| private name: string; |
| private postedIndexingConfigurations: IndexingConfigurations; |
| private sensorIndexingConfig: IndexingConfigurations; |
| private throwError: boolean; |
| |
| public post( |
| name: string, |
| sensorIndexingConfig: IndexingConfigurations |
| ): Observable<IndexingConfigurations> { |
| if (this.throwError) { |
| let error = new RestError(); |
| error.message = 'IndexingConfigurations post error'; |
| return throwError(error); |
| } |
| this.postedIndexingConfigurations = sensorIndexingConfig; |
| return Observable.create(observer => { |
| observer.next(sensorIndexingConfig); |
| observer.complete(); |
| }); |
| } |
| |
| public get(name: string): Observable<IndexingConfigurations> { |
| if (this.sensorIndexingConfig === null) { |
| let error = new RestError(); |
| error.message = 'IndexingConfigurations get error'; |
| return throwError(error); |
| } |
| return Observable.create(observer => { |
| if (name === this.name) { |
| observer.next(this.sensorIndexingConfig); |
| } |
| observer.complete(); |
| }); |
| } |
| |
| public setSensorIndexingConfig(name: string, result: IndexingConfigurations) { |
| this.name = name; |
| this.sensorIndexingConfig = result; |
| } |
| |
| public setThrowError(throwError: boolean) { |
| this.throwError = throwError; |
| } |
| |
| public getPostedIndexingConfigurations(): IndexingConfigurations { |
| return this.postedIndexingConfigurations; |
| } |
| } |
| |
| class MockKafkaService extends KafkaService { |
| private name: string; |
| private kafkaTopic: KafkaTopic; |
| private kafkaTopicForPost: KafkaTopic; |
| private sampleData = { key1: 'value1', key2: 'value2' }; |
| |
| public setKafkaTopic(name: string, kafkaTopic: KafkaTopic) { |
| this.name = name; |
| this.kafkaTopic = kafkaTopic; |
| } |
| |
| public setSampleData(name: string, sampleData?: any) { |
| this.name = name; |
| this.sampleData = sampleData; |
| } |
| |
| public sample(name: string): Observable<string> { |
| if (this.sampleData === null) { |
| return throwError(new RestError()); |
| } |
| return Observable.create(observer => { |
| if (name === this.name) { |
| observer.next(JSON.stringify(this.sampleData)); |
| } |
| observer.complete(); |
| }); |
| } |
| |
| public get(name: string): Observable<KafkaTopic> { |
| if (this.kafkaTopic === null) { |
| return throwError(new RestError()); |
| } |
| return Observable.create(observer => { |
| if (name === this.name) { |
| observer.next(this.kafkaTopic); |
| } |
| observer.complete(); |
| }); |
| } |
| |
| public post(k: KafkaTopic): Observable<KafkaTopic> { |
| this.kafkaTopicForPost = k; |
| console.log('called post MockKafkaService: ' + this.kafkaTopicForPost); |
| return Observable.create(observer => { |
| observer.next({}); |
| observer.complete(); |
| }); |
| } |
| |
| public getKafkaTopicForPost(): KafkaTopic { |
| console.log('Called get MockKafkaService: ' + this.kafkaTopicForPost); |
| return this.kafkaTopicForPost; |
| } |
| } |
| |
| class MockGrokValidationService extends GrokValidationService { |
| private path: string; |
| private contents: string; |
| |
| public setContents(path: string, contents: string) { |
| this.path = path; |
| this.contents = contents; |
| } |
| |
| public list(): Observable<string[]> { |
| return Observable.create(observer => { |
| observer.next({ |
| BASE10NUM: |
| '(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+)))', |
| BASE16FLOAT: |
| '\\b(?<![0-9A-Fa-f.])(?:[+-]?(?:0x)?(?:(?:[0-9A-Fa-f]+(?:\\.[0-9A-Fa-f]*)?)|(?:\\.[0-9A-Fa-f]+)))\\b', |
| BASE16NUM: '(?<![0-9A-Fa-f])(?:[+-]?(?:0x)?(?:[0-9A-Fa-f]+))', |
| CISCOMAC: '(?:(?:[A-Fa-f0-9]{4}\\.){2}[A-Fa-f0-9]{4})', |
| COMMONMAC: '(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})', |
| DATA: '.*?' |
| }); |
| observer.complete(); |
| }); |
| } |
| |
| public getStatement(path: string): Observable<string> { |
| if (this.contents === null) { |
| return throwError('Error'); |
| } |
| return Observable.create(observer => { |
| if (path === this.path) { |
| observer.next(this.contents); |
| } |
| observer.complete(); |
| }); |
| } |
| } |
| |
| class MockHdfsService extends HdfsService { |
| private fileList: string[]; |
| private path: string; |
| private contents: string; |
| private postedContents: string; |
| |
| public setFileList(path: string, fileList: string[]) { |
| this.path = path; |
| this.fileList = fileList; |
| } |
| |
| public setContents(path: string, contents: string) { |
| this.path = path; |
| this.contents = contents; |
| } |
| |
| public list(path: string): Observable<string[]> { |
| if (this.fileList === null) { |
| return throwError('Error'); |
| } |
| return Observable.create(observer => { |
| if (path === this.path) { |
| observer.next(this.fileList); |
| } |
| observer.complete(); |
| }); |
| } |
| |
| public read(path: string): Observable<string> { |
| if (this.contents === null) { |
| return throwError('Error'); |
| } |
| return Observable.create(observer => { |
| if (path === this.path) { |
| observer.next(this.contents); |
| } |
| observer.complete(); |
| }); |
| } |
| |
| public post(path: string, contents: string): Observable<{}> { |
| if (this.contents === null) { |
| let error = new RestError(); |
| error.message = 'HDFS post Error'; |
| return throwError(error); |
| } |
| this.postedContents = contents; |
| return Observable.create(observer => { |
| observer.next(contents); |
| observer.complete(); |
| }); |
| } |
| |
| public deleteFile(path: string): Observable<HttpResponse<{}>> { |
| return Observable.create(observer => { |
| observer.next({}); |
| observer.complete(); |
| }); |
| } |
| |
| public getPostedContents() { |
| return this.postedContents; |
| } |
| } |
| |
| class MockAuthenticationService extends AuthenticationService { |
| |
| public getCurrentUser(options: {}): Observable<HttpResponse<{}>> { |
| let responseOptions: {} = { body: 'user' }; |
| let response: HttpResponse<{}> = new HttpResponse(responseOptions); |
| return of(response); |
| } |
| } |
| |
| class MockTransformationValidationService extends StellarService { |
| private transformationValidationResult: any; |
| private transformationValidationForValidate: SensorParserContext; |
| |
| public setTransformationValidationResultForTest( |
| transformationValidationResult: any |
| ): void { |
| this.transformationValidationResult = transformationValidationResult; |
| } |
| |
| public getTransformationValidationForValidate(): SensorParserContext { |
| return this.transformationValidationForValidate; |
| } |
| |
| public validate(t: SensorParserContext): Observable<{}> { |
| this.transformationValidationForValidate = t; |
| return Observable.create(observer => { |
| observer.next(this.transformationValidationResult); |
| observer.complete(); |
| }); |
| } |
| } |
| |
| export class MockSensorEnrichmentConfigService { |
| private name: string; |
| private sensorEnrichmentConfig: SensorEnrichmentConfig; |
| private postedSensorEnrichmentConfig: SensorEnrichmentConfig; |
| private throwError: boolean; |
| |
| public setSensorEnrichmentConfig( |
| name: string, |
| sensorEnrichmentConfig: SensorEnrichmentConfig |
| ) { |
| this.name = name; |
| this.sensorEnrichmentConfig = sensorEnrichmentConfig; |
| } |
| |
| public get(name: string): Observable<SensorEnrichmentConfig> { |
| if (this.sensorEnrichmentConfig === null) { |
| let error = new RestError(); |
| error.message = 'SensorEnrichmentConfig get error'; |
| return throwError(error); |
| } |
| return Observable.create(observer => { |
| if (name === this.name) { |
| observer.next(this.sensorEnrichmentConfig); |
| } |
| observer.complete(); |
| }); |
| } |
| |
| public post( |
| name: string, |
| sensorEnrichmentConfig: SensorEnrichmentConfig |
| ): Observable<SensorEnrichmentConfig> { |
| if (this.throwError) { |
| let error = new RestError(); |
| error.message = 'SensorEnrichmentConfig post error'; |
| return throwError(error); |
| } |
| this.postedSensorEnrichmentConfig = sensorEnrichmentConfig; |
| return Observable.create(observer => { |
| observer.next(sensorEnrichmentConfig); |
| observer.complete(); |
| }); |
| } |
| |
| public setThrowError(throwError: boolean) { |
| this.throwError = throwError; |
| } |
| |
| public getPostedSensorEnrichmentConfig() { |
| return this.postedSensorEnrichmentConfig; |
| } |
| } |
| |
| describe('Component: SensorParserConfig', () => { |
| let component: SensorParserConfigComponent; |
| let fixture: ComponentFixture<SensorParserConfigComponent>; |
| let sensorParserConfigService: MockSensorParserConfigService; |
| let sensorEnrichmentConfigService: MockSensorEnrichmentConfigService; |
| let sensorIndexingConfigService: MockSensorIndexingConfigService; |
| let transformationValidationService: MockTransformationValidationService; |
| let kafkaService: MockKafkaService; |
| let hdfsService: MockHdfsService; |
| let grokValidationService: MockGrokValidationService; |
| let activatedRoute: MockActivatedRoute; |
| let metronAlerts: MetronAlerts; |
| let router: MockRouter; |
| |
| let squidSensorParserConfig: any = { |
| parserClassName: 'org.apache.metron.parsers.GrokParser', |
| sensorTopic: 'squid', |
| parserConfig: { |
| grokPath: '/apps/metron/patterns/squid', |
| patternLabel: 'SQUID_DELIMITED', |
| timestampField: 'timestamp' |
| }, |
| fieldTransformations: [ |
| { |
| input: [], |
| output: ['full_hostname', 'domain_without_subdomains', 'hostname'], |
| transformation: 'STELLAR', |
| config: { |
| full_hostname: 'URL_TO_HOST(url)', |
| domain_without_subdomains: 'DOMAIN_REMOVE_SUBDOMAINS(full_hostname)' |
| } |
| } |
| ] |
| }; |
| |
| let squidSensorEnrichmentConfig = { |
| enrichment: { |
| fieldMap: { |
| geo: ['ip_dst_addr'], |
| host: ['ip_dst_addr'], |
| whois: [], |
| stellar: { config: { group1: {} } } |
| }, |
| fieldToTypeMap: {}, |
| config: {} |
| }, |
| threatIntel: { |
| threatIntel: { |
| fieldMap: { hbaseThreatIntel: ['ip_dst_addr'] }, |
| fieldToTypeMap: { ip_dst_addr: ['malicious_ip'] } |
| } |
| } |
| }; |
| |
| let squidIndexingConfigurations = { |
| hdfs: { |
| index: 'squid', |
| batchSize: 5, |
| enabled: true |
| }, |
| elasticsearch: { |
| index: 'squid', |
| batchSize: 10, |
| enabled: true |
| }, |
| solr: { |
| index: 'squid', |
| batchSize: 1, |
| enabled: false |
| } |
| }; |
| |
| beforeEach(async(() => { |
| TestBed.configureTestingModule({ |
| imports: [SensorParserConfigModule], |
| providers: [ |
| MetronAlerts, |
| { provide: HttpClient }, |
| { |
| provide: SensorParserConfigService, |
| useClass: MockSensorParserConfigService |
| }, |
| { |
| provide: SensorIndexingConfigService, |
| useClass: MockSensorIndexingConfigService |
| }, |
| { provide: KafkaService, useClass: MockKafkaService }, |
| { provide: HdfsService, useClass: MockHdfsService }, |
| { provide: GrokValidationService, useClass: MockGrokValidationService }, |
| { |
| provide: StellarService, |
| useClass: MockTransformationValidationService |
| }, |
| { provide: ActivatedRoute, useClass: MockActivatedRoute }, |
| { provide: Router, useClass: MockRouter }, |
| { provide: AuthenticationService, useClass: MockAuthenticationService }, |
| { |
| provide: SensorEnrichmentConfigService, |
| useClass: MockSensorEnrichmentConfigService |
| }, |
| { provide: AppConfigService, useClass: MockAppConfigService } |
| ] |
| }); |
| fixture = TestBed.createComponent(SensorParserConfigComponent); |
| component = fixture.componentInstance; |
| sensorParserConfigService = TestBed.get(SensorParserConfigService); |
| sensorIndexingConfigService = TestBed.get(SensorIndexingConfigService); |
| transformationValidationService = TestBed.get(StellarService); |
| kafkaService = TestBed.get(KafkaService); |
| hdfsService = TestBed.get(HdfsService); |
| grokValidationService = TestBed.get(GrokValidationService); |
| sensorEnrichmentConfigService = TestBed.get(SensorEnrichmentConfigService); |
| activatedRoute = TestBed.get(ActivatedRoute); |
| metronAlerts = TestBed.get(MetronAlerts); |
| router = TestBed.get(Router); |
| })); |
| |
| afterEach(() => { |
| fixture.destroy(); |
| }) |
| |
| it('should create an instance of SensorParserConfigComponent', async(() => { |
| expect(component).toBeDefined(); |
| })); |
| |
| it('should handle ngOnInit', async(() => { |
| spyOn(component, 'init'); |
| spyOn(component, 'createForms'); |
| spyOn(component, 'getAvailableParsers'); |
| |
| activatedRoute.setNameForTest('squid'); |
| |
| component.ngOnInit(); |
| |
| expect(component.init).toHaveBeenCalledWith('squid'); |
| expect(component.createForms).toHaveBeenCalled(); |
| expect(component.getAvailableParsers).toHaveBeenCalled(); |
| })); |
| |
| it('should createForms', async(() => { |
| component.sensorParserConfig = Object.assign( |
| new SensorParserConfig(), |
| squidSensorParserConfig |
| ); |
| component.createForms(); |
| |
| expect(Object.keys(component.sensorConfigForm.controls).length).toEqual(17); |
| expect( |
| Object.keys(component.transformsValidationForm.controls).length |
| ).toEqual(2); |
| expect(component.showAdvancedParserConfiguration).toEqual(true); |
| |
| component.sensorParserConfig.parserConfig = {}; |
| component.showAdvancedParserConfiguration = false; |
| component.createForms(); |
| expect(component.showAdvancedParserConfiguration).toEqual(false); |
| })); |
| |
| it('should getAvailableParsers', async(() => { |
| component.getAvailableParsers(); |
| expect(component.availableParsers).toEqual({ |
| Bro: 'org.apache.metron.parsers.bro.BasicBroParser', |
| Grok: 'org.apache.metron.parsers.GrokParser' |
| }); |
| expect(component.availableParserNames).toEqual(['Bro', 'Grok']); |
| })); |
| |
| it('should init', async(() => { |
| sensorParserConfigService.setSensorParserConfig( |
| 'squid', |
| squidSensorParserConfig |
| ); |
| component.init('new'); |
| |
| let expectedSensorParserConfig = new SensorParserConfig(); |
| expectedSensorParserConfig.parserClassName = |
| 'org.apache.metron.parsers.GrokParser'; |
| expect(component.sensorParserConfig).toEqual(expectedSensorParserConfig); |
| expect(component.sensorEnrichmentConfig).toEqual( |
| new SensorEnrichmentConfig() |
| ); |
| expect(component.indexingConfigurations).toEqual( |
| new IndexingConfigurations() |
| ); |
| expect(component.editMode).toEqual(false); |
| expect(component.currentSensors).toEqual(['squid']); |
| |
| spyOn(component, 'getKafkaStatus'); |
| let sensorParserConfig = Object.assign( |
| new SensorParserConfig(), |
| squidSensorParserConfig |
| ); |
| sensorParserConfigService.setSensorParserConfig( |
| 'squid', |
| sensorParserConfig |
| ); |
| sensorEnrichmentConfigService.setSensorEnrichmentConfig( |
| 'squid', |
| Object.assign(new SensorEnrichmentConfig(), squidSensorEnrichmentConfig) |
| ); |
| sensorIndexingConfigService.setSensorIndexingConfig( |
| 'squid', |
| Object.assign(new IndexingConfigurations(), squidIndexingConfigurations) |
| ); |
| hdfsService.setContents( |
| '/apps/metron/patterns/squid', |
| 'SQUID_DELIMITED grok statement' |
| ); |
| |
| component.init('squid'); |
| expect(component.sensorParserConfig).toEqual( |
| Object.assign(new SensorParserConfig(), squidSensorParserConfig) |
| ); |
| expect(component.sensorNameValid).toEqual(true); |
| expect(component.getKafkaStatus).toHaveBeenCalled(); |
| expect(component.showAdvancedParserConfiguration).toEqual(true); |
| expect(component.grokStatement).toEqual('SQUID_DELIMITED grok statement'); |
| expect(component.patternLabel).toEqual('SQUID_DELIMITED'); |
| expect(component.sensorEnrichmentConfig).toEqual( |
| Object.assign(new SensorEnrichmentConfig(), squidSensorEnrichmentConfig) |
| ); |
| expect(component.indexingConfigurations).toEqual( |
| Object.assign(new IndexingConfigurations(), squidIndexingConfigurations) |
| ); |
| |
| component.sensorParserConfig.parserConfig['grokPath'] = '/patterns/squid'; |
| hdfsService.setContents('/patterns/squid', null); |
| grokValidationService.setContents( |
| '/patterns/squid', |
| 'SQUID grok statement from classpath' |
| ); |
| |
| component.init('squid'); |
| expect(component.grokStatement).toEqual( |
| 'SQUID grok statement from classpath' |
| ); |
| |
| spyOn(metronAlerts, 'showErrorMessage'); |
| |
| sensorEnrichmentConfigService.setSensorEnrichmentConfig('squid', null); |
| component.init('squid'); |
| expect(metronAlerts.showErrorMessage).toHaveBeenCalledWith( |
| 'SensorEnrichmentConfig get error' |
| ); |
| |
| sensorIndexingConfigService.setSensorIndexingConfig('squid', null); |
| component.init('squid'); |
| expect(metronAlerts.showErrorMessage).toHaveBeenCalledWith( |
| 'IndexingConfigurations get error' |
| ); |
| |
| grokValidationService.setContents('/patterns/squid', null); |
| |
| component.init('squid'); |
| expect(metronAlerts.showErrorMessage).toHaveBeenCalledWith( |
| 'Could not find grok statement in HDFS or classpath at /patterns/squid' |
| ); |
| |
| sensorParserConfig = new SensorParserConfig(); |
| sensorParserConfig.sensorTopic = 'bro'; |
| sensorParserConfigService.setSensorParserConfig('bro', sensorParserConfig); |
| component.showAdvancedParserConfiguration = false; |
| |
| component.init('bro'); |
| expect(component.showAdvancedParserConfiguration).toEqual(false); |
| })); |
| |
| it('should getMessagePrefix', async(() => { |
| component.getAvailableParsers(); |
| expect(component.getMessagePrefix()).toEqual('Created'); |
| component.editMode = true; |
| expect(component.getMessagePrefix()).toEqual('Modified'); |
| })); |
| |
| it('should handle onSetKafkaTopic', async(() => { |
| spyOn(component, 'getKafkaStatus'); |
| |
| component.onSetKafkaTopic(); |
| expect(component.getKafkaStatus).not.toHaveBeenCalled(); |
| |
| component.sensorParserConfig.sensorTopic = 'bro'; |
| component.onSetKafkaTopic(); |
| expect(component.kafkaTopicValid).toEqual(true); |
| expect(component.getKafkaStatus).toHaveBeenCalled(); |
| })); |
| |
| it('should handle onSetSensorName', async(() => { |
| |
| component.onSetSensorName(); |
| expect(component.sensorNameValid).toEqual(false); |
| |
| component.sensorName = 'squid'; |
| component.currentSensors = ['squid']; |
| component.onSetSensorName(); |
| expect(component.sensorNameUnique).toEqual(false); |
| expect(component.sensorNameValid).toEqual(false); |
| |
| component.sensorName = 'squidUnique'; |
| component.onSetSensorName(); |
| expect(component.sensorNameUnique).toEqual(true); |
| expect(component.sensorNameValid).toEqual(true); |
| })); |
| |
| it('sensor name should be validated agains special characters', () => { |
| component.sensorName = 'squidWithSpecChar@'; |
| component.onSetSensorName(); |
| expect(component.sensorNameNoSpecChars).toEqual(false); |
| expect(component.sensorNameValid).toEqual(false); |
| |
| component.sensorName = 'squidWithSpecChar#'; |
| component.onSetSensorName(); |
| expect(component.sensorNameNoSpecChars).toEqual(false); |
| expect(component.sensorNameValid).toEqual(false); |
| |
| component.sensorName = 'squidWithSpecChar/'; |
| component.onSetSensorName(); |
| expect(component.sensorNameNoSpecChars).toEqual(false); |
| expect(component.sensorNameValid).toEqual(false); |
| |
| component.sensorName = 'squidWithSpecChar?'; |
| component.onSetSensorName(); |
| expect(component.sensorNameNoSpecChars).toEqual(false); |
| expect(component.sensorNameValid).toEqual(false); |
| |
| component.sensorName = 'squidWithSpecChar?$&%'; |
| component.onSetSensorName(); |
| expect(component.sensorNameNoSpecChars).toEqual(false); |
| expect(component.sensorNameValid).toEqual(false); |
| }); |
| |
| it('sensor name validation should accept dash and lowdash chars', () => { |
| component.sensorName = 'squidWithSpecChar-'; |
| component.onSetSensorName(); |
| expect(component.sensorNameNoSpecChars).toEqual(true); |
| expect(component.sensorNameValid).toEqual(true); |
| |
| component.sensorName = 'squidWithSpecChar_'; |
| component.onSetSensorName(); |
| expect(component.sensorNameNoSpecChars).toEqual(true); |
| expect(component.sensorNameValid).toEqual(true); |
| }) |
| |
| it('should handle onParserTypeChange', async(() => { |
| spyOn(component, 'hidePane'); |
| |
| component.onParserTypeChange(); |
| expect(component.hidePane).not.toHaveBeenCalled(); |
| |
| component.sensorParserConfig.parserClassName = |
| 'org.apache.metron.parsers.GrokParser'; |
| component.onParserTypeChange(); |
| expect(component.parserClassValid).toEqual(true); |
| expect(component.hidePane).not.toHaveBeenCalled(); |
| |
| component.sensorParserConfig.parserClassName = |
| 'org.apache.metron.parsers.bro.BasicBroParser'; |
| component.onParserTypeChange(); |
| expect(component.hidePane).toHaveBeenCalledWith(Pane.GROK); |
| })); |
| |
| it('isGrokStatementValid should validate grokStatement', async(() => { |
| expect(component.isGrokStatementValid()).toEqual(false); |
| |
| component.grokStatement = ''; |
| expect(component.isGrokStatementValid()).toEqual(false); |
| |
| component.grokStatement = 'grok statement'; |
| expect(component.isGrokStatementValid()).toEqual(true); |
| })); |
| |
| it('should handle isConfigValid', async(() => { |
| expect(component.isConfigValid()).toEqual(false); |
| |
| component.sensorNameValid = true; |
| component.kafkaTopicValid = true; |
| component.parserClassValid = true; |
| |
| expect(component.isConfigValid()).toEqual(true); |
| |
| component.sensorParserConfig.parserClassName = |
| 'org.apache.metron.parsers.GrokParser'; |
| expect(component.isConfigValid()).toEqual(false); |
| |
| component.grokStatement = 'grok statement'; |
| expect(component.isConfigValid()).toEqual(true); |
| })); |
| |
| it('should getKafkaStatus', async(() => { |
| component.getKafkaStatus(); |
| expect(component.currentKafkaStatus).toEqual(null); |
| |
| component.sensorParserConfig.sensorTopic = 'squid'; |
| kafkaService.setKafkaTopic('squid', null); |
| |
| component.getKafkaStatus(); |
| expect(component.currentKafkaStatus).toEqual(KafkaStatus.NO_TOPIC); |
| |
| kafkaService.setKafkaTopic('squid', new KafkaTopic()); |
| kafkaService.setSampleData('squid', null); |
| |
| component.getKafkaStatus(); |
| expect(component.currentKafkaStatus).toEqual(KafkaStatus.NOT_EMITTING); |
| |
| kafkaService.setSampleData('squid', 'message'); |
| component.getKafkaStatus(); |
| expect(component.currentKafkaStatus).toEqual(KafkaStatus.EMITTING); |
| })); |
| |
| it('should getTransforms', async(() => { |
| expect(component.getTransforms()).toEqual('0 Transformations Applied'); |
| |
| component.sensorParserConfig.fieldTransformations.push( |
| Object.assign(new FieldTransformer(), { output: ['field1', 'field2'] }) |
| ); |
| component.sensorParserConfig.fieldTransformations.push( |
| Object.assign(new FieldTransformer(), { output: ['field3'] }) |
| ); |
| |
| expect(component.getTransforms()).toEqual('3 Transformations Applied'); |
| })); |
| |
| it('should handle onSaveGrokStatement', async(() => { |
| component.sensorName = 'squid'; |
| |
| component.onSaveGrokStatement('grok statement'); |
| expect(component.grokStatement).toEqual('grok statement'); |
| expect(component.sensorParserConfig.parserConfig['grokPath']).toEqual( |
| '/apps/metron/patterns/squid' |
| ); |
| |
| component.sensorParserConfig.parserConfig['grokPath'] = '/patterns/squid'; |
| component.onSaveGrokStatement('grok statement'); |
| expect(component.sensorParserConfig.parserConfig['grokPath']).toEqual( |
| '/apps/metron/patterns/squid' |
| ); |
| |
| component.sensorParserConfig.parserConfig['grokPath'] = '/custom/grok/path'; |
| component.onSaveGrokStatement('grok statement'); |
| expect(component.sensorParserConfig.parserConfig['grokPath']).toEqual( |
| '/custom/grok/path' |
| ); |
| })); |
| |
| it('should onSavePatternLabel', async(() => { |
| component.onSavePatternLabel('PATTERN_LABEL'); |
| expect(component.patternLabel).toEqual('PATTERN_LABEL'); |
| expect(component.sensorParserConfig.parserConfig['patternLabel']).toEqual( |
| 'PATTERN_LABEL' |
| ); |
| })); |
| |
| it('should goBack', async(() => { |
| activatedRoute.setNameForTest('new'); |
| |
| router.navigateByUrl = jasmine.createSpy('navigateByUrl'); |
| component.goBack(); |
| expect(router.navigateByUrl).toHaveBeenCalledWith('/sensors'); |
| })); |
| |
| it('should save sensor configuration', async(() => { |
| let fieldTransformer = Object.assign(new FieldTransformer(), { |
| input: [], |
| output: ['url_host'], |
| transformation: 'MTL', |
| config: { url_host: 'TO_LOWER(URL_TO_HOST(url))' } |
| }); |
| let sensorParserConfigSave: SensorParserConfig = new SensorParserConfig(); |
| sensorParserConfigSave.sensorTopic = 'squid'; |
| sensorParserConfigSave.parserClassName = |
| 'org.apache.metron.parsers.GrokParser'; |
| sensorParserConfigSave.parserConfig['grokPath'] = |
| '/apps/metron/patterns/squid'; |
| sensorParserConfigSave.fieldTransformations = [fieldTransformer]; |
| activatedRoute.setNameForTest('new'); |
| sensorParserConfigService.setThrowError(true); |
| |
| spyOn(metronAlerts, 'showSuccessMessage'); |
| spyOn(metronAlerts, 'showErrorMessage'); |
| |
| component.sensorParserConfig.sensorTopic = 'squid'; |
| component.sensorParserConfig.parserClassName = |
| 'org.apache.metron.parsers.GrokParser'; |
| component.sensorParserConfig.parserConfig['grokPath'] = |
| '/apps/metron/patterns/squid'; |
| component.sensorParserConfig.fieldTransformations = [fieldTransformer]; |
| |
| component.onSave(); |
| expect(metronAlerts.showErrorMessage['calls'].mostRecent().args[0]).toEqual( |
| 'Unable to save sensor config: SensorParserConfig post error' |
| ); |
| |
| component.sensorEnrichmentConfig = Object.assign( |
| new SensorEnrichmentConfig(), |
| squidSensorEnrichmentConfig |
| ); |
| component.indexingConfigurations = Object.assign( |
| new IndexingConfigurations(), |
| squidIndexingConfigurations |
| ); |
| sensorParserConfigService.setThrowError(false); |
| hdfsService.setContents( |
| '/apps/metron/patterns/squid', |
| 'SQUID grok statement' |
| ); |
| component.grokStatement = 'SQUID grok statement'; |
| |
| component.onSave(); |
| expect(sensorParserConfigService.getPostedSensorParserConfig()).toEqual( |
| sensorParserConfigSave |
| ); |
| expect( |
| sensorEnrichmentConfigService.getPostedSensorEnrichmentConfig() |
| ).toEqual( |
| Object.assign(new SensorEnrichmentConfig(), squidSensorEnrichmentConfig) |
| ); |
| expect( |
| sensorIndexingConfigService.getPostedIndexingConfigurations() |
| ).toEqual( |
| Object.assign(new IndexingConfigurations(), squidIndexingConfigurations) |
| ); |
| expect(hdfsService.getPostedContents()).toEqual('SQUID grok statement'); |
| |
| hdfsService.setContents('/apps/metron/patterns/squid', null); |
| |
| component.onSave(); |
| expect(metronAlerts.showErrorMessage['calls'].mostRecent().args[0]).toEqual( |
| 'HDFS post Error' |
| ); |
| |
| sensorEnrichmentConfigService.setThrowError(true); |
| |
| component.onSave(); |
| expect(metronAlerts.showErrorMessage['calls'].mostRecent().args[0]).toEqual( |
| 'Created Sensor parser config but unable to save enrichment configuration: SensorEnrichmentConfig post error' |
| ); |
| |
| sensorIndexingConfigService.setThrowError(true); |
| |
| component.onSave(); |
| expect(metronAlerts.showErrorMessage['calls'].mostRecent().args[0]).toEqual( |
| 'Created Sensor parser config but unable to save indexing configuration: IndexingConfigurations post error' |
| ); |
| })); |
| |
| it('should getTransformationCount', async(() => { |
| let transforms = [ |
| Object.assign(new FieldTransformer(), { |
| input: ['method'], |
| output: null, |
| transformation: 'REMOVE', |
| config: { |
| condition: 'exists(method) and method == "foo"' |
| } |
| }), |
| Object.assign(new FieldTransformer(), { |
| input: [], |
| output: ['method', 'status_code', 'url'], |
| transformation: 'STELLAR', |
| config: { |
| method: 'TO_UPPER(method)', |
| status_code: 'TO_LOWER(code)', |
| url: 'TO_STRING(TRIM(url))' |
| } |
| }) |
| ]; |
| |
| expect(component.getTransformationCount()).toEqual(0); |
| |
| fixture.componentInstance.sensorParserConfig.fieldTransformations = transforms; |
| expect(component.getTransformationCount()).toEqual(3); |
| |
| fixture.componentInstance.sensorParserConfig.fieldTransformations = [ |
| transforms[0] |
| ]; |
| expect(component.getTransformationCount()).toEqual(0); |
| })); |
| |
| it('should getEnrichmentCount', async(() => { |
| component.sensorEnrichmentConfig.enrichment.fieldMap['geo'] = [ |
| 'ip_src_addr', |
| 'ip_dst_addr' |
| ]; |
| component.sensorEnrichmentConfig.enrichment.fieldToTypeMap[ |
| 'hbaseenrichment' |
| ] = ['ip_src_addr', 'ip_dst_addr']; |
| |
| expect(component.getEnrichmentCount()).toEqual(4); |
| })); |
| |
| it('should getThreatIntelCount', async(() => { |
| component.sensorEnrichmentConfig.threatIntel.fieldToTypeMap[ |
| 'hbaseenrichment' |
| ] = ['ip_src_addr', 'ip_dst_addr']; |
| |
| expect(component.getThreatIntelCount()).toEqual(2); |
| })); |
| |
| it('should getRuleCount', async(() => { |
| let rule1 = Object.assign(new RiskLevelRule(), { |
| name: 'rule1', |
| rule: 'some rule', |
| score: 50 |
| }); |
| let rule2 = Object.assign(new RiskLevelRule(), { |
| name: 'rule2', |
| rule: 'some other rule', |
| score: 80 |
| }); |
| component.sensorEnrichmentConfig.threatIntel.triageConfig.riskLevelRules.push( |
| rule1 |
| ); |
| component.sensorEnrichmentConfig.threatIntel.triageConfig.riskLevelRules.push( |
| rule2 |
| ); |
| |
| expect(component.getRuleCount()).toEqual(2); |
| })); |
| |
| it('should showPane', async(() => { |
| component.showPane(Pane.GROK); |
| expect(component.showGrokValidator).toEqual(true); |
| expect(component.showFieldSchema).toEqual(false); |
| expect(component.showRawJson).toEqual(false); |
| |
| component.showPane(Pane.FIELDSCHEMA); |
| expect(component.showGrokValidator).toEqual(false); |
| expect(component.showFieldSchema).toEqual(true); |
| expect(component.showRawJson).toEqual(false); |
| |
| component.showPane(Pane.RAWJSON); |
| expect(component.showGrokValidator).toEqual(false); |
| expect(component.showFieldSchema).toEqual(false); |
| expect(component.showRawJson).toEqual(true); |
| })); |
| |
| it('should hidePane', async(() => { |
| component.hidePane(Pane.GROK); |
| expect(component.showGrokValidator).toEqual(false); |
| expect(component.showFieldSchema).toEqual(false); |
| expect(component.showRawJson).toEqual(false); |
| |
| component.hidePane(Pane.FIELDSCHEMA); |
| expect(component.showGrokValidator).toEqual(false); |
| expect(component.showFieldSchema).toEqual(false); |
| expect(component.showRawJson).toEqual(false); |
| |
| component.hidePane(Pane.RAWJSON); |
| expect(component.showGrokValidator).toEqual(false); |
| expect(component.showFieldSchema).toEqual(false); |
| expect(component.showRawJson).toEqual(false); |
| })); |
| |
| it('should handle onShowGrokPane', async(() => { |
| spyOn(component, 'showPane'); |
| component.sensorName = 'squid'; |
| |
| component.onShowGrokPane(); |
| expect(component.patternLabel).toEqual('SQUID'); |
| expect(component.showPane).toHaveBeenCalledWith(component.pane.GROK); |
| |
| component.patternLabel = 'PATTERN_LABEL'; |
| |
| component.onShowGrokPane(); |
| expect(component.patternLabel).toEqual('PATTERN_LABEL'); |
| })); |
| |
| it('should handle onRawJsonChanged', async(() => { |
| spyOn(component.sensorFieldSchema, 'createFieldSchemaRows'); |
| |
| component.onRawJsonChanged(); |
| |
| expect( |
| component.sensorFieldSchema.createFieldSchemaRows |
| ).toHaveBeenCalled(); |
| })); |
| |
| it('should handle onAdvancedConfigFormClose', async(() => { |
| component.onAdvancedConfigFormClose(); |
| |
| expect(component.showAdvancedParserConfiguration).toEqual(false); |
| })); |
| |
| it('should be timestamp by default', () => { |
| expect(component.sensorParserConfig.timestampField).toEqual('timestamp'); |
| }); |
| |
| it('should be invalid if timestamp field is empty', () => { |
| component.sensorNameValid = true; |
| component.kafkaTopicValid = true; |
| component.parserClassValid = true; |
| component.grokStatement = 'grokStatement'; |
| component.sensorParserConfig = new SensorParserConfig(); |
| component.sensorParserConfig.parserClassName = 'org.apache.metron.parsers.GrokParser' |
| component.sensorParserConfig.timestampField = ''; |
| expect(component.isConfigValid()).toEqual(false); |
| component.sensorParserConfig.timestampField = 'timestamp'; |
| expect(component.isConfigValid()).toEqual(true); |
| }); |
| }); |