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:
Khristinin Nikita 2023-03-01 17:42:16 +01:00 committed by GitHub
parent 06c70d96c6
commit b956d91b10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 311 additions and 262 deletions

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { get, isEqual } from 'lodash';
import { get, isEqual, omit } from 'lodash';
import expect from '@kbn/expect';
import {
ALERT_REASON,
@ -19,6 +19,7 @@ import {
VERSION,
} from '@kbn/rule-data-utils';
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';
@ -32,6 +33,7 @@ import {
ALERT_ORIGINAL_EVENT_MODULE,
ALERT_ORIGINAL_TIME,
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/rule_monitoring';
import {
previewRule,
getOpenSignals,
@ -41,7 +43,6 @@ import {
createRule,
} from '../../utils';
import { FtrProviderContext } from '../../common/ftr_provider_context';
const format = (value: unknown): string => JSON.stringify(value, null, 2);
// 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
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
@ -66,6 +146,8 @@ export default ({ getService }: FtrProviderContext) => {
*/
describe('Threat match type rules', () => {
before(async () => {
// await deleteSignalsIndex(supertest, log);
// await deleteAllAlerts(supertest, log);
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
it('should be able to execute and get 10 signals when doing a specific query (terms query)', async () => {
const rule: ThreatMatchRuleCreateProps = {
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: [],
};
it('should be able to execute and get all signals when doing a specific query (terms query)', async () => {
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule();
const createdRule = await createRule(supertest, log, rule);
const alerts = await getOpenSignals(supertest, log, es, createdRule);
expect(alerts.hits.hits.length).equal(10);
const alerts = await getOpenSignals(
supertest,
log,
es,
createdRule,
RuleExecutionStatus.succeeded,
100
);
expect(alerts.hits.hits.length).equal(88);
const fullSource = alerts.hits.hits.find(
(signal) =>
(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 () => {
const rule: ThreatMatchRuleCreateProps = {
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
it('should be able to execute and get all signals when doing a specific query (match query)', async () => {
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
threat_mapping: [
// 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 alerts = await getOpenSignals(supertest, log, es, createdRule);
expect(alerts.hits.hits.length).equal(10);
const alerts = await getOpenSignals(
supertest,
log,
es,
createdRule,
RuleExecutionStatus.succeeded,
100
);
expect(alerts.hits.hits.length).equal(88);
const fullSource = alerts.hits.hits.find(
(signal) =>
(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 () => {
const rule: ThreatMatchRuleCreateProps = {
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
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
threat_mapping: [
// We match host.name against host.name
{
@ -470,8 +571,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
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 () => {
const rule: ThreatMatchRuleCreateProps = {
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
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
threat_mapping: [
{
entries: [
@ -508,8 +596,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
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 () => {
const rule: ThreatMatchRuleCreateProps = {
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
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
threat_mapping: [
{
entries: [
@ -546,8 +621,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId });
@ -557,35 +631,13 @@ export default ({ getService }: FtrProviderContext) => {
describe('timeout behavior', () => {
// 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 () => {
const rule: ThreatMatchRuleCreateProps = {
description: 'Detecting root and admin users',
name: 'Query with a short interval',
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: '*:*', // 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 rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
override: {
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 });
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 () => {
const rule: ThreatMatchRuleCreateProps = {
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
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
threat_mapping: [
{
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 previewAlerts = await getPreviewAlerts({ es, previewId });
@ -690,18 +730,8 @@ export default ({ getService }: FtrProviderContext) => {
});
it('enriches signals with multiple indicators if several matched', async () => {
const rule: ThreatMatchRuleCreateProps = {
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',
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
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_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
threat_mapping: [
@ -715,9 +745,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId });
expect(previewAlerts.length).equal(1);
@ -767,18 +795,8 @@ export default ({ getService }: FtrProviderContext) => {
});
it('adds a single indicator that matched multiple fields', async () => {
const rule: ThreatMatchRuleCreateProps = {
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',
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
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_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
threat_mapping: [
@ -801,8 +819,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId });
@ -876,19 +893,7 @@ export default ({ getService }: FtrProviderContext) => {
});
it('generates multiple signals with multiple matches', async () => {
const rule: ThreatMatchRuleCreateProps = {
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',
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
threat_query: '*:*',
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
threat_mapping: [
@ -916,8 +921,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId });
@ -1023,18 +1027,8 @@ export default ({ getService }: FtrProviderContext) => {
});
it('enriches signals with the single indicator that matched', async () => {
const rule: ThreatMatchRuleCreateProps = {
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',
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
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_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
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 previewAlerts = await getPreviewAlerts({ es, previewId });
expect(previewAlerts.length).equal(2);
const { previewId: termPrevieId } = await previewRule({ supertest, rule: termRule });
const termPreviewAlerts = await getPreviewAlerts({ es, previewId: termPrevieId });
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([
{
enrichments: [
@ -1108,21 +1124,12 @@ export default ({ getService }: FtrProviderContext) => {
],
},
]);
alertsAreTheSame(termPreviewAlerts, matchPrevieAlerts);
});
it('enriches signals with multiple indicators if several matched', async () => {
const rule: ThreatMatchRuleCreateProps = {
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
threat_indicator_path: 'threat.indicator',
const termRule: 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: [
@ -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 previewAlerts = await getPreviewAlerts({ es, previewId });
expect(previewAlerts.length).equal(1);
const { previewId: termPrevieId } = await previewRule({ supertest, rule: termRule });
const termPreviewAlerts = await getPreviewAlerts({ es, previewId: termPrevieId });
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[];
}>;
@ -1185,21 +1214,12 @@ export default ({ getService }: FtrProviderContext) => {
},
},
]);
alertsAreTheSame(termPreviewAlerts, matchPrevieAlerts);
});
it('adds a single indicator that matched multiple fields', async () => {
const rule: ThreatMatchRuleCreateProps = {
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',
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
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_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
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 previewAlerts = await getPreviewAlerts({ es, previewId });
expect(previewAlerts.length).equal(1);
const { previewId: termPrevieId } = await previewRule({ supertest, rule: termRule });
const termPreviewAlerts = await getPreviewAlerts({ es, previewId: termPrevieId });
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[];
}>;
assertContains(threat.enrichments, [
const [threatMatch] = matchPrevieAlerts.map((hit) => hit._source?.threat) as Array<{
enrichments: unknown[];
}>;
assertContains(threatTerm.enrichments, [
{
feed: {},
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 () => {
const rule: ThreatMatchRuleCreateProps = {
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',
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
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_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
threat_mapping: [
@ -1337,8 +1397,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId });
@ -1444,19 +1503,9 @@ export default ({ getService }: FtrProviderContext) => {
});
it('should be enriched with host risk score', async () => {
const rule: ThreatMatchRuleCreateProps = {
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',
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
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
{
@ -1469,8 +1518,7 @@ export default ({ getService }: FtrProviderContext) => {
],
},
],
threat_filters: [],
};
});
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId, size: 100 });

View file

@ -117,5 +117,6 @@
"@kbn/security-api-integration-helpers",
"@kbn/alerts-as-data-utils",
"@kbn/discover-plugin",
"@kbn/securitysolution-io-ts-alerting-types",
]
}