Skip to content

Commit 359ef50

Browse files
e40pudkibanamachine
authored andcommitted
[Rules migration] Add rules migrations update route (elastic#11209) (elastic#200815)
## Summary Changes in this PR: * Added `update` route to handle bulk rule migrations docs updates * Exposed `id` field in `RuleMigration` object needed for ES bulk update operation * Updated SIEM migrations schemas to use `NonEmptyString` when it is needed ## Testing locally Enable the flag ``` xpack.securitySolution.enableExperimental: ['siemMigrationsEnabled'] ``` Create and start a rule migration. Then use `update` API to updated corresponding docs. cURL request examples: <details> <summary>Rules migration `create` POST request</summary> ``` curl --location --request POST 'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules' \ --header 'kbn-xsrf;' \ --header 'x-elastic-internal-origin: security-solution' \ --header 'elastic-api-version: 1' \ --header 'Content-Type: application/json' \ --data '[ { "id": "f8c325ea-506e-4105-8ccf-da1492e90115", "vendor": "splunk", "title": "Linux Auditd Add User Account Type", "description": "The following analytic detects the suspicious add user account type. This behavior is critical for a SOC to monitor because it may indicate attempts to gain unauthorized access or maintain control over a system. Such actions could be signs of malicious activity. If confirmed, this could lead to serious consequences, including a compromised system, unauthorized access to sensitive data, or even a wider breach affecting the entire network. Detecting and responding to these signs early is essential to prevent potential security incidents.", "query": "sourcetype=\"linux:audit\" type=ADD_USER \n| rename hostname as dest \n| stats count min(_time) as firstTime max(_time) as lastTime by exe pid dest res UID type \n| `security_content_ctime(firstTime)` \n| `security_content_ctime(lastTime)`\n| search *", "query_language":"spl", "mitre_attack_ids": [ "T1136" ] }, { "id": "7b87c556-0ca4-47e0-b84c-6cd62a0a3e90", "vendor": "splunk", "title": "Linux Auditd Change File Owner To Root", "description": "The following analytic detects the use of the '\''chown'\'' command to change a file owner to '\''root'\'' on a Linux system. It leverages Linux Auditd telemetry, specifically monitoring command-line executions and process details. This activity is significant as it may indicate an attempt to escalate privileges by adversaries, malware, or red teamers. If confirmed malicious, this action could allow an attacker to gain root-level access, leading to full control over the compromised host and potential persistence within the environment.", "query": "`linux_auditd` `linux_auditd_normalized_proctitle_process`\r\n| rename host as dest \r\n| where LIKE (process_exec, \"%chown %root%\") \r\n| stats count min(_time) as firstTime max(_time) as lastTime by process_exec proctitle normalized_proctitle_delimiter dest \r\n| `security_content_ctime(firstTime)` \r\n| `security_content_ctime(lastTime)`\r\n| `linux_auditd_change_file_owner_to_root_filter`", "query_language": "spl", "mitre_attack_ids": [ "T1222" ] } ]' ``` </details> <details> <summary>Rules migration `start` task request</summary> - Assuming the connector `azureOpenAiGPT4o` is already created in the local environment. - Using the {{`migration_id`}} from the first POST request response ``` curl --location --request PUT 'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}/start' \ --header 'kbn-xsrf;' \ --header 'x-elastic-internal-origin: security-solution' \ --header 'elastic-api-version: 1' \ --header 'Content-Type: application/json' \ --data '{ "connectorId": "azureOpenAiGPT4o" }' ``` </details> <details> <summary>Rules migration rules documents request</summary> - Using the {{`migration_id`}} from the first POST request response. ``` curl --location --request GET 'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}' \ --header 'kbn-xsrf;' \ --header 'x-elastic-internal-origin: security-solution' \ --header 'elastic-api-version: 1' ``` </details> <details> <summary>Rules migration `update` PUT request</summary> - Using the {{`rule_migration_id_1`}} and {{`rule_migration_id_2`}} from previous GET request response ``` curl --location --request PUT 'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules' \ --header 'kbn-xsrf;' \ --header 'x-elastic-internal-origin: security-solution' \ --header 'elastic-api-version: 1' --data '[ { "comments": [ "## Migration Summary\n- The `FROM` command is used to select the `logs-*` index pattern.\n- The `RENAME` command is used to rename the `host` field to `dest`.\n- The `WHERE` command filters the rows where `process_exec` contains the pattern `*chown *root*`.\n- The `STATS` command is used to aggregate the data, counting the number of occurrences and finding the minimum and maximum timestamps, grouped by `process_exec`, `proctitle`, `normalized_proctitle_delimiter`, and `dest`.\n- The macros `security_content_ctime` and `linux_auditd_change_file_owner_to_root_filter` are placeholders for the corresponding Splunk macros.", "Additional comment 2.0" ], "translation_result": "full", "id": "{{rule_migration_id_1}}" }, { "created_by": "elastic2.0", "elastic_rule": { "severity": "high", "title": "Linux Auditd Change File Owner To Root (UPDATED)" }, "id": "{{rule_migration_id_2}}" } ]' ``` </details> --------- Co-authored-by: kibanamachine <[email protected]>
1 parent d8beffb commit 359ef50

File tree

17 files changed

+408
-85
lines changed

17 files changed

+408
-85
lines changed

x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ import type {
371371
StartRuleMigrationResponse,
372372
StopRuleMigrationRequestParamsInput,
373373
StopRuleMigrationResponse,
374+
UpdateRuleMigrationRequestBodyInput,
375+
UpdateRuleMigrationResponse,
374376
UpsertRuleMigrationResourcesRequestParamsInput,
375377
UpsertRuleMigrationResourcesRequestBodyInput,
376378
UpsertRuleMigrationResourcesResponse,
@@ -2099,6 +2101,22 @@ detection engine rules.
20992101
})
21002102
.catch(catchAxiosErrorFormatAndThrow);
21012103
}
2104+
/**
2105+
* Updates rules migrations attributes
2106+
*/
2107+
async updateRuleMigration(props: UpdateRuleMigrationProps) {
2108+
this.log.info(`${new Date().toISOString()} Calling API UpdateRuleMigration`);
2109+
return this.kbnClient
2110+
.request<UpdateRuleMigrationResponse>({
2111+
path: '/internal/siem_migrations/rules',
2112+
headers: {
2113+
[ELASTIC_HTTP_VERSION_HEADER]: '1',
2114+
},
2115+
method: 'PUT',
2116+
body: props.body,
2117+
})
2118+
.catch(catchAxiosErrorFormatAndThrow);
2119+
}
21022120
async uploadAssetCriticalityRecords(props: UploadAssetCriticalityRecordsProps) {
21032121
this.log.info(`${new Date().toISOString()} Calling API UploadAssetCriticalityRecords`);
21042122
return this.kbnClient
@@ -2401,6 +2419,9 @@ export interface TriggerRiskScoreCalculationProps {
24012419
export interface UpdateRuleProps {
24022420
body: UpdateRuleRequestBodyInput;
24032421
}
2422+
export interface UpdateRuleMigrationProps {
2423+
body: UpdateRuleMigrationRequestBodyInput;
2424+
}
24042425
export interface UploadAssetCriticalityRecordsProps {
24052426
attachment: FormData;
24062427
}

x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ import { ArrayFromString } from '@kbn/zod-helpers';
1919

2020
import {
2121
OriginalRule,
22+
ElasticRulePartial,
23+
RuleMigrationTranslationResult,
24+
RuleMigrationComments,
2225
RuleMigrationAllTaskStats,
2326
RuleMigration,
2427
RuleMigrationTaskStats,
2528
RuleMigrationResourceData,
2629
RuleMigrationResourceType,
2730
RuleMigrationResource,
2831
} from '../../rule_migration.gen';
29-
import { ConnectorId, LangSmithOptions } from '../common.gen';
32+
import { NonEmptyString, ConnectorId, LangSmithOptions } from '../../common.gen';
3033

3134
export type CreateRuleMigrationRequestBody = z.infer<typeof CreateRuleMigrationRequestBody>;
3235
export const CreateRuleMigrationRequestBody = z.array(OriginalRule);
@@ -37,15 +40,15 @@ export const CreateRuleMigrationResponse = z.object({
3740
/**
3841
* The migration id created.
3942
*/
40-
migration_id: z.string(),
43+
migration_id: NonEmptyString,
4144
});
4245

4346
export type GetAllStatsRuleMigrationResponse = z.infer<typeof GetAllStatsRuleMigrationResponse>;
4447
export const GetAllStatsRuleMigrationResponse = RuleMigrationAllTaskStats;
4548

4649
export type GetRuleMigrationRequestParams = z.infer<typeof GetRuleMigrationRequestParams>;
4750
export const GetRuleMigrationRequestParams = z.object({
48-
migration_id: z.string(),
51+
migration_id: NonEmptyString,
4952
});
5053
export type GetRuleMigrationRequestParamsInput = z.input<typeof GetRuleMigrationRequestParams>;
5154

@@ -66,7 +69,7 @@ export type GetRuleMigrationResourcesRequestParams = z.infer<
6669
typeof GetRuleMigrationResourcesRequestParams
6770
>;
6871
export const GetRuleMigrationResourcesRequestParams = z.object({
69-
migration_id: z.string(),
72+
migration_id: NonEmptyString,
7073
});
7174
export type GetRuleMigrationResourcesRequestParamsInput = z.input<
7275
typeof GetRuleMigrationResourcesRequestParams
@@ -77,7 +80,7 @@ export const GetRuleMigrationResourcesResponse = z.array(RuleMigrationResource);
7780

7881
export type GetRuleMigrationStatsRequestParams = z.infer<typeof GetRuleMigrationStatsRequestParams>;
7982
export const GetRuleMigrationStatsRequestParams = z.object({
80-
migration_id: z.string(),
83+
migration_id: NonEmptyString,
8184
});
8285
export type GetRuleMigrationStatsRequestParamsInput = z.input<
8386
typeof GetRuleMigrationStatsRequestParams
@@ -88,7 +91,7 @@ export const GetRuleMigrationStatsResponse = RuleMigrationTaskStats;
8891

8992
export type StartRuleMigrationRequestParams = z.infer<typeof StartRuleMigrationRequestParams>;
9093
export const StartRuleMigrationRequestParams = z.object({
91-
migration_id: z.string(),
94+
migration_id: NonEmptyString,
9295
});
9396
export type StartRuleMigrationRequestParamsInput = z.input<typeof StartRuleMigrationRequestParams>;
9497

@@ -109,7 +112,7 @@ export const StartRuleMigrationResponse = z.object({
109112

110113
export type StopRuleMigrationRequestParams = z.infer<typeof StopRuleMigrationRequestParams>;
111114
export const StopRuleMigrationRequestParams = z.object({
112-
migration_id: z.string(),
115+
migration_id: NonEmptyString,
113116
});
114117
export type StopRuleMigrationRequestParamsInput = z.input<typeof StopRuleMigrationRequestParams>;
115118

@@ -121,11 +124,42 @@ export const StopRuleMigrationResponse = z.object({
121124
stopped: z.boolean(),
122125
});
123126

127+
export type UpdateRuleMigrationRequestBody = z.infer<typeof UpdateRuleMigrationRequestBody>;
128+
export const UpdateRuleMigrationRequestBody = z.array(
129+
z.object({
130+
/**
131+
* The rule migration id
132+
*/
133+
id: NonEmptyString,
134+
/**
135+
* The migrated elastic rule attributes to update.
136+
*/
137+
elastic_rule: ElasticRulePartial.optional(),
138+
/**
139+
* The rule translation result.
140+
*/
141+
translation_result: RuleMigrationTranslationResult.optional(),
142+
/**
143+
* The comments for the migration including a summary from the LLM in markdown.
144+
*/
145+
comments: RuleMigrationComments.optional(),
146+
})
147+
);
148+
export type UpdateRuleMigrationRequestBodyInput = z.input<typeof UpdateRuleMigrationRequestBody>;
149+
150+
export type UpdateRuleMigrationResponse = z.infer<typeof UpdateRuleMigrationResponse>;
151+
export const UpdateRuleMigrationResponse = z.object({
152+
/**
153+
* Indicates rules migrations have been updated.
154+
*/
155+
updated: z.boolean(),
156+
});
157+
124158
export type UpsertRuleMigrationResourcesRequestParams = z.infer<
125159
typeof UpsertRuleMigrationResourcesRequestParams
126160
>;
127161
export const UpsertRuleMigrationResourcesRequestParams = z.object({
128-
migration_id: z.string(),
162+
migration_id: NonEmptyString,
129163
});
130164
export type UpsertRuleMigrationResourcesRequestParamsInput = z.input<
131165
typeof UpsertRuleMigrationResourcesRequestParams
@@ -134,7 +168,16 @@ export type UpsertRuleMigrationResourcesRequestParamsInput = z.input<
134168
export type UpsertRuleMigrationResourcesRequestBody = z.infer<
135169
typeof UpsertRuleMigrationResourcesRequestBody
136170
>;
137-
export const UpsertRuleMigrationResourcesRequestBody = z.array(RuleMigrationResourceData);
171+
export const UpsertRuleMigrationResourcesRequestBody = z.array(
172+
RuleMigrationResourceData.merge(
173+
z.object({
174+
/**
175+
* The rule resource migration id
176+
*/
177+
id: NonEmptyString,
178+
})
179+
)
180+
);
138181
export type UpsertRuleMigrationResourcesRequestBodyInput = z.input<
139182
typeof UpsertRuleMigrationResourcesRequestBody
140183
>;

x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ info:
33
title: SIEM Rules Migration API
44
version: '1'
55
paths:
6-
76
# Rule migrations APIs
87

98
/internal/siem_migrations/rules:
@@ -33,8 +32,52 @@ paths:
3332
- migration_id
3433
properties:
3534
migration_id:
36-
type: string
3735
description: The migration id created.
36+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
37+
38+
put:
39+
summary: Updates rules migrations
40+
operationId: UpdateRuleMigration
41+
x-codegen-enabled: true
42+
description: Updates rules migrations attributes
43+
tags:
44+
- SIEM Rule Migrations
45+
requestBody:
46+
required: true
47+
content:
48+
application/json:
49+
schema:
50+
type: array
51+
items:
52+
type: object
53+
required:
54+
- id
55+
properties:
56+
id:
57+
description: The rule migration id
58+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
59+
elastic_rule:
60+
description: The migrated elastic rule attributes to update.
61+
$ref: '../../rule_migration.schema.yaml#/components/schemas/ElasticRulePartial'
62+
translation_result:
63+
description: The rule translation result.
64+
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationTranslationResult'
65+
comments:
66+
description: The comments for the migration including a summary from the LLM in markdown.
67+
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationComments'
68+
responses:
69+
200:
70+
description: Indicates rules migrations have been updated correctly.
71+
content:
72+
application/json:
73+
schema:
74+
type: object
75+
required:
76+
- updated
77+
properties:
78+
updated:
79+
type: boolean
80+
description: Indicates rules migrations have been updated.
3881

3982
/internal/siem_migrations/rules/stats:
4083
get:
@@ -67,8 +110,8 @@ paths:
67110
in: path
68111
required: true
69112
schema:
70-
type: string
71113
description: The migration id to start
114+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
72115
responses:
73116
200:
74117
description: Indicates rule migration have been retrieved correctly.
@@ -94,8 +137,8 @@ paths:
94137
in: path
95138
required: true
96139
schema:
97-
type: string
98140
description: The migration id to start
141+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
99142
requestBody:
100143
required: true
101144
content:
@@ -106,9 +149,9 @@ paths:
106149
- connector_id
107150
properties:
108151
connector_id:
109-
$ref: '../common.schema.yaml#/components/schemas/ConnectorId'
152+
$ref: '../../common.schema.yaml#/components/schemas/ConnectorId'
110153
langsmith_options:
111-
$ref: '../common.schema.yaml#/components/schemas/LangSmithOptions'
154+
$ref: '../../common.schema.yaml#/components/schemas/LangSmithOptions'
112155
responses:
113156
200:
114157
description: Indicates the migration start request has been processed successfully.
@@ -138,8 +181,8 @@ paths:
138181
in: path
139182
required: true
140183
schema:
141-
type: string
142184
description: The migration id to start
185+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
143186
responses:
144187
200:
145188
description: Indicates the migration stats has been retrieved correctly.
@@ -163,8 +206,8 @@ paths:
163206
in: path
164207
required: true
165208
schema:
166-
type: string
167209
description: The migration id to stop
210+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
168211
responses:
169212
200:
170213
description: Indicates migration task stop has been processed successfully.
@@ -197,16 +240,24 @@ paths:
197240
in: path
198241
required: true
199242
schema:
200-
type: string
201243
description: The migration id to attach the resources
244+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
202245
requestBody:
203246
required: true
204247
content:
205248
application/json:
206249
schema:
207250
type: array
208251
items:
209-
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationResourceData'
252+
allOf:
253+
- $ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationResourceData'
254+
- type: object
255+
required:
256+
- id
257+
properties:
258+
id:
259+
description: The rule resource migration id
260+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
210261
responses:
211262
200:
212263
description: Indicates migration resources have been created or updated correctly.
@@ -234,8 +285,8 @@ paths:
234285
in: path
235286
required: true
236287
schema:
237-
type: string
238288
description: The migration id to attach the resources
289+
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
239290
- name: type
240291
in: query
241292
required: false

x-pack/plugins/security_solution/common/siem_migrations/model/api/common.gen.ts renamed to x-pack/plugins/security_solution/common/siem_migrations/model/common.gen.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,21 @@
1010
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
1111
*
1212
* info:
13-
* title: SIEM Rule Migrations API common components
13+
* title: SIEM Rule Migration common components
1414
* version: not applicable
1515
*/
1616

1717
import { z } from '@kbn/zod';
1818

19+
/**
20+
* A string that is not empty and does not contain only whitespace
21+
*/
22+
export type NonEmptyString = z.infer<typeof NonEmptyString>;
23+
export const NonEmptyString = z
24+
.string()
25+
.min(1)
26+
.regex(/^(?! *$).+$/);
27+
1928
/**
2029
* The GenAI connector id to use.
2130
*/
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
openapi: 3.0.3
22
info:
3-
title: SIEM Rule Migrations API common components
3+
title: SIEM Rule Migration common components
44
version: 'not applicable'
55
paths: {}
66
components:
77
x-codegen-enabled: true
88
schemas:
9+
NonEmptyString:
10+
type: string
11+
pattern: ^(?! *$).+$
12+
minLength: 1
13+
description: A string that is not empty and does not contain only whitespace
914
ConnectorId:
1015
type: string
1116
description: The GenAI connector id to use.

0 commit comments

Comments
 (0)