[SIEM][Detection Engine] Fixes TypeScript types and adds format to time range query

## Summary

* Fixes the Type Script types so we don't have to use non-null-assertions
* Adds null checks where needed
* Changes the time range query to have a format of epoch to avoid mapping issues

### Checklist

- [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios
This commit is contained in:
Frank Hassanabad 2020-04-07 19:37:47 -06:00 committed by GitHub
parent 7e3c68bb7a
commit 5218e30487
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 84 additions and 42 deletions

View file

@ -70,7 +70,7 @@ const MetaRule = t.intersection([
}),
t.partial({
throttle: t.string,
kibanaSiemAppUrl: t.string,
kibana_siem_app_url: t.string,
}),
]);

View file

@ -515,7 +515,7 @@ describe('helpers', () => {
actions: [],
enabled: false,
meta: {
kibanaSiemAppUrl: 'http://localhost:5601/app/siem',
kibana_siem_app_url: 'http://localhost:5601/app/siem',
},
throttle: 'no_actions',
};
@ -533,7 +533,7 @@ describe('helpers', () => {
actions: [],
enabled: false,
meta: {
kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl,
kibana_siem_app_url: mockStepData.kibanaSiemAppUrl,
},
throttle: 'no_actions',
};
@ -566,7 +566,7 @@ describe('helpers', () => {
],
enabled: false,
meta: {
kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl,
kibana_siem_app_url: mockStepData.kibanaSiemAppUrl,
},
throttle: 'rule',
};
@ -599,7 +599,7 @@ describe('helpers', () => {
],
enabled: false,
meta: {
kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl,
kibana_siem_app_url: mockStepData.kibanaSiemAppUrl,
},
throttle: mockStepData.throttle,
};
@ -631,7 +631,7 @@ describe('helpers', () => {
],
enabled: false,
meta: {
kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl,
kibana_siem_app_url: mockStepData.kibanaSiemAppUrl,
},
throttle: 'no_actions',
};

View file

@ -155,7 +155,7 @@ export const formatActionsStepData = (actionsStepData: ActionsStepRule): Actions
enabled,
throttle: actions.length ? throttle : NOTIFICATION_THROTTLE_NO_ACTIONS,
meta: {
kibanaSiemAppUrl,
kibana_siem_app_url: kibanaSiemAppUrl,
},
};
};

View file

@ -64,7 +64,7 @@ export const getActionsStepsData = (
actions: actions?.map(transformRuleToAlertAction),
isNew: false,
throttle,
kibanaSiemAppUrl: meta?.kibanaSiemAppUrl,
kibanaSiemAppUrl: meta?.kibana_siem_app_url,
enabled,
};
};

View file

@ -41,6 +41,7 @@ describe('buildSignalsSearchQuery', () => {
'@timestamp': {
gt: from,
lte: to,
format: 'epoch_millis',
},
},
},

View file

@ -32,6 +32,7 @@ export const buildSignalsSearchQuery = ({ ruleId, index, from, to }: BuildSignal
'@timestamp': {
gt: from,
lte: to,
format: 'epoch_millis',
},
},
},

View file

