diff --git a/CHANGELOG.md b/CHANGELOG.md index d699a9c35..fc15adeb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,4 +4,5 @@ ### Bug Fixes -- Improved exception handling in `automaticReview` to prevent the process from stopping due to errors, ensuring the system continues evaluating alerts even if a specific rule fails. -- Improved operator selection for more accurate and consistent filtering. --- Introduced new compliance reports aligned with the PCI DSS standard to expand auditing capabilities. \ No newline at end of file +-- Introduced new compliance reports aligned with the PCI DSS standard to expand auditing capabilities. +-- Enabled creation and update of tag-based rules with dynamic conditions. \ No newline at end of file diff --git a/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.html b/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.html index 1a038d97e..8019eca69 100644 --- a/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.html +++ b/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.html @@ -8,7 +8,7 @@
Alerts rules
Manage alerts diff --git a/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.ts b/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.ts index db456d788..c70732977 100644 --- a/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-rules/alert-rules.component.ts @@ -5,10 +5,12 @@ import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; import {SourcesService} from '../../../admin/sources/sources.service'; import {UtmToastService} from '../../../shared/alert/utm-toast.service'; import {ModalConfirmationComponent} from '../../../shared/components/utm/util/modal-confirmation/modal-confirmation.component'; +import {FALSE_POSITIVE_OBJECT} from '../../../shared/constants/alert/alert-field.constant'; import {ITEMS_PER_PAGE} from '../../../shared/constants/pagination.constants'; import {SortEvent} from '../../../shared/directives/sortable/type/sort-event'; import {AlertTags} from '../../../shared/types/alert/alert-tag.type'; import {TimeFilterType} from '../../../shared/types/time-filter.type'; +import {AlertRuleCreateComponent} from '../shared/components/alert-rule-create/alert-rule-create.component'; import {AlertRulesService} from '../shared/services/alert-rules.service'; import {AlertTagService} from '../shared/services/alert-tag.service'; import {AlertRuleType} from './alert-rule.type'; diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.html b/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.html index 71cc4e550..4178566cd 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.html +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.html @@ -1,4 +1,5 @@ - +
- - -
- - -
-
+
@@ -81,48 +82,70 @@ rows="2">
-
-
-
+
+
By creating a False Positive rule, future alerts where the fields and values match the operator defined will be tagged as False positive. The alerts that match with this rule will be marked as Completed. +
-
-
+
View rule fields    -
-
-
- {{ getFieldName(filter.field) }}:  - - - -
-
- +
+
+
+ {{ getFieldName(condition.get('field').value) }}:  + + + + + + +
+
+ +
+
+
+
diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts index 79931f59e..a2d04f8e2 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts @@ -1,6 +1,6 @@ import {HttpResponse} from '@angular/common/http'; import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core'; -import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms'; import {NgbActiveModal, NgbModal} from '@ng-bootstrap/ng-bootstrap'; import {UUID} from 'angular2-uuid'; import {Observable, Subject} from 'rxjs'; @@ -20,7 +20,7 @@ import { ALERT_INCIDENT_FLAG_FIELD, ALERT_INCIDENT_MODULE_FIELD, ALERT_INCIDENT_OBSERVATION_FIELD, - ALERT_INCIDENT_USER_FIELD, + ALERT_INCIDENT_USER_FIELD, ALERT_NAME_FIELD, ALERT_NOTE_FIELD, ALERT_OBSERVATION_FIELD, ALERT_REFERENCE_FIELD, @@ -142,18 +142,12 @@ export class AlertRuleCreateComponent implements OnInit, OnDestroy { ngOnInit() { this.initForm(); + this.createDefaultFilters(); this.getTags(); this.formRule.get('name').valueChanges.pipe(debounceTime(3000)).subscribe(ruleName => { this.searchRule(ruleName); }); - if (!this.alert) { - this.loading = true; - this.alertService.notifyRefresh(true); - } else { - this.createDefaultFilters(); - } - if (this.rule) { this.filters = [... this.rule.conditions]; this.selected = this.rule.tags.length > 0 ? [...this.rule.tags] : []; @@ -173,18 +167,52 @@ export class AlertRuleCreateComponent implements OnInit, OnDestroy { id: [this.rule ? this.rule.id : null], name: [ this.rule ? this.rule.name : '', Validators.required], description: [this.rule ? this.rule.description : '', Validators.required], - conditions: [ this.rule ? this.rule.conditions : [], Validators.required], + conditions: this.fb.array([], Validators.required), tags: [this.rule ? this.rule.tags : null, Validators.required], }); } createDefaultFilters() { + if (this.alert) { + this.initConditionsFromAlert(); + } else { + this.initConditionsFromAction(); + } + + this.subscribeToFieldChanges(); + } + + private initConditionsFromAlert() { for (const field of this.fields) { - if (this.getFieldValue(field.field)) { - this.filters.push({field: field.field, operator: ElasticOperatorsEnum.IS, value: this.getFieldValue(field.field)}); + const value = this.getFieldValue(field.field); + if (value) { + const condition = this.buildCondition(field.field, value); + this.filters.push(condition); + this.ruleConditions.push(this.createConditionGroup(condition)); } } - this.formRule.get('conditions').setValue(this.filters); + } + + private initConditionsFromAction() { + if (this.action === 'create') { + const field = this.fields[0]; + const condition = this.buildCondition(field.field, ''); + this.filters.push(condition); + this.ruleConditions.push(this.createConditionGroup(condition)); + } else if (this.action === 'update') { + this.filters = [...this.rule.conditions]; + this.rule.conditions.forEach(condition => { + this.ruleConditions.push(this.createConditionGroup(condition)); + }); + } + } + + private buildCondition(field: string, value: any) { + return { + field, + operator: ElasticOperatorsEnum.IS, + value + }; } getFieldValue(field: string): any { @@ -262,8 +290,8 @@ export class AlertRuleCreateComponent implements OnInit, OnDestroy { } - deleteFilter(filter: ElasticFilterType) { - const index = this.filters.indexOf(filter); + deleteFilter(elasticFilterType: ElasticFilterType) { + const index = this.filters.indexOf(elasticFilterType); if (index !== -1) { this.filters.splice(index, 1); this.formRule.get('conditions').setValue(this.filters); @@ -326,8 +354,8 @@ export class AlertRuleCreateComponent implements OnInit, OnDestroy { return this.selected.findIndex(value => value.tagName.includes('False positive')) !== -1; } - getOperators(filter: ElasticFilterType) { - const field = this.fields.find(f => f.field === filter.field); + getOperators(conditionField: string) { + const field = this.fields.find(f => f.field === conditionField); if (field) { return this.operatorService.getOperators({name: field.field, type: field.type}, this.operators); } @@ -391,6 +419,48 @@ export class AlertRuleCreateComponent implements OnInit, OnDestroy { this.activeModal.close(); } + get ruleConditions() { + return this.formRule.get('conditions') as FormArray; + } + + removeRuleCondition(index: number) { + this.ruleConditions.removeAt(index); + } + + createConditionGroup(condition: any): FormGroup { + return this.fb.group({ + field: [condition.field, Validators.required], + operator: [condition.operator, Validators.required], + value: [condition.value] + }); + } + + subscribeToFieldChanges() { + this.ruleConditions.controls.forEach((group: AbstractControl, index: number) => { + const fieldControl = group.get('field'); + const operatorControl = group.get('operator'); + + fieldControl.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(newField => { + const validOperators = this.getOperators(newField).map(op => op.operator); + if (!validOperators.includes(operatorControl.value)) { + operatorControl.setValue(null); + } + }); + }); + } + + addRuleCondition() { + this.ruleConditions.push( + this.createConditionGroup({ + field: this.fields[0].field, + operator: null, + value: null + })); + this.subscribeToFieldChanges(); + } + ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete();