Skip to content

Commit

Permalink
Audit our rule statements/switches
Browse files Browse the repository at this point in the history
I made a pass through our treatment of RuleType to verify that EQL rules
would be treated appropriately. Since the default/fallthrough case is
typically the Query rule, and since this rule has the same
attributes/behavior as the new EQL rule, not much had to change here.

I converted a few if statements to exhaustive switches where possible,
and used predicate helpers in places where it was not.
  • Loading branch information
rylnd committed Sep 9, 2020
1 parent 8d88dcb commit c85ffcf
Show file tree
Hide file tree
Showing 16 changed files with 86 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { isMlRule } from '../../../machine_learning/helpers';
import { isThresholdRule } from '../../utils';
import { AddPrepackagedRulesSchema } from './add_prepackaged_rules_schema';

export const validateAnomalyThreshold = (rule: AddPrepackagedRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.anomaly_threshold == null) {
return ['when "type" is "machine_learning" anomaly_threshold is required'];
} else {
Expand All @@ -19,7 +21,7 @@ export const validateAnomalyThreshold = (rule: AddPrepackagedRulesSchema): strin
};

export const validateQuery = (rule: AddPrepackagedRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.query != null) {
return ['when "type" is "machine_learning", "query" cannot be set'];
} else {
Expand All @@ -31,7 +33,7 @@ export const validateQuery = (rule: AddPrepackagedRulesSchema): string[] => {
};

export const validateLanguage = (rule: AddPrepackagedRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.language != null) {
return ['when "type" is "machine_learning", "language" cannot be set'];
} else {
Expand All @@ -55,7 +57,7 @@ export const validateSavedId = (rule: AddPrepackagedRulesSchema): string[] => {
};

export const validateMachineLearningJobId = (rule: AddPrepackagedRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.machine_learning_job_id == null) {
return ['when "type" is "machine_learning", "machine_learning_job_id" is required'];
} else {
Expand Down Expand Up @@ -93,7 +95,7 @@ export const validateTimelineTitle = (rule: AddPrepackagedRulesSchema): string[]
};

export const validateThreshold = (rule: AddPrepackagedRulesSchema): string[] => {
if (rule.type === 'threshold') {
if (isThresholdRule(rule.type)) {
if (!rule.threshold) {
return ['when "type" is "threshold", "threshold" is required'];
} else if (rule.threshold.value <= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { isMlRule } from '../../../machine_learning/helpers';
import { isThresholdRule } from '../../utils';
import { CreateRulesSchema } from './create_rules_schema';

export const validateAnomalyThreshold = (rule: CreateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.anomaly_threshold == null) {
return ['when "type" is "machine_learning" anomaly_threshold is required'];
} else {
Expand All @@ -19,7 +21,7 @@ export const validateAnomalyThreshold = (rule: CreateRulesSchema): string[] => {
};

export const validateQuery = (rule: CreateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.query != null) {
return ['when "type" is "machine_learning", "query" cannot be set'];
} else {
Expand All @@ -31,7 +33,7 @@ export const validateQuery = (rule: CreateRulesSchema): string[] => {
};

export const validateLanguage = (rule: CreateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.language != null) {
return ['when "type" is "machine_learning", "language" cannot be set'];
} else {
Expand All @@ -55,7 +57,7 @@ export const validateSavedId = (rule: CreateRulesSchema): string[] => {
};

export const validateMachineLearningJobId = (rule: CreateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.machine_learning_job_id == null) {
return ['when "type" is "machine_learning", "machine_learning_job_id" is required'];
} else {
Expand Down Expand Up @@ -93,7 +95,7 @@ export const validateTimelineTitle = (rule: CreateRulesSchema): string[] => {
};

export const validateThreshold = (rule: CreateRulesSchema): string[] => {
if (rule.type === 'threshold') {
if (isThresholdRule(rule.type)) {
if (!rule.threshold) {
return ['when "type" is "threshold", "threshold" is required'];
} else if (rule.threshold.value <= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { isMlRule } from '../../../machine_learning/helpers';
import { isThresholdRule } from '../../utils';
import { ImportRulesSchema } from './import_rules_schema';

export const validateAnomalyThreshold = (rule: ImportRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.anomaly_threshold == null) {
return ['when "type" is "machine_learning" anomaly_threshold is required'];
} else {
Expand All @@ -19,7 +21,7 @@ export const validateAnomalyThreshold = (rule: ImportRulesSchema): string[] => {
};

export const validateQuery = (rule: ImportRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.query != null) {
return ['when "type" is "machine_learning", "query" cannot be set'];
} else {
Expand All @@ -31,7 +33,7 @@ export const validateQuery = (rule: ImportRulesSchema): string[] => {
};

export const validateLanguage = (rule: ImportRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.language != null) {
return ['when "type" is "machine_learning", "language" cannot be set'];
} else {
Expand All @@ -55,7 +57,7 @@ export const validateSavedId = (rule: ImportRulesSchema): string[] => {
};

export const validateMachineLearningJobId = (rule: ImportRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.machine_learning_job_id == null) {
return ['when "type" is "machine_learning", "machine_learning_job_id" is required'];
} else {
Expand Down Expand Up @@ -93,7 +95,7 @@ export const validateTimelineTitle = (rule: ImportRulesSchema): string[] => {
};

export const validateThreshold = (rule: ImportRulesSchema): string[] => {
if (rule.type === 'threshold') {
if (isThresholdRule(rule.type)) {
if (!rule.threshold) {
return ['when "type" is "threshold", "threshold" is required'];
} else if (rule.threshold.value <= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { isMlRule } from '../../../machine_learning/helpers';
import { isThresholdRule } from '../../utils';
import { PatchRulesSchema } from './patch_rules_schema';

export const validateQuery = (rule: PatchRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.query != null) {
return ['when "type" is "machine_learning", "query" cannot be set'];
} else {
Expand All @@ -19,7 +21,7 @@ export const validateQuery = (rule: PatchRulesSchema): string[] => {
};

export const validateLanguage = (rule: PatchRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.language != null) {
return ['when "type" is "machine_learning", "language" cannot be set'];
} else {
Expand Down Expand Up @@ -67,7 +69,7 @@ export const validateId = (rule: PatchRulesSchema): string[] => {
};

export const validateThreshold = (rule: PatchRulesSchema): string[] => {
if (rule.type === 'threshold') {
if (isThresholdRule(rule.type)) {
if (!rule.threshold) {
return ['when "type" is "threshold", "threshold" is required'];
} else if (rule.threshold.value <= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { isMlRule } from '../../../machine_learning/helpers';
import { isThresholdRule } from '../../utils';
import { UpdateRulesSchema } from './update_rules_schema';

export const validateAnomalyThreshold = (rule: UpdateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.anomaly_threshold == null) {
return ['when "type" is "machine_learning" anomaly_threshold is required'];
} else {
Expand All @@ -19,7 +21,7 @@ export const validateAnomalyThreshold = (rule: UpdateRulesSchema): string[] => {
};

export const validateQuery = (rule: UpdateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.query != null) {
return ['when "type" is "machine_learning", "query" cannot be set'];
} else {
Expand All @@ -31,7 +33,7 @@ export const validateQuery = (rule: UpdateRulesSchema): string[] => {
};

export const validateLanguage = (rule: UpdateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.language != null) {
return ['when "type" is "machine_learning", "language" cannot be set'];
} else {
Expand All @@ -55,7 +57,7 @@ export const validateSavedId = (rule: UpdateRulesSchema): string[] => {
};

export const validateMachineLearningJobId = (rule: UpdateRulesSchema): string[] => {
if (rule.type === 'machine_learning') {
if (isMlRule(rule.type)) {
if (rule.machine_learning_job_id == null) {
return ['when "type" is "machine_learning", "machine_learning_job_id" is required'];
} else {
Expand Down Expand Up @@ -103,7 +105,7 @@ export const validateId = (rule: UpdateRulesSchema): string[] => {
};

export const validateThreshold = (rule: UpdateRulesSchema): string[] => {
if (rule.type === 'threshold') {
if (isThresholdRule(rule.type)) {
if (!rule.threshold) {
return ['when "type" is "threshold", "threshold" is required'];
} else if (rule.threshold.value <= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import * as t from 'io-ts';
import { isObject } from 'lodash/fp';
import { Either, left, fold } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/pipeable';
import { typeAndTimelineOnlySchema, TypeAndTimelineOnly } from './type_timeline_only_schema';
import { isMlRule } from '../../../machine_learning/helpers';

import { isMlRule } from '../../../machine_learning/helpers';
import { isThresholdRule } from '../../utils';
import {
actions,
anomaly_threshold,
Expand Down Expand Up @@ -66,6 +66,7 @@ import {
DefaultRiskScoreMappingArray,
DefaultSeverityMappingArray,
} from '../types';
import { typeAndTimelineOnlySchema, TypeAndTimelineOnly } from './type_timeline_only_schema';

/**
* This is the required fields for the rules schema response. Put all required properties on
Expand Down Expand Up @@ -229,7 +230,7 @@ export const addMlFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[]
};

export const addThresholdFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => {
if (typeAndTimelineOnly.type === 'threshold') {
if (isThresholdRule(typeAndTimelineOnly.type)) {
return [
t.exact(t.type({ threshold: dependentRulesSchema.props.threshold })),
t.exact(t.partial({ saved_id: dependentRulesSchema.props.saved_id })),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ export const hasNestedEntry = (entries: EntriesArray): boolean => {
return found.length > 0;
};

export const isEqlRule = (ruleType: Type) => ruleType === 'eql';
export const isThresholdRule = (ruleType: Type) => ruleType === 'threshold';
export const isQueryRule = (ruleType: Type) => ruleType === 'query';
export const isEqlRule = (ruleType: Type | undefined) => ruleType === 'eql';
export const isThresholdRule = (ruleType: Type | undefined) => ruleType === 'threshold';
export const isQueryRule = (ruleType: Type | undefined) => ruleType === 'query';
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ export const isJobFailed = (jobState: string, datafeedState: string): boolean =>
return failureStates.includes(jobState) || failureStates.includes(datafeedState);
};

export const isMlRule = (ruleType: Type) => ruleType === 'machine_learning';
export const isMlRule = (ruleType: Type | undefined) => ruleType === 'machine_learning';
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { ActionToaster, displayWarningToast } from '../../../../../common/components/toasters';
import { Rule } from '../../../../containers/detection_engine/rules';
import * as detectionI18n from '../../translations';
import { isMlRule } from '../../../../../../common/machine_learning/helpers';

interface GetBatchItems {
closePopover: () => void;
Expand Down Expand Up @@ -62,7 +63,7 @@ export const getBatchItems = ({
);

const deactivatedIdsNoML = deactivatedIds.filter(
(id) => rules.find((r) => r.id === id)?.type !== 'machine_learning' ?? false
(id) => !isMlRule(rules.find((r) => r.id === id)?.type)
);

const mlRuleCount = deactivatedIds.length - deactivatedIdsNoML.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import moment from 'moment';
import deepmerge from 'deepmerge';

import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../../common/constants';
import { assertUnreachable } from '../../../../../../common/utility_types';
import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions';
import { isMlRule } from '../../../../../../common/machine_learning/helpers';
import { isThresholdRule } from '../../../../../../common/detection_engine/utils';
import { List } from '../../../../../../common/detection_engine/schemas/types';
import { ENDPOINT_LIST_ID, ExceptionListType, NamespaceType } from '../../../../../shared_imports';
import { Rule } from '../../../../containers/detection_engine/rules';
Expand Down Expand Up @@ -88,16 +87,25 @@ const isThresholdFields = <T>(
): fields is ThresholdRuleFields<T> => has('threshold', fields);

export const filterRuleFieldsForType = <T extends RuleFields>(fields: T, type: Type) => {
if (isMlRule(type)) {
const { index, queryBar, threshold, ...mlRuleFields } = fields;
return mlRuleFields;
} else if (isThresholdRule(type)) {
const { anomalyThreshold, machineLearningJobId, ...thresholdRuleFields } = fields;
return thresholdRuleFields;
} else {
const { anomalyThreshold, machineLearningJobId, threshold, ...queryRuleFields } = fields;
return queryRuleFields;
switch (type) {
case 'machine_learning':
const { index, queryBar, threshold, ...mlRuleFields } = fields;
return mlRuleFields;
case 'threshold':
const { anomalyThreshold, machineLearningJobId, ...thresholdRuleFields } = fields;
return thresholdRuleFields;
case 'query':
case 'saved_query':
case 'eql':
const {
anomalyThreshold: _a,
machineLearningJobId: _m,
threshold: _t,
...queryRuleFields
} = fields;
return queryRuleFields;
}
assertUnreachable(type);
};

export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ export const RuleDetailsPageComponent: FC<PropsFromRedux> = ({
<EuiToolTip
position="top"
content={
rule?.type === 'machine_learning' && !hasMlPermissions
isMlRule(rule?.type) && !hasMlPermissions
? detectionI18n.ML_RULES_DISABLED_MESSAGE
: undefined
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useLocation } from 'react-router-dom';

import { ActionVariable } from '../../../../../../triggers_actions_ui/public';
import { RuleAlertAction } from '../../../../../common/detection_engine/types';
import { isMlRule } from '../../../../../common/machine_learning/helpers';
import { assertUnreachable } from '../../../../../common/utility_types';
import { transformRuleToAlertAction } from '../../../../../common/detection_engine/transform_actions';
import { Filter } from '../../../../../../../../src/plugins/data/public';
import { ENDPOINT_LIST_ID } from '../../../../shared_imports';
Expand Down Expand Up @@ -310,15 +310,17 @@ export const redirectToDetections = (
const getRuleSpecificRuleParamKeys = (ruleType: Type) => {
const queryRuleParams = ['index', 'filters', 'language', 'query', 'saved_id'];

if (isMlRule(ruleType)) {
return ['anomaly_threshold', 'machine_learning_job_id'];
switch (ruleType) {
case 'machine_learning':
return ['anomaly_threshold', 'machine_learning_job_id'];
case 'threshold':
return ['threshold', ...queryRuleParams];
case 'query':
case 'saved_query':
case 'eql':
return queryRuleParams;
}

if (ruleType === 'threshold') {
return ['threshold', ...queryRuleParams];
}

return queryRuleParams;
assertUnreachable(ruleType);
};

export const getActionMessageRuleParams = (ruleType: Type): string[] => {
Expand Down
Loading

0 comments on commit c85ffcf

Please sign in to comment.