@ -53,7 +53,7 @@ export const rulesNotificationAlertType = ({
from: fromInMs,
to: toInMs,
index: ruleParams.outputIndex,
ruleId: ruleParams.ruleId!,
ruleId: ruleParams.ruleId,
callCluster: services.callCluster,
});
@ -61,14 +61,14 @@ export const rulesNotificationAlertType = ({
from: fromInMs,
to: toInMs,
id: ruleAlertSavedObject.id,
kibanaSiemAppUrl: ruleAlertParams.meta?.kibanaSiemAppUrl as string,
kibanaSiemAppUrl: ruleAlertParams.meta?.kibana_siem_app_url,
});
logger.info(
`Found ${signalsCount} signals using signal rule name: "${ruleParams.name}", id: "${params.ruleAlertId}", rule_id: "${ruleParams.ruleId}" in "${ruleParams.outputIndex}" index`
);
if (signalsCount) {
if (signalsCount !== 0) {
const alertInstance = services.alertInstanceFactory(alertId);
scheduleNotificationActions({ alertInstance, signalsCount, resultsLink, ruleParams });
}

View file

@ -8,7 +8,7 @@ import { mapKeys, snakeCase } from 'lodash/fp';
import { AlertInstance } from '../../../../../../../plugins/alerting/server';
import { RuleTypeParams } from '../types';
type NotificationRuleTypeParams = RuleTypeParams & {
export type NotificationRuleTypeParams = RuleTypeParams & {
name: string;
id: string;
};

View file

@ -10,7 +10,7 @@ export const getNotificationResultsLink = ({
from,
to,
}: {
kibanaSiemAppUrl: string;
kibanaSiemAppUrl?: string;
id: string;
from?: string;
to?: string;

View file

@ -121,15 +121,15 @@ export const patchRulesBulkRoute = (router: IRouter) => {
anomalyThreshold,
machineLearningJobId,
});
if (rule != null) {
if (rule != null && rule.enabled != null && rule.name != null) {
const ruleActions = await updateRulesNotifications({
ruleAlertId: rule.id,
alertsClient,
savedObjectsClient,
enabled: rule.enabled!,
enabled: rule.enabled,
actions,
throttle,
name: rule.name!,
name: rule.name,
});
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes

View file

@ -117,15 +117,15 @@ export const patchRulesRoute = (router: IRouter) => {
anomalyThreshold,
machineLearningJobId,
});
if (rule != null) {
if (rule != null && rule.enabled != null && rule.name != null) {
const ruleActions = await updateRulesNotifications({
ruleAlertId: rule.id,
alertsClient,
savedObjectsClient,
enabled: rule.enabled!,
enabled: rule.enabled,
actions,
throttle,
name: rule.name!,
name: rule.name,
});
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes

View file

@ -309,7 +309,7 @@ export const validateLicenseForRuleType = ({
}: {
license: ILicense;
ruleType: RuleType;
}) => {
}): void => {
if (isMlRule(ruleType) && !license.hasAtLeast(MINIMUM_ML_LICENSE)) {
const message = i18n.translate('xpack.siem.licensing.unsupportedMachineLearningMessage', {
defaultMessage:

View file

@ -14,7 +14,7 @@ interface CreateRuleActionsSavedObject {
ruleAlertId: string;
savedObjectsClient: AlertServices['savedObjectsClient'];
actions: RuleAlertAction[] | undefined;
throttle: string | undefined;
throttle: string | null | undefined;
}
export const createRuleActionsSavedObject = async ({
@ -22,7 +22,12 @@ export const createRuleActionsSavedObject = async ({
savedObjectsClient,
actions = [],
throttle,
}: CreateRuleActionsSavedObject) => {
}: CreateRuleActionsSavedObject): Promise<{
id: string;
actions: RuleAlertAction[];
alertThrottle: string | null;
ruleThrottle: string;
}> => {
const ruleActionsSavedObject = await savedObjectsClient.create<
IRuleActionsAttributesSavedObjectAttributes
>(ruleActionsSavedObjectType, {

View file

@ -16,7 +16,7 @@ interface DeleteRuleActionsSavedObject {
export const deleteRuleActionsSavedObject = async ({
ruleAlertId,
savedObjectsClient,
}: DeleteRuleActionsSavedObject) => {
}: DeleteRuleActionsSavedObject): Promise<{} | null> => {
const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient });
if (!ruleActions) return null;

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RuleAlertAction } from '../../../../common/detection_engine/types';
import { AlertServices } from '../../../../../../../plugins/alerting/server';
import { ruleActionsSavedObjectType } from './saved_object_mappings';
import { IRuleActionsAttributesSavedObjectAttributes } from './types';
@ -17,7 +18,12 @@ interface GetRuleActionsSavedObject {
export const getRuleActionsSavedObject = async ({
ruleAlertId,
savedObjectsClient,
}: GetRuleActionsSavedObject) => {
}: GetRuleActionsSavedObject): Promise<{
id: string;
actions: RuleAlertAction[];
alertThrottle: string | null;
ruleThrottle: string;
} | null> => {
const { saved_objects } = await savedObjectsClient.find<
IRuleActionsAttributesSavedObjectAttributes
>({

View file

@ -15,7 +15,7 @@ interface UpdateOrCreateRuleActionsSavedObject {
ruleAlertId: string;
savedObjectsClient: AlertServices['savedObjectsClient'];
actions: RuleAlertAction[] | undefined;
throttle: string | undefined;
throttle: string | null | undefined;
}
export const updateOrCreateRuleActionsSavedObject = async ({

View file

@ -15,7 +15,7 @@ interface DeleteRuleActionsSavedObject {
ruleAlertId: string;
savedObjectsClient: AlertServices['savedObjectsClient'];
actions: RuleAlertAction[] | undefined;
throttle: string | undefined;
throttle: string | null | undefined;
}
export const updateRuleActionsSavedObject = async ({
@ -23,7 +23,12 @@ export const updateRuleActionsSavedObject = async ({
savedObjectsClient,
actions,
throttle,
}: DeleteRuleActionsSavedObject) => {
}: DeleteRuleActionsSavedObject): Promise<{
ruleThrottle: string;
alertThrottle: string | null;
actions: RuleAlertAction[];
id: string;
} | null> => {
const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient });
if (!ruleActions) return null;

View file

@ -5,16 +5,27 @@
*/
import { SavedObjectsUpdateResponse } from 'kibana/server';
import { RuleAlertAction } from '../../../../common/detection_engine/types';
import { IRuleActionsAttributesSavedObjectAttributes } from './types';
export const getThrottleOptions = (throttle = 'no_actions') => ({
ruleThrottle: throttle,
alertThrottle: ['no_actions', 'rule'].includes(throttle) ? null : throttle,
export const getThrottleOptions = (
throttle: string | undefined | null = 'no_actions'
): {
ruleThrottle: string;
alertThrottle: string | null;
} => ({
ruleThrottle: throttle ?? 'no_actions',
alertThrottle: ['no_actions', 'rule'].includes(throttle ?? 'no_actions') ? null : throttle,
});
export const getRuleActionsFromSavedObject = (
savedObject: SavedObjectsUpdateResponse<IRuleActionsAttributesSavedObjectAttributes>
) => ({
): {
id: string;
actions: RuleAlertAction[];
alertThrottle: string | null;
ruleThrottle: string;
} => ({
id: savedObject.id,
actions: savedObject.attributes.actions || [],
alertThrottle: savedObject.attributes.alertThrottle || null,

View file

@ -4,7 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AlertsClient, AlertServices } from '../../../../../../../plugins/alerting/server';
import {
AlertsClient,
AlertServices,
PartialAlert,
} from '../../../../../../../plugins/alerting/server';
import { getRuleActionsSavedObject } from '../rule_actions/get_rule_actions_saved_object';
import { readRules } from './read_rules';
import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions';
@ -19,7 +23,7 @@ export const updateRuleActions = async ({
alertsClient,
savedObjectsClient,
ruleAlertId,
}: UpdateRuleActions) => {
}: UpdateRuleActions): Promise<PartialAlert | null> => {
const rule = await readRules({ alertsClient, id: ruleAlertId });
if (rule == null) {
return null;

View file

@ -9,13 +9,14 @@ import { AlertsClient, AlertServices } from '../../../../../../../plugins/alerti
import { updateOrCreateRuleActionsSavedObject } from '../rule_actions/update_or_create_rule_actions_saved_object';
import { updateNotifications } from '../notifications/update_notifications';
import { updateRuleActions } from './update_rule_actions';
import { RuleActions } from '../rule_actions/types';
interface UpdateRulesNotifications {
alertsClient: AlertsClient;
savedObjectsClient: AlertServices['savedObjectsClient'];
ruleAlertId: string;
actions: RuleAlertAction[] | undefined;
throttle: string | undefined;
throttle: string | null | undefined;
enabled: boolean;
name: string;
}
@ -28,7 +29,7 @@ export const updateRulesNotifications = async ({
enabled,
name,
throttle,
}: UpdateRulesNotifications) => {
}: UpdateRulesNotifications): Promise<RuleActions> => {
const ruleActions = await updateOrCreateRuleActionsSavedObject({
savedObjectsClient,
ruleAlertId,

View file

@ -47,7 +47,7 @@ const getPayload = (
interval: ruleAlert.schedule.interval,
name: ruleAlert.name,
tags: ruleAlert.tags,
throttle: ruleAlert.throttle!,
throttle: ruleAlert.throttle,
scrollSize: 10,
scrollLock: '0',
},

View file

@ -24,7 +24,10 @@ import { signalParamsSchema } from './signal_params_schema';
import { siemRuleActionGroups } from './siem_rule_action_groups';
import { findMlSignals } from './find_ml_signals';
import { bulkCreateMlSignals } from './bulk_create_ml_signals';
import { scheduleNotificationActions } from '../notifications/schedule_notification_actions';
import {
scheduleNotificationActions,
NotificationRuleTypeParams,
} from '../notifications/schedule_notification_actions';
import { ruleStatusServiceFactory } from './rule_status_service';
import { buildRuleMessageFactory } from './rule_messages';
import { ruleStatusSavedObjectsClientFactory } from './rule_status_saved_objects_client';
@ -246,7 +249,7 @@ export const signalRulesAlertType = ({
if (result.success) {
if (actions.length) {
const notificationRuleParams = {
const notificationRuleParams: NotificationRuleTypeParams = {
...ruleParams,
name,
id: savedObject.id,
@ -259,7 +262,7 @@ export const signalRulesAlertType = ({
from: fromInMs,
to: toInMs,
id: savedObject.id,
kibanaSiemAppUrl: meta?.kibanaSiemAppUrl as string,
kibanaSiemAppUrl: meta?.kibana_siem_app_url,
});
logger.info(

View file

@ -162,5 +162,5 @@ export interface AlertAttributes {
}
export interface RuleAlertAttributes extends AlertAttributes {
params: RuleAlertParams;
params: Omit<RuleAlertParams, 'ruleId'> & { ruleId: string };
}

View file

@ -29,6 +29,11 @@ export interface ThreatParams {
// We don't have the input types defined through io-ts just yet but as we being introducing types from there we will more and more remove
// types and share them between input and output schema but have an input Rule Schema and an output Rule Schema.
export interface Meta {
[key: string]: {} | string | undefined | null;
kibana_siem_app_url?: string | undefined;
}
export interface RuleAlertParams {
actions: RuleAlertAction[];
anomalyThreshold: number | undefined;
@ -51,7 +56,7 @@ export interface RuleAlertParams {
query: string | undefined | null;
references: string[];
savedId?: string | undefined | null;
meta: Record<string, {} | string> | undefined | null;
meta: Meta | undefined | null;
severity: string;
tags: string[];
to: string;
@ -60,7 +65,7 @@ export interface RuleAlertParams {
threat: ThreatParams[] | undefined | null;
type: RuleType;
version: number;
throttle: string;
throttle: string | undefined | null;
lists: ListsDefaultArraySchema | null | undefined;
}