Skip to content

Commit 7cee394

Browse files
e40pudakowalska622
authored andcommitted
[Attack Discovery][Scheduling] Telemetry (elastic#12008) (elastic#220998)
## Summary Main ticket ([Internal link](elastic/security-team#12008)) These changes add attack discovery schedules telemetry similarly to existing manually generated attack discoveries added in this commit elastic@3c41972. ## NOTES The feature is hidden behind the feature flag (in `kibana.dev.yml`): ``` feature_flags.overrides: securitySolution.assistantAttackDiscoverySchedulingEnabled: true ```
1 parent 0cc0751 commit 7cee394

File tree

12 files changed

+920
-461
lines changed

12 files changed

+920
-461
lines changed

x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/persistence/transforms/transform_to_alert_documents/index.test.ts

Lines changed: 304 additions & 188 deletions
Large diffs are not rendered by default.

x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/persistence/transforms/transform_to_alert_documents/index.ts

Lines changed: 110 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
*/
77

88
import { EcsVersion } from '@elastic/ecs';
9+
import { Alert } from '@kbn/alerts-as-data-utils';
910
import { AuthenticatedUser } from '@kbn/core/server';
1011
import {
1112
ATTACK_DISCOVERY_AD_HOC_RULE_ID,
1213
ATTACK_DISCOVERY_AD_HOC_RULE_TYPE_ID,
1314
type CreateAttackDiscoveryAlertsParams,
1415
replaceAnonymizedValuesWithOriginalValues,
16+
AttackDiscovery,
1517
} from '@kbn/elastic-assistant-common';
1618
import {
1719
ALERT_INSTANCE_ID,
@@ -55,6 +57,80 @@ import {
5557
} from '../../../schedules/fields/field_names';
5658
import { AttackDiscoveryAlertDocument } from '../../../schedules/types';
5759

60+
type AttackDiscoveryAlertDocumentBase = Omit<AttackDiscoveryAlertDocument, keyof Alert>;
61+
62+
export const transformToBaseAlertDocument = ({
63+
attackDiscovery,
64+
alertsParams,
65+
}: {
66+
attackDiscovery: AttackDiscovery;
67+
alertsParams: Omit<CreateAttackDiscoveryAlertsParams, 'attackDiscoveries' | 'generationUuid'>;
68+
}): AttackDiscoveryAlertDocumentBase => {
69+
const { alertsContextCount, anonymizedAlerts, apiConfig, connectorName, replacements } =
70+
alertsParams;
71+
72+
const {
73+
alertIds,
74+
entitySummaryMarkdown,
75+
detailsMarkdown,
76+
mitreAttackTactics,
77+
summaryMarkdown,
78+
title,
79+
} = attackDiscovery;
80+
81+
return {
82+
// Alert base fields
83+
[ECS_VERSION]: EcsVersion,
84+
[ALERT_RISK_SCORE]: getAlertRiskScore({
85+
alertIds,
86+
anonymizedAlerts,
87+
}),
88+
89+
// Attack discovery fields
90+
[ALERT_ATTACK_DISCOVERY_ALERT_IDS]: alertIds,
91+
[ALERT_ATTACK_DISCOVERY_ALERTS_CONTEXT_COUNT]: alertsContextCount,
92+
[ALERT_ATTACK_DISCOVERY_API_CONFIG]: {
93+
action_type_id: apiConfig.actionTypeId,
94+
connector_id: apiConfig.connectorId,
95+
model: apiConfig.model,
96+
name: connectorName,
97+
provider: apiConfig.provider,
98+
},
99+
[ALERT_ATTACK_DISCOVERY_DETAILS_MARKDOWN]: detailsMarkdown,
100+
[ALERT_ATTACK_DISCOVERY_DETAILS_MARKDOWN_WITH_REPLACEMENTS]:
101+
replaceAnonymizedValuesWithOriginalValues({
102+
messageContent: detailsMarkdown,
103+
replacements,
104+
}),
105+
[ALERT_ATTACK_DISCOVERY_ENTITY_SUMMARY_MARKDOWN]: entitySummaryMarkdown,
106+
[ALERT_ATTACK_DISCOVERY_ENTITY_SUMMARY_MARKDOWN_WITH_REPLACEMENTS]:
107+
entitySummaryMarkdown != null
108+
? replaceAnonymizedValuesWithOriginalValues({
109+
messageContent: entitySummaryMarkdown,
110+
replacements,
111+
})
112+
: undefined,
113+
[ALERT_ATTACK_DISCOVERY_MITRE_ATTACK_TACTICS]: mitreAttackTactics,
114+
[ALERT_ATTACK_DISCOVERY_REPLACEMENTS]: !isEmpty(replacements)
115+
? Object.entries(replacements).map(([uuid, value]) => ({
116+
uuid,
117+
value,
118+
}))
119+
: undefined,
120+
[ALERT_ATTACK_DISCOVERY_SUMMARY_MARKDOWN]: summaryMarkdown,
121+
[ALERT_ATTACK_DISCOVERY_SUMMARY_MARKDOWN_WITH_REPLACEMENTS]:
122+
replaceAnonymizedValuesWithOriginalValues({
123+
messageContent: summaryMarkdown,
124+
replacements,
125+
}),
126+
[ALERT_ATTACK_DISCOVERY_TITLE]: title,
127+
[ALERT_ATTACK_DISCOVERY_TITLE_WITH_REPLACEMENTS]: replaceAnonymizedValuesWithOriginalValues({
128+
messageContent: title,
129+
replacements,
130+
}),
131+
};
132+
};
133+
58134
export const transformToAlertDocuments = ({
59135
authenticatedUser,
60136
createAttackDiscoveryAlertsParams,
@@ -66,102 +142,42 @@ export const transformToAlertDocuments = ({
66142
now: Date;
67143
spaceId: string;
68144
}): AttackDiscoveryAlertDocument[] => {
69-
const {
70-
alertsContextCount,
71-
anonymizedAlerts,
72-
apiConfig,
73-
attackDiscoveries,
74-
connectorName,
75-
generationUuid,
76-
replacements,
77-
} = createAttackDiscoveryAlertsParams;
145+
const { attackDiscoveries, generationUuid, ...restParams } = createAttackDiscoveryAlertsParams;
78146

79-
const replacementsOrEmpty = replacements ?? {};
147+
return attackDiscoveries.map((attackDiscovery) => {
148+
const alertUuid = uuidv4();
80149

81-
return attackDiscoveries.map(
82-
({
83-
alertIds,
84-
entitySummaryMarkdown,
85-
detailsMarkdown,
86-
mitreAttackTactics,
87-
summaryMarkdown,
88-
title,
89-
}) => {
90-
const alertUuid = uuidv4();
150+
const baseAlertDocument = transformToBaseAlertDocument({
151+
attackDiscovery,
152+
alertsParams: restParams,
153+
});
154+
155+
return {
156+
...baseAlertDocument,
91157

92-
return {
93-
'@timestamp': now.toISOString(),
94-
[ALERT_ATTACK_DISCOVERY_ALERT_IDS]: alertIds,
95-
[ALERT_ATTACK_DISCOVERY_ALERTS_CONTEXT_COUNT]: alertsContextCount,
96-
[ALERT_ATTACK_DISCOVERY_API_CONFIG]: {
97-
action_type_id: apiConfig.actionTypeId,
98-
connector_id: apiConfig.connectorId,
99-
model: apiConfig.model,
100-
name: connectorName,
101-
provider: apiConfig.provider,
158+
'@timestamp': now.toISOString(),
159+
[ALERT_ATTACK_DISCOVERY_USER_ID]: authenticatedUser.profile_uid,
160+
[ALERT_ATTACK_DISCOVERY_USER_NAME]: authenticatedUser.username,
161+
[ALERT_ATTACK_DISCOVERY_USERS]: [
162+
{
163+
id: authenticatedUser.profile_uid,
164+
name: authenticatedUser.username,
102165
},
103-
[ALERT_ATTACK_DISCOVERY_DETAILS_MARKDOWN]: detailsMarkdown,
104-
[ALERT_ATTACK_DISCOVERY_DETAILS_MARKDOWN_WITH_REPLACEMENTS]:
105-
replaceAnonymizedValuesWithOriginalValues({
106-
messageContent: detailsMarkdown,
107-
replacements: replacementsOrEmpty,
108-
}),
109-
[ALERT_ATTACK_DISCOVERY_ENTITY_SUMMARY_MARKDOWN]: entitySummaryMarkdown,
110-
[ALERT_ATTACK_DISCOVERY_ENTITY_SUMMARY_MARKDOWN_WITH_REPLACEMENTS]:
111-
entitySummaryMarkdown != null
112-
? replaceAnonymizedValuesWithOriginalValues({
113-
messageContent: entitySummaryMarkdown,
114-
replacements: replacementsOrEmpty,
115-
})
116-
: undefined,
117-
[ALERT_ATTACK_DISCOVERY_MITRE_ATTACK_TACTICS]: mitreAttackTactics,
118-
[ALERT_ATTACK_DISCOVERY_REPLACEMENTS]: !isEmpty(replacementsOrEmpty)
119-
? Object.entries(replacementsOrEmpty).map(([uuid, value]) => ({
120-
uuid,
121-
value,
122-
}))
123-
: undefined,
124-
[ALERT_ATTACK_DISCOVERY_SUMMARY_MARKDOWN]: summaryMarkdown,
125-
[ALERT_ATTACK_DISCOVERY_SUMMARY_MARKDOWN_WITH_REPLACEMENTS]:
126-
replaceAnonymizedValuesWithOriginalValues({
127-
messageContent: summaryMarkdown,
128-
replacements: replacementsOrEmpty,
129-
}),
130-
[ALERT_ATTACK_DISCOVERY_TITLE]: title,
131-
[ALERT_ATTACK_DISCOVERY_TITLE_WITH_REPLACEMENTS]: replaceAnonymizedValuesWithOriginalValues(
132-
{
133-
messageContent: title,
134-
replacements: replacementsOrEmpty,
135-
}
136-
),
137-
[ALERT_ATTACK_DISCOVERY_USER_ID]: authenticatedUser.profile_uid,
138-
[ALERT_ATTACK_DISCOVERY_USER_NAME]: authenticatedUser.username,
139-
[ALERT_ATTACK_DISCOVERY_USERS]: [
140-
{
141-
id: authenticatedUser.profile_uid,
142-
name: authenticatedUser.username,
143-
},
144-
],
145-
[ALERT_RULE_EXECUTION_UUID]: generationUuid,
146-
[ALERT_INSTANCE_ID]: alertUuid,
147-
[ALERT_RISK_SCORE]: getAlertRiskScore({
148-
alertIds,
149-
anonymizedAlerts,
150-
}),
151-
[ALERT_RULE_CATEGORY]: 'Attack discovery ad hoc (placeholder rule category)',
152-
[ALERT_RULE_CONSUMER]: 'siem',
153-
[ALERT_RULE_NAME]: 'Attack discovery ad hoc (placeholder rule name)',
154-
[ALERT_RULE_PRODUCER]: 'siem',
155-
[ALERT_RULE_REVISION]: 1,
156-
[ALERT_RULE_TYPE_ID]: ATTACK_DISCOVERY_AD_HOC_RULE_TYPE_ID, // sentinel value
157-
[ALERT_RULE_UUID]: ATTACK_DISCOVERY_AD_HOC_RULE_ID, // sentinel value
158-
[ALERT_STATUS]: 'active',
159-
[ALERT_UUID]: alertUuid, // IMPORTANT: the document _id should be the same as this field when it's bulk inserted
160-
[ALERT_WORKFLOW_STATUS]: 'open',
161-
[ECS_VERSION]: EcsVersion,
162-
[EVENT_KIND]: 'signal',
163-
[SPACE_IDS]: [spaceId],
164-
};
165-
}
166-
);
166+
],
167+
[ALERT_RULE_EXECUTION_UUID]: generationUuid,
168+
[ALERT_INSTANCE_ID]: alertUuid,
169+
[ALERT_RULE_CATEGORY]: 'Attack discovery ad hoc (placeholder rule category)',
170+
[ALERT_RULE_CONSUMER]: 'siem',
171+
[ALERT_RULE_NAME]: 'Attack discovery ad hoc (placeholder rule name)',
172+
[ALERT_RULE_PRODUCER]: 'siem',
173+
[ALERT_RULE_REVISION]: 1,
174+
[ALERT_RULE_TYPE_ID]: ATTACK_DISCOVERY_AD_HOC_RULE_TYPE_ID, // sentinel value
175+
[ALERT_RULE_UUID]: ATTACK_DISCOVERY_AD_HOC_RULE_ID, // sentinel value
176+
[ALERT_STATUS]: 'active',
177+
[ALERT_UUID]: alertUuid, // IMPORTANT: the document _id should be the same as this field when it's bulk inserted
178+
[ALERT_WORKFLOW_STATUS]: 'open',
179+
[EVENT_KIND]: 'signal',
180+
[SPACE_IDS]: [spaceId],
181+
};
182+
});
167183
};

x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/schedules/register_schedule/definition.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import { loggerMock } from '@kbn/logging-mocks';
9+
import { analyticsServiceMock } from '@kbn/core/server/mocks';
910
import {
1011
ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
1112
AttackDiscoveryScheduleParams,
@@ -16,13 +17,17 @@ import { ATTACK_DISCOVERY_ALERTS_AAD_CONFIG } from '../constants';
1617

1718
describe('getAttackDiscoveryScheduleType', () => {
1819
const mockLogger = loggerMock.create();
20+
const mockTelemetry = analyticsServiceMock.createAnalyticsServiceSetup();
1921

2022
beforeEach(() => {
2123
jest.clearAllMocks();
2224
});
2325

2426
it('should return schedule type definition', async () => {
25-
const scheduleType = getAttackDiscoveryScheduleType({ logger: mockLogger });
27+
const scheduleType = getAttackDiscoveryScheduleType({
28+
logger: mockLogger,
29+
telemetry: mockTelemetry,
30+
});
2631

2732
expect(scheduleType).toEqual({
2833
id: ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,

x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/schedules/register_schedule/definition.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77

8-
import { DEFAULT_APP_CATEGORIES, Logger } from '@kbn/core/server';
8+
import { AnalyticsServiceSetup, DEFAULT_APP_CATEGORIES, Logger } from '@kbn/core/server';
99
import {
1010
ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
1111
AttackDiscoveryScheduleParams,
@@ -17,10 +17,12 @@ import { attackDiscoveryScheduleExecutor } from './executor';
1717

1818
export interface GetAttackDiscoveryScheduleParams {
1919
logger: Logger;
20+
telemetry: AnalyticsServiceSetup;
2021
}
2122

2223
export const getAttackDiscoveryScheduleType = ({
2324
logger,
25+
telemetry,
2426
}: GetAttackDiscoveryScheduleParams): AttackDiscoveryScheduleType => {
2527
return {
2628
id: ATTACK_DISCOVERY_SCHEDULES_ALERT_TYPE_ID,
@@ -52,6 +54,7 @@ export const getAttackDiscoveryScheduleType = ({
5254
return attackDiscoveryScheduleExecutor({
5355
options,
5456
logger,
57+
telemetry,
5558
});
5659
},
5760
};

0 commit comments

Comments
 (0)