mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Threat match tests for term query (#152071)
Added several tests for terms query rules, which tests that match and term query rules are the same. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
06c70d96c6
commit
b956d91b10
2 changed files with 311 additions and 262 deletions
|
@ -5,7 +5,7 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { get, isEqual } from 'lodash';
|
import { get, isEqual, omit } from 'lodash';
|
||||||
import expect from '@kbn/expect';
|
import expect from '@kbn/expect';
|
||||||
import {
|
import {
|
||||||
ALERT_REASON,
|
ALERT_REASON,
|
||||||
|
@ -19,6 +19,7 @@ import {
|
||||||
VERSION,
|
VERSION,
|
||||||
} from '@kbn/rule-data-utils';
|
} from '@kbn/rule-data-utils';
|
||||||
import { flattenWithPrefix } from '@kbn/securitysolution-rules';
|
import { flattenWithPrefix } from '@kbn/securitysolution-rules';
|
||||||
|
import { ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||||
|
|
||||||
import { ThreatMatchRuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema';
|
import { ThreatMatchRuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema';
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ import {
|
||||||
ALERT_ORIGINAL_EVENT_MODULE,
|
ALERT_ORIGINAL_EVENT_MODULE,
|
||||||
ALERT_ORIGINAL_TIME,
|
ALERT_ORIGINAL_TIME,
|
||||||
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||||
|
import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/rule_monitoring';
|
||||||
import {
|
import {
|
||||||
previewRule,
|
previewRule,
|
||||||
getOpenSignals,
|
getOpenSignals,
|
||||||
|
@ -41,7 +43,6 @@ import {
|
||||||
createRule,
|
createRule,
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||||
|
|
||||||
const format = (value: unknown): string => JSON.stringify(value, null, 2);
|
const format = (value: unknown): string => JSON.stringify(value, null, 2);
|
||||||
|
|
||||||
// Asserts that each expected value is included in the subject, independent of
|
// Asserts that each expected value is included in the subject, independent of
|
||||||
|
@ -54,6 +55,85 @@ const assertContains = (subject: unknown[], expected: unknown[]) =>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const createThreatMatchRule = ({
|
||||||
|
name = 'Query with a rule id',
|
||||||
|
index = ['auditbeat-*'],
|
||||||
|
query = '*:*',
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
rule_id = 'rule-1',
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
threat_indicator_path = 'threat.indicator',
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
threat_query = 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
threat_index = ['auditbeat-*'],
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
threat_mapping = [
|
||||||
|
// We match host.name against host.name
|
||||||
|
{
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
field: 'host.name',
|
||||||
|
value: 'host.name',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
override = {},
|
||||||
|
}: {
|
||||||
|
threat_mapping?: ThreatMapping;
|
||||||
|
name?: string;
|
||||||
|
query?: string;
|
||||||
|
rule_id?: string;
|
||||||
|
index?: string[];
|
||||||
|
threat_index?: string[];
|
||||||
|
threat_query?: string;
|
||||||
|
threat_indicator_path?: string;
|
||||||
|
override?: any;
|
||||||
|
} = {}): ThreatMatchRuleCreateProps => ({
|
||||||
|
description: 'Detecting root and admin users',
|
||||||
|
name,
|
||||||
|
severity: 'high',
|
||||||
|
index,
|
||||||
|
type: 'threat_match',
|
||||||
|
risk_score: 55,
|
||||||
|
language: 'kuery',
|
||||||
|
rule_id,
|
||||||
|
from: '1900-01-01T00:00:00.000Z',
|
||||||
|
query,
|
||||||
|
threat_query,
|
||||||
|
threat_index,
|
||||||
|
threat_mapping,
|
||||||
|
threat_filters: [],
|
||||||
|
threat_indicator_path,
|
||||||
|
...override,
|
||||||
|
});
|
||||||
|
|
||||||
|
function alertsAreTheSame(alertsA: any[], alertsB: any[]): void {
|
||||||
|
const mapAlert = (alert: any) => {
|
||||||
|
return omit(alert._source, [
|
||||||
|
'@timestamp',
|
||||||
|
'kibana.alert.last_detected',
|
||||||
|
'kibana.rule.created_at',
|
||||||
|
'kibana.rule.execution.uuid',
|
||||||
|
'kibana.name.execution.uuid',
|
||||||
|
'kibana.alert.rule.parameters',
|
||||||
|
'kibana.alert.rule.rule_id',
|
||||||
|
'kibana.alert.rule.name',
|
||||||
|
'kibana.alert.rule.created_at',
|
||||||
|
'kibana.alert.rule.updated_at',
|
||||||
|
'kibana.alert.rule.uuid',
|
||||||
|
'kibana.alert.rule.execution.uuid',
|
||||||
|
'kibana.alert.start',
|
||||||
|
'kibana.alert.reason',
|
||||||
|
'kibana.alert.uuid',
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(alertsA.map(mapAlert)).to.eql(alertsB.map(mapAlert));
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default ({ getService }: FtrProviderContext) => {
|
export default ({ getService }: FtrProviderContext) => {
|
||||||
const esArchiver = getService('esArchiver');
|
const esArchiver = getService('esArchiver');
|
||||||
|
@ -66,6 +146,8 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
*/
|
*/
|
||||||
describe('Threat match type rules', () => {
|
describe('Threat match type rules', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
// await deleteSignalsIndex(supertest, log);
|
||||||
|
// await deleteAllAlerts(supertest, log);
|
||||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,38 +158,19 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// First 2 test creates a real rule - remaining tests use preview API
|
// First 2 test creates a real rule - remaining tests use preview API
|
||||||
it('should be able to execute and get 10 signals when doing a specific query (terms query)', async () => {
|
it('should be able to execute and get all signals when doing a specific query (terms query)', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule();
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*',
|
|
||||||
threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
|
||||||
threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
|
|
||||||
threat_mapping: [
|
|
||||||
// We match host.name against host.name
|
|
||||||
{
|
|
||||||
entries: [
|
|
||||||
{
|
|
||||||
field: 'host.name',
|
|
||||||
value: 'host.name',
|
|
||||||
type: 'mapping',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
threat_filters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const createdRule = await createRule(supertest, log, rule);
|
const createdRule = await createRule(supertest, log, rule);
|
||||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
const alerts = await getOpenSignals(
|
||||||
expect(alerts.hits.hits.length).equal(10);
|
supertest,
|
||||||
|
log,
|
||||||
|
es,
|
||||||
|
createdRule,
|
||||||
|
RuleExecutionStatus.succeeded,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
expect(alerts.hits.hits.length).equal(88);
|
||||||
const fullSource = alerts.hits.hits.find(
|
const fullSource = alerts.hits.hits.find(
|
||||||
(signal) =>
|
(signal) =>
|
||||||
(signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
(signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||||
|
@ -257,20 +320,8 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should be able to execute and get 10 signals when doing a specific query (match query)', async () => {
|
it('should be able to execute and get all signals when doing a specific query (match query)', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*',
|
|
||||||
threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
|
||||||
threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
|
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
// We match host.name against host.name
|
// We match host.name against host.name
|
||||||
{
|
{
|
||||||
|
@ -288,12 +339,18 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const createdRule = await createRule(supertest, log, rule);
|
const createdRule = await createRule(supertest, log, rule);
|
||||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
const alerts = await getOpenSignals(
|
||||||
expect(alerts.hits.hits.length).equal(10);
|
supertest,
|
||||||
|
log,
|
||||||
|
es,
|
||||||
|
createdRule,
|
||||||
|
RuleExecutionStatus.succeeded,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
expect(alerts.hits.hits.length).equal(88);
|
||||||
const fullSource = alerts.hits.hits.find(
|
const fullSource = alerts.hits.hits.find(
|
||||||
(signal) =>
|
(signal) =>
|
||||||
(signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
(signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||||
|
@ -444,20 +501,64 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('terms and match should have the same alerts with pagination', async () => {
|
||||||
|
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
|
override: {
|
||||||
|
items_per_search: 1,
|
||||||
|
concurrent_searches: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const matchRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
|
override: {
|
||||||
|
items_per_search: 1,
|
||||||
|
concurrent_searches: 1,
|
||||||
|
},
|
||||||
|
name: 'Math rule',
|
||||||
|
rule_id: 'rule-2',
|
||||||
|
threat_mapping: [
|
||||||
|
// We match host.name against host.name
|
||||||
|
{
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
field: 'host.name',
|
||||||
|
value: 'host.name',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'host.name',
|
||||||
|
value: 'host.name',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdRuleTerm = await createRule(supertest, log, termRule);
|
||||||
|
const createdRuleMatch = await createRule(supertest, log, matchRule);
|
||||||
|
const alertsTerm = await getOpenSignals(
|
||||||
|
supertest,
|
||||||
|
log,
|
||||||
|
es,
|
||||||
|
createdRuleTerm,
|
||||||
|
RuleExecutionStatus.succeeded,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
const alertsMatch = await getOpenSignals(
|
||||||
|
supertest,
|
||||||
|
log,
|
||||||
|
es,
|
||||||
|
createdRuleMatch,
|
||||||
|
RuleExecutionStatus.succeeded,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
alertsAreTheSame(alertsTerm.hits.hits, alertsMatch.hits.hits);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return 0 matches if the mapping does not match against anything in the mapping', async () => {
|
it('should return 0 matches if the mapping does not match against anything in the mapping', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*',
|
|
||||||
threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
|
||||||
threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
|
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
// We match host.name against host.name
|
// We match host.name against host.name
|
||||||
{
|
{
|
||||||
|
@ -470,8 +571,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
|
@ -479,19 +579,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 0 signals when using an AND and one of the clauses does not have data', async () => {
|
it('should return 0 signals when using an AND and one of the clauses does not have data', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*',
|
|
||||||
threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
|
||||||
threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
|
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
{
|
{
|
||||||
entries: [
|
entries: [
|
||||||
|
@ -508,8 +596,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
|
@ -517,19 +604,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 0 signals when using an AND and one of the clauses has a made up value that does not exist', async () => {
|
it('should return 0 signals when using an AND and one of the clauses has a made up value that does not exist', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
type: 'threat_match',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*',
|
|
||||||
threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
|
||||||
threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
|
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
{
|
{
|
||||||
entries: [
|
entries: [
|
||||||
|
@ -546,8 +621,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
|
@ -557,35 +631,13 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
describe('timeout behavior', () => {
|
describe('timeout behavior', () => {
|
||||||
// TODO: unskip this and see if we can make it not flaky
|
// TODO: unskip this and see if we can make it not flaky
|
||||||
it.skip('will return an error if a rule execution exceeds the rule interval', async () => {
|
it.skip('will return an error if a rule execution exceeds the rule interval', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
override: {
|
||||||
name: 'Query with a short interval',
|
concurrent_searches: 1,
|
||||||
severity: 'high',
|
interval: '1s', // short interval
|
||||||
index: ['auditbeat-*'],
|
items_per_search: 1, // iterate only 1 threat item per loop to ensure we're slow
|
||||||
type: 'threat_match',
|
},
|
||||||
risk_score: 55,
|
});
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*',
|
|
||||||
threat_query: '*:*', // broad query to take more time
|
|
||||||
threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
|
|
||||||
threat_mapping: [
|
|
||||||
{
|
|
||||||
entries: [
|
|
||||||
{
|
|
||||||
field: 'host.name',
|
|
||||||
value: 'host.name',
|
|
||||||
type: 'mapping',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
threat_filters: [],
|
|
||||||
concurrent_searches: 1,
|
|
||||||
interval: '1s', // short interval
|
|
||||||
items_per_search: 1, // iterate only 1 threat item per loop to ensure we're slow
|
|
||||||
};
|
|
||||||
|
|
||||||
const { logs } = await previewRule({ supertest, rule });
|
const { logs } = await previewRule({ supertest, rule });
|
||||||
expect(logs[0].errors[0]).to.contain('execution has exceeded its allotted interval');
|
expect(logs[0].errors[0]).to.contain('execution has exceeded its allotted interval');
|
||||||
|
@ -602,20 +654,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enriches signals with the single indicator that matched', async () => {
|
it('enriches signals with the single indicator that matched', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*', // narrow events down to 2 with a destination.ip
|
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: 'threat.indicator.domain: 159.89.119.67', // narrow things down to indicators with a domain
|
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
{
|
{
|
||||||
entries: [
|
entries: [
|
||||||
|
@ -627,8 +666,9 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
threat_query: 'threat.indicator.domain: 159.89.119.67', // narrow things down to indicators with a domain
|
||||||
};
|
threat_index: ['filebeat-*'],
|
||||||
|
});
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
|
@ -690,18 +730,8 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enriches signals with multiple indicators if several matched', async () => {
|
it('enriches signals with multiple indicators if several matched', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: 'NOT source.port:35326', // specify query to have signals more than treat indicators, but only 1 will match
|
query: 'NOT source.port:35326', // specify query to have signals more than treat indicators, but only 1 will match
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: 'threat.indicator.ip: *',
|
threat_query: 'threat.indicator.ip: *',
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
|
@ -715,9 +745,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
expect(previewAlerts.length).equal(1);
|
expect(previewAlerts.length).equal(1);
|
||||||
|
@ -767,18 +795,8 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds a single indicator that matched multiple fields', async () => {
|
it('adds a single indicator that matched multiple fields', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: 'NOT source.port:35326', // specify query to have signals more than treat indicators, but only 1 will match
|
query: 'NOT source.port:35326', // specify query to have signals more than treat indicators, but only 1 will match
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: 'threat.indicator.port: 57324 or threat.indicator.ip:45.115.45.3', // narrow our query to a single indicator
|
threat_query: 'threat.indicator.port: 57324 or threat.indicator.ip:45.115.45.3', // narrow our query to a single indicator
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
|
@ -801,8 +819,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
|
@ -876,19 +893,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates multiple signals with multiple matches', async () => {
|
it('generates multiple signals with multiple matches', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
threat_language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*', // narrow our query to a single record that matches two indicators
|
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: '*:*',
|
threat_query: '*:*',
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
|
@ -916,8 +921,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
|
@ -1023,18 +1027,8 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enriches signals with the single indicator that matched', async () => {
|
it('enriches signals with the single indicator that matched', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: 'destination.ip:159.89.119.67',
|
query: 'destination.ip:159.89.119.67',
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: 'threat.indicator.domain: *', // narrow things down to indicators with a domain
|
threat_query: 'threat.indicator.domain: *', // narrow things down to indicators with a domain
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
|
@ -1048,14 +1042,36 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
const matchRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
|
query: 'destination.ip:159.89.119.67',
|
||||||
|
threat_query: 'threat.indicator.domain: *', // narrow things down to indicators with a domain
|
||||||
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
|
threat_mapping: [
|
||||||
|
{
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.domain',
|
||||||
|
field: 'destination.ip',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.domain',
|
||||||
|
field: 'destination.ip',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId: termPrevieId } = await previewRule({ supertest, rule: termRule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const termPreviewAlerts = await getPreviewAlerts({ es, previewId: termPrevieId });
|
||||||
expect(previewAlerts.length).equal(2);
|
const { previewId: matchPreviewId } = await previewRule({ supertest, rule: matchRule });
|
||||||
|
const matchPrevieAlerts = await getPreviewAlerts({ es, previewId: matchPreviewId });
|
||||||
|
expect(termPreviewAlerts.length).equal(2);
|
||||||
|
|
||||||
const threats = previewAlerts.map((hit) => hit._source?.threat);
|
const threats = termPreviewAlerts.map((hit) => hit._source?.threat);
|
||||||
expect(threats).to.eql([
|
expect(threats).to.eql([
|
||||||
{
|
{
|
||||||
enrichments: [
|
enrichments: [
|
||||||
|
@ -1108,21 +1124,12 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
alertsAreTheSame(termPreviewAlerts, matchPrevieAlerts);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enriches signals with multiple indicators if several matched', async () => {
|
it('enriches signals with multiple indicators if several matched', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
query: 'source.port: 57324', // narrow our query to a single record that matches two indicatorsthreat_query: 'threat.indicator.ip: *',
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: 'source.port: 57324', // narrow our query to a single record that matches two indicators
|
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: 'threat.indicator.ip: *',
|
threat_query: 'threat.indicator.ip: *',
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
|
@ -1136,14 +1143,36 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
const matchRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
|
query: 'source.port: 57324', // narrow our query to a single record that matches two indicatorsthreat_query: 'threat.indicator.ip: *',
|
||||||
|
threat_query: 'threat.indicator.ip: *',
|
||||||
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
|
threat_mapping: [
|
||||||
|
{
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.ip',
|
||||||
|
field: 'source.ip',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.ip',
|
||||||
|
field: 'source.ip',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId: termPrevieId } = await previewRule({ supertest, rule: termRule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const termPreviewAlerts = await getPreviewAlerts({ es, previewId: termPrevieId });
|
||||||
expect(previewAlerts.length).equal(1);
|
const { previewId: matchPreviewId } = await previewRule({ supertest, rule: matchRule });
|
||||||
|
const matchPrevieAlerts = await getPreviewAlerts({ es, previewId: matchPreviewId });
|
||||||
|
expect(termPreviewAlerts.length).equal(1);
|
||||||
|
|
||||||
const [threat] = previewAlerts.map((hit) => hit._source?.threat) as Array<{
|
const [threat] = termPreviewAlerts.map((hit) => hit._source?.threat) as Array<{
|
||||||
enrichments: unknown[];
|
enrichments: unknown[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
@ -1185,21 +1214,12 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
alertsAreTheSame(termPreviewAlerts, matchPrevieAlerts);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds a single indicator that matched multiple fields', async () => {
|
it('adds a single indicator that matched multiple fields', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: 'source.port: 57324', // narrow our query to a single record that matches two indicators
|
query: 'source.port: 57324', // narrow our query to a single record that matches two indicators
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: 'threat.indicator.ip: *',
|
threat_query: 'threat.indicator.ip: *',
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
|
@ -1222,18 +1242,57 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
const matchRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
|
query: 'source.port: 57324', // narrow our query to a single record that matches two indicators
|
||||||
|
threat_query: 'threat.indicator.ip: *',
|
||||||
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
|
threat_mapping: [
|
||||||
|
{
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.port',
|
||||||
|
field: 'source.port',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.port',
|
||||||
|
field: 'source.port',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.ip',
|
||||||
|
field: 'source.ip',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'threat.indicator.ip',
|
||||||
|
field: 'source.ip',
|
||||||
|
type: 'mapping',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId: termPrevieId } = await previewRule({ supertest, rule: termRule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const termPreviewAlerts = await getPreviewAlerts({ es, previewId: termPrevieId });
|
||||||
expect(previewAlerts.length).equal(1);
|
const { previewId: matchPreviewId } = await previewRule({ supertest, rule: matchRule });
|
||||||
|
const matchPrevieAlerts = await getPreviewAlerts({ es, previewId: matchPreviewId });
|
||||||
|
expect(termPreviewAlerts.length).equal(1);
|
||||||
|
|
||||||
const [threat] = previewAlerts.map((hit) => hit._source?.threat) as Array<{
|
const [threatTerm] = termPreviewAlerts.map((hit) => hit._source?.threat) as Array<{
|
||||||
enrichments: unknown[];
|
enrichments: unknown[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
assertContains(threat.enrichments, [
|
const [threatMatch] = matchPrevieAlerts.map((hit) => hit._source?.threat) as Array<{
|
||||||
|
enrichments: unknown[];
|
||||||
|
}>;
|
||||||
|
assertContains(threatTerm.enrichments, [
|
||||||
{
|
{
|
||||||
feed: {},
|
feed: {},
|
||||||
indicator: {
|
indicator: {
|
||||||
|
@ -1294,22 +1353,23 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
const sortEnrichments = (a: any, b: any) => {
|
||||||
|
const atomicA = a.matched.atomic.toString();
|
||||||
|
const atomicB = b.matched.atomic.toString();
|
||||||
|
if (atomicA === atomicB) {
|
||||||
|
return a.indicator.description > b.indicator.description ? 1 : -1;
|
||||||
|
}
|
||||||
|
return atomicA > atomicB ? 1 : -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(threatTerm.enrichments.sort(sortEnrichments)).to.be.eql(
|
||||||
|
threatMatch.enrichments.sort(sortEnrichments)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates multiple signals with multiple matches', async () => {
|
it('generates multiple signals with multiple matches', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
threat_language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '(source.port:57324 and source.ip:45.115.45.3) or destination.ip:159.89.119.67', // narrow our query to a single record that matches two indicators
|
query: '(source.port:57324 and source.ip:45.115.45.3) or destination.ip:159.89.119.67', // narrow our query to a single record that matches two indicators
|
||||||
threat_indicator_path: 'threat.indicator',
|
|
||||||
threat_query: '*:*',
|
threat_query: '*:*',
|
||||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
|
@ -1337,8 +1397,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||||
|
@ -1444,19 +1503,9 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be enriched with host risk score', async () => {
|
it('should be enriched with host risk score', async () => {
|
||||||
const rule: ThreatMatchRuleCreateProps = {
|
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||||
description: 'Detecting root and admin users',
|
|
||||||
name: 'Query with a rule id',
|
|
||||||
severity: 'high',
|
|
||||||
index: ['auditbeat-*'],
|
|
||||||
type: 'threat_match',
|
|
||||||
risk_score: 55,
|
|
||||||
language: 'kuery',
|
|
||||||
rule_id: 'rule-1',
|
|
||||||
from: '1900-01-01T00:00:00.000Z',
|
|
||||||
query: '*:*',
|
query: '*:*',
|
||||||
threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
|
||||||
threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
|
|
||||||
threat_mapping: [
|
threat_mapping: [
|
||||||
// We match host.name against host.name
|
// We match host.name against host.name
|
||||||
{
|
{
|
||||||
|
@ -1469,8 +1518,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
threat_filters: [],
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { previewId } = await previewRule({ supertest, rule });
|
const { previewId } = await previewRule({ supertest, rule });
|
||||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: 100 });
|
const previewAlerts = await getPreviewAlerts({ es, previewId, size: 100 });
|
||||||
|
|
|
@ -117,5 +117,6 @@
|
||||||
"@kbn/security-api-integration-helpers",
|
"@kbn/security-api-integration-helpers",
|
||||||
"@kbn/alerts-as-data-utils",
|
"@kbn/alerts-as-data-utils",
|
||||||
"@kbn/discover-plugin",
|
"@kbn/discover-plugin",
|
||||||
|
"@kbn/securitysolution-io-ts-alerting-types",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue