[8.x] [Rules migration] Add rules migrations update route (#11209) (#200815) (#200910)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Rules migration] Add rules migrations update route (#11209)
(#200815)](https://github.com/elastic/kibana/pull/200815)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Ievgen
Sorokopud","email":"ievgen.sorokopud@elastic.co"},"sourceCommit":{"committedDate":"2024-11-20T12:36:37Z","message":"[Rules
migration] Add rules migrations update route (#11209) (#200815)\n\n##
Summary\r\n\r\nChanges in this PR:\r\n* Added `update` route to handle
bulk rule migrations docs updates\r\n* Exposed `id` field in
`RuleMigration` object needed for ES bulk update\r\noperation\r\n*
Updated SIEM migrations schemas to use `NonEmptyString` when it
is\r\nneeded\r\n\r\n## Testing locally\r\n\r\nEnable the
flag\r\n```\r\nxpack.securitySolution.enableExperimental:
['siemMigrationsEnabled']\r\n```\r\n\r\nCreate and start a rule
migration. Then use `update` API to updated\r\ncorresponding
docs.\r\n\r\ncURL request examples:\r\n\r\n<details>\r\n <summary>Rules
migration `create` POST request</summary>\r\n\r\n```\r\ncurl --location
--request POST
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\\\r\n--header 'Content-Type: application/json' \\\r\n--data '[\r\n
{\r\n \"id\": \"f8c325ea-506e-4105-8ccf-da1492e90115\",\r\n \"vendor\":
\"splunk\",\r\n \"title\": \"Linux Auditd Add User Account Type\",\r\n
\"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.\",\r\n \"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 *\",\r\n
\"query_language\":\"spl\",\r\n \"mitre_attack_ids\": [\r\n
\"T1136\"\r\n ]\r\n },\r\n {\r\n \"id\":
\"7b87c556-0ca4-47e0-b84c-6cd62a0a3e90\",\r\n \"vendor\":
\"splunk\",\r\n \"title\": \"Linux Auditd Change File Owner To
Root\",\r\n \"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.\",\r\n \"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`\",\r\n
\"query_language\": \"spl\",\r\n \"mitre_attack_ids\": [\r\n
\"T1222\"\r\n ]\r\n }\r\n]'\r\n```\r\n</details>\r\n\r\n<details>\r\n
<summary>Rules migration `start` task request</summary>\r\n\r\n-
Assuming the connector `azureOpenAiGPT4o` is already created in
the\r\nlocal environment.\r\n- Using the {{`migration_id`}} from the
first POST request response\r\n\r\n```\r\ncurl --location --request PUT
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}/start'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\\\r\n--header 'Content-Type: application/json' \\\r\n--data '{\r\n
\"connectorId\":
\"azureOpenAiGPT4o\"\r\n}'\r\n```\r\n</details>\r\n\r\n<details>\r\n
<summary>Rules migration rules documents request</summary>\r\n\r\n-
Using the {{`migration_id`}} from the first POST request
response.\r\n\r\n```\r\ncurl --location --request GET
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\r\n```\r\n</details>\r\n\r\n<details>\r\n <summary>Rules migration
`update` PUT request</summary>\r\n\r\n- Using the
{{`rule_migration_id_1`}} and {{`rule_migration_id_2`}} from\r\nprevious
GET request response\r\n\r\n```\r\ncurl --location --request PUT
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1' \r\n--data
'[\r\n {\r\n \"comments\": [\r\n \"## 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.\",\r\n \"Additional comment 2.0\"\r\n
],\r\n \"translation_result\": \"full\",\r\n \"id\":
\"{{rule_migration_id_1}}\"\r\n },\r\n {\r\n \"created_by\":
\"elastic2.0\",\r\n \"elastic_rule\": {\r\n \"severity\": \"high\",\r\n
\"title\": \"Linux Auditd Change File Owner To Root (UPDATED)\"\r\n
},\r\n \"id\": \"{{rule_migration_id_2}}\"\r\n
}\r\n]'\r\n```\r\n</details>\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"f6ac2cf8603ca633070e719f69b4fcef45ea92cb","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Threat
Hunting","Team:
SecuritySolution","backport:prev-minor"],"number":200815,"url":"https://github.com/elastic/kibana/pull/200815","mergeCommit":{"message":"[Rules
migration] Add rules migrations update route (#11209) (#200815)\n\n##
Summary\r\n\r\nChanges in this PR:\r\n* Added `update` route to handle
bulk rule migrations docs updates\r\n* Exposed `id` field in
`RuleMigration` object needed for ES bulk update\r\noperation\r\n*
Updated SIEM migrations schemas to use `NonEmptyString` when it
is\r\nneeded\r\n\r\n## Testing locally\r\n\r\nEnable the
flag\r\n```\r\nxpack.securitySolution.enableExperimental:
['siemMigrationsEnabled']\r\n```\r\n\r\nCreate and start a rule
migration. Then use `update` API to updated\r\ncorresponding
docs.\r\n\r\ncURL request examples:\r\n\r\n<details>\r\n <summary>Rules
migration `create` POST request</summary>\r\n\r\n```\r\ncurl --location
--request POST
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\\\r\n--header 'Content-Type: application/json' \\\r\n--data '[\r\n
{\r\n \"id\": \"f8c325ea-506e-4105-8ccf-da1492e90115\",\r\n \"vendor\":
\"splunk\",\r\n \"title\": \"Linux Auditd Add User Account Type\",\r\n
\"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.\",\r\n \"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 *\",\r\n
\"query_language\":\"spl\",\r\n \"mitre_attack_ids\": [\r\n
\"T1136\"\r\n ]\r\n },\r\n {\r\n \"id\":
\"7b87c556-0ca4-47e0-b84c-6cd62a0a3e90\",\r\n \"vendor\":
\"splunk\",\r\n \"title\": \"Linux Auditd Change File Owner To
Root\",\r\n \"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.\",\r\n \"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`\",\r\n
\"query_language\": \"spl\",\r\n \"mitre_attack_ids\": [\r\n
\"T1222\"\r\n ]\r\n }\r\n]'\r\n```\r\n</details>\r\n\r\n<details>\r\n
<summary>Rules migration `start` task request</summary>\r\n\r\n-
Assuming the connector `azureOpenAiGPT4o` is already created in
the\r\nlocal environment.\r\n- Using the {{`migration_id`}} from the
first POST request response\r\n\r\n```\r\ncurl --location --request PUT
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}/start'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\\\r\n--header 'Content-Type: application/json' \\\r\n--data '{\r\n
\"connectorId\":
\"azureOpenAiGPT4o\"\r\n}'\r\n```\r\n</details>\r\n\r\n<details>\r\n
<summary>Rules migration rules documents request</summary>\r\n\r\n-
Using the {{`migration_id`}} from the first POST request
response.\r\n\r\n```\r\ncurl --location --request GET
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\r\n```\r\n</details>\r\n\r\n<details>\r\n <summary>Rules migration
`update` PUT request</summary>\r\n\r\n- Using the
{{`rule_migration_id_1`}} and {{`rule_migration_id_2`}} from\r\nprevious
GET request response\r\n\r\n```\r\ncurl --location --request PUT
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1' \r\n--data
'[\r\n {\r\n \"comments\": [\r\n \"## 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.\",\r\n \"Additional comment 2.0\"\r\n
],\r\n \"translation_result\": \"full\",\r\n \"id\":
\"{{rule_migration_id_1}}\"\r\n },\r\n {\r\n \"created_by\":
\"elastic2.0\",\r\n \"elastic_rule\": {\r\n \"severity\": \"high\",\r\n
\"title\": \"Linux Auditd Change File Owner To Root (UPDATED)\"\r\n
},\r\n \"id\": \"{{rule_migration_id_2}}\"\r\n
}\r\n]'\r\n```\r\n</details>\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"f6ac2cf8603ca633070e719f69b4fcef45ea92cb"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/200815","number":200815,"mergeCommit":{"message":"[Rules
migration] Add rules migrations update route (#11209) (#200815)\n\n##
Summary\r\n\r\nChanges in this PR:\r\n* Added `update` route to handle
bulk rule migrations docs updates\r\n* Exposed `id` field in
`RuleMigration` object needed for ES bulk update\r\noperation\r\n*
Updated SIEM migrations schemas to use `NonEmptyString` when it
is\r\nneeded\r\n\r\n## Testing locally\r\n\r\nEnable the
flag\r\n```\r\nxpack.securitySolution.enableExperimental:
['siemMigrationsEnabled']\r\n```\r\n\r\nCreate and start a rule
migration. Then use `update` API to updated\r\ncorresponding
docs.\r\n\r\ncURL request examples:\r\n\r\n<details>\r\n <summary>Rules
migration `create` POST request</summary>\r\n\r\n```\r\ncurl --location
--request POST
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\\\r\n--header 'Content-Type: application/json' \\\r\n--data '[\r\n
{\r\n \"id\": \"f8c325ea-506e-4105-8ccf-da1492e90115\",\r\n \"vendor\":
\"splunk\",\r\n \"title\": \"Linux Auditd Add User Account Type\",\r\n
\"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.\",\r\n \"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 *\",\r\n
\"query_language\":\"spl\",\r\n \"mitre_attack_ids\": [\r\n
\"T1136\"\r\n ]\r\n },\r\n {\r\n \"id\":
\"7b87c556-0ca4-47e0-b84c-6cd62a0a3e90\",\r\n \"vendor\":
\"splunk\",\r\n \"title\": \"Linux Auditd Change File Owner To
Root\",\r\n \"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.\",\r\n \"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`\",\r\n
\"query_language\": \"spl\",\r\n \"mitre_attack_ids\": [\r\n
\"T1222\"\r\n ]\r\n }\r\n]'\r\n```\r\n</details>\r\n\r\n<details>\r\n
<summary>Rules migration `start` task request</summary>\r\n\r\n-
Assuming the connector `azureOpenAiGPT4o` is already created in
the\r\nlocal environment.\r\n- Using the {{`migration_id`}} from the
first POST request response\r\n\r\n```\r\ncurl --location --request PUT
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}/start'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\\\r\n--header 'Content-Type: application/json' \\\r\n--data '{\r\n
\"connectorId\":
\"azureOpenAiGPT4o\"\r\n}'\r\n```\r\n</details>\r\n\r\n<details>\r\n
<summary>Rules migration rules documents request</summary>\r\n\r\n-
Using the {{`migration_id`}} from the first POST request
response.\r\n\r\n```\r\ncurl --location --request GET
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules/{{migration_id}}'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1'
\r\n```\r\n</details>\r\n\r\n<details>\r\n <summary>Rules migration
`update` PUT request</summary>\r\n\r\n- Using the
{{`rule_migration_id_1`}} and {{`rule_migration_id_2`}} from\r\nprevious
GET request response\r\n\r\n```\r\ncurl --location --request PUT
'http://elastic:changeme@localhost:5601/internal/siem_migrations/rules'
\\\r\n--header 'kbn-xsrf;' \\\r\n--header 'x-elastic-internal-origin:
security-solution' \\\r\n--header 'elastic-api-version: 1' \r\n--data
'[\r\n {\r\n \"comments\": [\r\n \"## 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.\",\r\n \"Additional comment 2.0\"\r\n
],\r\n \"translation_result\": \"full\",\r\n \"id\":
\"{{rule_migration_id_1}}\"\r\n },\r\n {\r\n \"created_by\":
\"elastic2.0\",\r\n \"elastic_rule\": {\r\n \"severity\": \"high\",\r\n
\"title\": \"Linux Auditd Change File Owner To Root (UPDATED)\"\r\n
},\r\n \"id\": \"{{rule_migration_id_2}}\"\r\n
}\r\n]'\r\n```\r\n</details>\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"f6ac2cf8603ca633070e719f69b4fcef45ea92cb"}}]}]
BACKPORT-->
This commit is contained in:
Ievgen Sorokopud 2024-11-20 15:40:07 +01:00 committed by GitHub
parent 784cbc8f8b
commit 33263b25c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 408 additions and 85 deletions

View file

@ -378,6 +378,8 @@ import type {
StartRuleMigrationResponse,
StopRuleMigrationRequestParamsInput,
StopRuleMigrationResponse,
UpdateRuleMigrationRequestBodyInput,
UpdateRuleMigrationResponse,
UpsertRuleMigrationResourcesRequestParamsInput,
UpsertRuleMigrationResourcesRequestBodyInput,
UpsertRuleMigrationResourcesResponse,
@ -2086,6 +2088,22 @@ detection engine rules.
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Updates rules migrations attributes
*/
async updateRuleMigration(props: UpdateRuleMigrationProps) {
this.log.info(`${new Date().toISOString()} Calling API UpdateRuleMigration`);
return this.kbnClient
.request<UpdateRuleMigrationResponse>({
path: '/internal/siem_migrations/rules',
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '1',
},
method: 'PUT',
body: props.body,
})
.catch(catchAxiosErrorFormatAndThrow);
}
async uploadAssetCriticalityRecords(props: UploadAssetCriticalityRecordsProps) {
this.log.info(`${new Date().toISOString()} Calling API UploadAssetCriticalityRecords`);
return this.kbnClient
@ -2394,6 +2412,9 @@ export interface TriggerRiskScoreCalculationProps {
export interface UpdateRuleProps {
body: UpdateRuleRequestBodyInput;
}
export interface UpdateRuleMigrationProps {
body: UpdateRuleMigrationRequestBodyInput;
}
export interface UploadAssetCriticalityRecordsProps {
attachment: FormData;
}

View file

@ -19,6 +19,9 @@ import { ArrayFromString } from '@kbn/zod-helpers';
import {
OriginalRule,
ElasticRulePartial,
RuleMigrationTranslationResult,
RuleMigrationComments,
RuleMigrationAllTaskStats,
RuleMigration,
RuleMigrationTaskStats,
@ -26,7 +29,7 @@ import {
RuleMigrationResourceType,
RuleMigrationResource,
} from '../../rule_migration.gen';
import { ConnectorId, LangSmithOptions } from '../common.gen';
import { NonEmptyString, ConnectorId, LangSmithOptions } from '../../common.gen';
export type CreateRuleMigrationRequestBody = z.infer<typeof CreateRuleMigrationRequestBody>;
export const CreateRuleMigrationRequestBody = z.array(OriginalRule);
@ -37,7 +40,7 @@ export const CreateRuleMigrationResponse = z.object({
/**
* The migration id created.
*/
migration_id: z.string(),
migration_id: NonEmptyString,
});
export type GetAllStatsRuleMigrationResponse = z.infer<typeof GetAllStatsRuleMigrationResponse>;
@ -45,7 +48,7 @@ export const GetAllStatsRuleMigrationResponse = RuleMigrationAllTaskStats;
export type GetRuleMigrationRequestParams = z.infer<typeof GetRuleMigrationRequestParams>;
export const GetRuleMigrationRequestParams = z.object({
migration_id: z.string(),
migration_id: NonEmptyString,
});
export type GetRuleMigrationRequestParamsInput = z.input<typeof GetRuleMigrationRequestParams>;
@ -66,7 +69,7 @@ export type GetRuleMigrationResourcesRequestParams = z.infer<
typeof GetRuleMigrationResourcesRequestParams
>;
export const GetRuleMigrationResourcesRequestParams = z.object({
migration_id: z.string(),
migration_id: NonEmptyString,
});
export type GetRuleMigrationResourcesRequestParamsInput = z.input<
typeof GetRuleMigrationResourcesRequestParams
@ -77,7 +80,7 @@ export const GetRuleMigrationResourcesResponse = z.array(RuleMigrationResource);
export type GetRuleMigrationStatsRequestParams = z.infer<typeof GetRuleMigrationStatsRequestParams>;
export const GetRuleMigrationStatsRequestParams = z.object({
migration_id: z.string(),
migration_id: NonEmptyString,
});
export type GetRuleMigrationStatsRequestParamsInput = z.input<
typeof GetRuleMigrationStatsRequestParams
@ -88,7 +91,7 @@ export const GetRuleMigrationStatsResponse = RuleMigrationTaskStats;
export type StartRuleMigrationRequestParams = z.infer<typeof StartRuleMigrationRequestParams>;
export const StartRuleMigrationRequestParams = z.object({
migration_id: z.string(),
migration_id: NonEmptyString,
});
export type StartRuleMigrationRequestParamsInput = z.input<typeof StartRuleMigrationRequestParams>;
@ -109,7 +112,7 @@ export const StartRuleMigrationResponse = z.object({
export type StopRuleMigrationRequestParams = z.infer<typeof StopRuleMigrationRequestParams>;
export const StopRuleMigrationRequestParams = z.object({
migration_id: z.string(),
migration_id: NonEmptyString,
});
export type StopRuleMigrationRequestParamsInput = z.input<typeof StopRuleMigrationRequestParams>;
@ -121,11 +124,42 @@ export const StopRuleMigrationResponse = z.object({
stopped: z.boolean(),
});
export type UpdateRuleMigrationRequestBody = z.infer<typeof UpdateRuleMigrationRequestBody>;
export const UpdateRuleMigrationRequestBody = z.array(
z.object({
/**
* The rule migration id
*/
id: NonEmptyString,
/**
* The migrated elastic rule attributes to update.
*/
elastic_rule: ElasticRulePartial.optional(),
/**
* The rule translation result.
*/
translation_result: RuleMigrationTranslationResult.optional(),
/**
* The comments for the migration including a summary from the LLM in markdown.
*/
comments: RuleMigrationComments.optional(),
})
);
export type UpdateRuleMigrationRequestBodyInput = z.input<typeof UpdateRuleMigrationRequestBody>;
export type UpdateRuleMigrationResponse = z.infer<typeof UpdateRuleMigrationResponse>;
export const UpdateRuleMigrationResponse = z.object({
/**
* Indicates rules migrations have been updated.
*/
updated: z.boolean(),
});
export type UpsertRuleMigrationResourcesRequestParams = z.infer<
typeof UpsertRuleMigrationResourcesRequestParams
>;
export const UpsertRuleMigrationResourcesRequestParams = z.object({
migration_id: z.string(),
migration_id: NonEmptyString,
});
export type UpsertRuleMigrationResourcesRequestParamsInput = z.input<
typeof UpsertRuleMigrationResourcesRequestParams
@ -134,7 +168,16 @@ export type UpsertRuleMigrationResourcesRequestParamsInput = z.input<
export type UpsertRuleMigrationResourcesRequestBody = z.infer<
typeof UpsertRuleMigrationResourcesRequestBody
>;
export const UpsertRuleMigrationResourcesRequestBody = z.array(RuleMigrationResourceData);
export const UpsertRuleMigrationResourcesRequestBody = z.array(
RuleMigrationResourceData.merge(
z.object({
/**
* The rule resource migration id
*/
id: NonEmptyString,
})
)
);
export type UpsertRuleMigrationResourcesRequestBodyInput = z.input<
typeof UpsertRuleMigrationResourcesRequestBody
>;

View file

@ -3,7 +3,6 @@ info:
title: SIEM Rules Migration API
version: '1'
paths:
# Rule migrations APIs
/internal/siem_migrations/rules:
@ -33,8 +32,52 @@ paths:
- migration_id
properties:
migration_id:
type: string
description: The migration id created.
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
put:
summary: Updates rules migrations
operationId: UpdateRuleMigration
x-codegen-enabled: true
description: Updates rules migrations attributes
tags:
- SIEM Rule Migrations
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
required:
- id
properties:
id:
description: The rule migration id
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
elastic_rule:
description: The migrated elastic rule attributes to update.
$ref: '../../rule_migration.schema.yaml#/components/schemas/ElasticRulePartial'
translation_result:
description: The rule translation result.
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationTranslationResult'
comments:
description: The comments for the migration including a summary from the LLM in markdown.
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationComments'
responses:
200:
description: Indicates rules migrations have been updated correctly.
content:
application/json:
schema:
type: object
required:
- updated
properties:
updated:
type: boolean
description: Indicates rules migrations have been updated.
/internal/siem_migrations/rules/stats:
get:
@ -67,8 +110,8 @@ paths:
in: path
required: true
schema:
type: string
description: The migration id to start
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
responses:
200:
description: Indicates rule migration have been retrieved correctly.
@ -94,8 +137,8 @@ paths:
in: path
required: true
schema:
type: string
description: The migration id to start
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
requestBody:
required: true
content:
@ -106,9 +149,9 @@ paths:
- connector_id
properties:
connector_id:
$ref: '../common.schema.yaml#/components/schemas/ConnectorId'
$ref: '../../common.schema.yaml#/components/schemas/ConnectorId'
langsmith_options:
$ref: '../common.schema.yaml#/components/schemas/LangSmithOptions'
$ref: '../../common.schema.yaml#/components/schemas/LangSmithOptions'
responses:
200:
description: Indicates the migration start request has been processed successfully.
@ -138,8 +181,8 @@ paths:
in: path
required: true
schema:
type: string
description: The migration id to start
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
responses:
200:
description: Indicates the migration stats has been retrieved correctly.
@ -163,8 +206,8 @@ paths:
in: path
required: true
schema:
type: string
description: The migration id to stop
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
responses:
200:
description: Indicates migration task stop has been processed successfully.
@ -197,8 +240,8 @@ paths:
in: path
required: true
schema:
type: string
description: The migration id to attach the resources
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
requestBody:
required: true
content:
@ -206,7 +249,15 @@ paths:
schema:
type: array
items:
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationResourceData'
allOf:
- $ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationResourceData'
- type: object
required:
- id
properties:
id:
description: The rule resource migration id
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
responses:
200:
description: Indicates migration resources have been created or updated correctly.
@ -234,8 +285,8 @@ paths:
in: path
required: true
schema:
type: string
description: The migration id to attach the resources
$ref: '../../common.schema.yaml#/components/schemas/NonEmptyString'
- name: type
in: query
required: false

View file

@ -10,12 +10,21 @@
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: SIEM Rule Migrations API common components
* title: SIEM Rule Migration common components
* version: not applicable
*/
import { z } from '@kbn/zod';
/**
* A string that is not empty and does not contain only whitespace
*/
export type NonEmptyString = z.infer<typeof NonEmptyString>;
export const NonEmptyString = z
.string()
.min(1)
.regex(/^(?! *$).+$/);
/**
* The GenAI connector id to use.
*/

View file

@ -1,11 +1,16 @@
openapi: 3.0.3
info:
title: SIEM Rule Migrations API common components
title: SIEM Rule Migration common components
version: 'not applicable'
paths: {}
components:
x-codegen-enabled: true
schemas:
NonEmptyString:
type: string
pattern: ^(?! *$).+$
minLength: 1
description: A string that is not empty and does not contain only whitespace
ConnectorId:
type: string
description: The GenAI connector id to use.

View file

@ -10,12 +10,14 @@
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
*
* info:
* title: SIEM Rule Migration common components
* title: SIEM Rule Migration components
* version: not applicable
*/
import { z } from '@kbn/zod';
import { NonEmptyString } from './common.gen';
/**
* The original rule vendor identifier.
*/
@ -30,7 +32,10 @@ export const OriginalRule = z.object({
/**
* The original rule id.
*/
id: z.string(),
id: NonEmptyString,
/**
* The original rule vendor identifier.
*/
vendor: OriginalRuleVendor,
/**
* The original rule name.
@ -82,18 +87,46 @@ export const ElasticRule = z.object({
/**
* The Elastic prebuilt rule id matched.
*/
prebuilt_rule_id: z.string().optional(),
prebuilt_rule_id: NonEmptyString.optional(),
/**
* The Elastic rule id installed as a result.
*/
id: z.string().optional(),
id: NonEmptyString.optional(),
});
/**
* The partial version of the migrated elastic rule.
*/
export type ElasticRulePartial = z.infer<typeof ElasticRulePartial>;
export const ElasticRulePartial = ElasticRule.partial();
/**
* The rule translation result.
*/
export type RuleMigrationTranslationResult = z.infer<typeof RuleMigrationTranslationResult>;
export const RuleMigrationTranslationResult = z.enum(['full', 'partial', 'untranslatable']);
export type RuleMigrationTranslationResultEnum = typeof RuleMigrationTranslationResult.enum;
export const RuleMigrationTranslationResultEnum = RuleMigrationTranslationResult.enum;
/**
* The status of the rule migration process.
*/
export type RuleMigrationStatus = z.infer<typeof RuleMigrationStatus>;
export const RuleMigrationStatus = z.enum(['pending', 'processing', 'completed', 'failed']);
export type RuleMigrationStatusEnum = typeof RuleMigrationStatus.enum;
export const RuleMigrationStatusEnum = RuleMigrationStatus.enum;
/**
* The comments for the migration including a summary from the LLM in markdown.
*/
export type RuleMigrationComments = z.infer<typeof RuleMigrationComments>;
export const RuleMigrationComments = z.array(z.string());
/**
* The rule migration document object.
*/
export type RuleMigration = z.infer<typeof RuleMigration>;
export const RuleMigration = z.object({
export type RuleMigrationData = z.infer<typeof RuleMigrationData>;
export const RuleMigrationData = z.object({
/**
* The moment of creation
*/
@ -101,25 +134,31 @@ export const RuleMigration = z.object({
/**
* The migration id.
*/
migration_id: z.string(),
migration_id: NonEmptyString,
/**
* The username of the user who created the migration.
*/
created_by: z.string(),
created_by: NonEmptyString,
/**
* The original rule to migrate.
*/
original_rule: OriginalRule,
/**
* The migrated elastic rule.
*/
elastic_rule: ElasticRule.optional(),
/**
* The rule translation result.
*/
translation_result: z.enum(['full', 'partial', 'untranslatable']).optional(),
translation_result: RuleMigrationTranslationResult.optional(),
/**
* The status of the rule migration process.
*/
status: z.enum(['pending', 'processing', 'completed', 'failed']).default('pending'),
status: RuleMigrationStatus.default('pending'),
/**
* The comments for the migration including a summary from the LLM in markdown.
*/
comments: z.array(z.string()).optional(),
comments: RuleMigrationComments.optional(),
/**
* The moment of the last update
*/
@ -130,6 +169,19 @@ export const RuleMigration = z.object({
updated_by: z.string().optional(),
});
/**
* The rule migration document object.
*/
export type RuleMigration = z.infer<typeof RuleMigration>;
export const RuleMigration = z
.object({
/**
* The rule migration id
*/
id: NonEmptyString,
})
.merge(RuleMigrationData);
/**
* The rule migration task stats object.
*/
@ -177,7 +229,7 @@ export const RuleMigrationAllTaskStats = z.array(
/**
* The migration id
*/
migration_id: z.string(),
migration_id: NonEmptyString,
})
)
);
@ -216,10 +268,14 @@ export const RuleMigrationResourceData = z.object({
export type RuleMigrationResource = z.infer<typeof RuleMigrationResource>;
export const RuleMigrationResource = RuleMigrationResourceData.merge(
z.object({
/**
* The rule resource migration id
*/
id: NonEmptyString,
/**
* The migration id
*/
migration_id: z.string(),
migration_id: NonEmptyString,
/**
* The moment of the last update
*/

View file

@ -1,12 +1,11 @@
openapi: 3.0.3
info:
title: SIEM Rule Migration common components
title: SIEM Rule Migration components
version: 'not applicable'
paths: {}
components:
x-codegen-enabled: true
schemas:
OriginalRuleVendor:
type: string
description: The original rule vendor identifier.
@ -25,9 +24,10 @@ components:
- query_language
properties:
id:
type: string
description: The original rule id.
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
vendor:
description: The original rule vendor identifier.
$ref: '#/components/schemas/OriginalRuleVendor'
title:
type: string
@ -71,13 +71,30 @@ components:
enum:
- esql
prebuilt_rule_id:
type: string
description: The Elastic prebuilt rule id matched.
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
id:
type: string
description: The Elastic rule id installed as a result.
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
ElasticRulePartial:
description: The partial version of the migrated elastic rule.
$ref: '#/components/schemas/ElasticRule'
x-modify: partial
RuleMigration:
description: The rule migration document object.
allOf:
- type: object
required:
- id
properties:
id:
description: The rule migration id
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
- $ref: '#/components/schemas/RuleMigrationData'
RuleMigrationData:
type: object
description: The rule migration document object.
required:
@ -91,36 +108,27 @@ components:
type: string
description: The moment of creation
migration_id:
type: string
description: The migration id.
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
created_by:
type: string
description: The username of the user who created the migration.
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
original_rule:
description: The original rule to migrate.
$ref: '#/components/schemas/OriginalRule'
elastic_rule:
description: The migrated elastic rule.
$ref: '#/components/schemas/ElasticRule'
translation_result:
type: string
description: The rule translation result.
enum: # should match SiemMigrationRuleTranslationResult enum at ../constants.ts
- full
- partial
- untranslatable
$ref: '#/components/schemas/RuleMigrationTranslationResult'
status:
type: string
description: The status of the rule migration process.
enum: # should match SiemMigrationsStatus enum at ../constants.ts
- pending
- processing
- completed
- failed
$ref: '#/components/schemas/RuleMigrationStatus'
default: pending
comments:
type: array
description: The comments for the migration including a summary from the LLM in markdown.
items:
type: string
$ref: '#/components/schemas/RuleMigrationComments'
updated_at:
type: string
description: The moment of the last update
@ -182,11 +190,34 @@ components:
- migration_id
properties:
migration_id:
type: string
description: The migration id
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
RuleMigrationTranslationResult:
type: string
description: The rule translation result.
enum: # should match SiemMigrationRuleTranslationResult enum at ../constants.ts
- full
- partial
- untranslatable
RuleMigrationStatus:
type: string
description: The status of the rule migration process.
enum: # should match SiemMigrationsStatus enum at ../constants.ts
- pending
- processing
- completed
- failed
RuleMigrationComments:
type: array
description: The comments for the migration including a summary from the LLM in markdown.
items:
type: string
## Rule migration resources
## Rule migration resources
RuleMigrationResourceType:
type: string
description: The type of the rule migration resource.
@ -220,11 +251,15 @@ components:
- $ref: '#/components/schemas/RuleMigrationResourceData'
- type: object
required:
- id
- migration_id
properties:
id:
description: The rule resource migration id
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
migration_id:
type: string
description: The migration id
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
updated_at:
type: string
description: The moment of the last update

View file

@ -14,7 +14,7 @@ import {
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
import { SIEM_RULE_MIGRATIONS_PATH } from '../../../../../common/siem_migrations/constants';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import type { CreateRuleMigrationInput } from '../data/rule_migrations_data_client';
import type { CreateRuleMigrationInput } from '../data/rule_migrations_data_rules_client';
import { withLicense } from './util/with_license';
export const registerSiemRuleMigrationsCreateRoute = (

View file

@ -8,6 +8,7 @@
import type { Logger } from '@kbn/core/server';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { registerSiemRuleMigrationsCreateRoute } from './create';
import { registerSiemRuleMigrationsUpdateRoute } from './update';
import { registerSiemRuleMigrationsGetRoute } from './get';
import { registerSiemRuleMigrationsStartRoute } from './start';
import { registerSiemRuleMigrationsStatsRoute } from './stats';
@ -22,6 +23,7 @@ export const registerSiemRuleMigrationsRoutes = (
logger: Logger
) => {
registerSiemRuleMigrationsCreateRoute(router, logger);
registerSiemRuleMigrationsUpdateRoute(router, logger);
registerSiemRuleMigrationsStatsAllRoute(router, logger);
registerSiemRuleMigrationsGetRoute(router, logger);
registerSiemRuleMigrationsStartRoute(router, logger);

View file

@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { IKibanaResponse, Logger } from '@kbn/core/server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import {
UpdateRuleMigrationRequestBody,
type UpdateRuleMigrationResponse,
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
import { SIEM_RULE_MIGRATIONS_PATH } from '../../../../../common/siem_migrations/constants';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { withLicense } from './util/with_license';
export const registerSiemRuleMigrationsUpdateRoute = (
router: SecuritySolutionPluginRouter,
logger: Logger
) => {
router.versioned
.put({
path: SIEM_RULE_MIGRATIONS_PATH,
access: 'internal',
security: { authz: { requiredPrivileges: ['securitySolution'] } },
})
.addVersion(
{
version: '1',
validate: {
request: { body: buildRouteValidationWithZod(UpdateRuleMigrationRequestBody) },
},
},
withLicense(
async (context, req, res): Promise<IKibanaResponse<UpdateRuleMigrationResponse>> => {
const rulesToUpdate = req.body;
try {
const ctx = await context.resolve(['securitySolution']);
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
await ruleMigrationsClient.data.rules.update(rulesToUpdate);
return res.ok({ body: { updated: true } });
} catch (err) {
logger.error(err);
return res.badRequest({ body: err.message });
}
}
)
);
};

View file

@ -33,7 +33,7 @@ export class RuleMigrationsDataBaseClient {
return hits.map(({ _id, _source }) => {
assert(_id, 'document should have _id');
assert(_source, 'document should have _source');
return { ..._source, ...override, _id };
return { ..._source, ...override, id: _id };
});
}

View file

@ -6,18 +6,10 @@
*/
import type { ElasticsearchClient, Logger } from '@kbn/core/server';
import type {
RuleMigration,
RuleMigrationTaskStats,
} from '../../../../../common/siem_migrations/model/rule_migration.gen';
import { RuleMigrationsDataRulesClient } from './rule_migrations_data_rules_client';
import { RuleMigrationsDataResourcesClient } from './rule_migrations_data_resources_client';
import type { AdapterId } from './rule_migrations_data_service';
export type CreateRuleMigrationInput = Omit<RuleMigration, '@timestamp' | 'status' | 'created_by'>;
export type RuleMigrationDataStats = Omit<RuleMigrationTaskStats, 'status'>;
export type RuleMigrationAllDataStats = Array<RuleMigrationDataStats & { migration_id: string }>;
export type IndexNameProvider = () => Promise<string>;
export type IndexNameProviders = Record<AdapterId, IndexNameProvider>;

View file

@ -15,12 +15,20 @@ import type {
import type { StoredRuleMigration } from '../types';
import { SiemMigrationStatus } from '../../../../../common/siem_migrations/constants';
import type {
ElasticRule,
RuleMigration,
RuleMigrationTaskStats,
} from '../../../../../common/siem_migrations/model/rule_migration.gen';
import { RuleMigrationsDataBaseClient } from './rule_migrations_data_base_client';
export type CreateRuleMigrationInput = Omit<RuleMigration, '@timestamp' | 'status' | 'created_by'>;
export type CreateRuleMigrationInput = Omit<
RuleMigration,
'@timestamp' | 'id' | 'status' | 'created_by'
>;
export type UpdateRuleMigrationInput = { elastic_rule?: Partial<ElasticRule> } & Pick<
RuleMigration,
'id' | 'translation_result' | 'comments'
>;
export type RuleMigrationDataStats = Omit<RuleMigrationTaskStats, 'status'>;
export type RuleMigrationAllDataStats = Array<RuleMigrationDataStats & { migration_id: string }>;
@ -35,6 +43,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
const index = await this.getIndexName();
let ruleMigrationsSlice: CreateRuleMigrationInput[];
const createdAt = new Date().toISOString();
while ((ruleMigrationsSlice = ruleMigrations.splice(0, BULK_MAX_SIZE)).length) {
await this.esClient
.bulk({
@ -43,9 +52,11 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
{ create: { _index: index } },
{
...ruleMigration,
'@timestamp': new Date().toISOString(),
'@timestamp': createdAt,
status: SiemMigrationStatus.PENDING,
created_by: this.username,
updated_by: this.username,
updated_at: createdAt,
},
]),
})
@ -56,6 +67,37 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
}
}
/** Updates an array of rule migrations to be processed */
async update(ruleMigrations: UpdateRuleMigrationInput[]): Promise<void> {
const index = await this.getIndexName();
let ruleMigrationsSlice: UpdateRuleMigrationInput[];
const updatedAt = new Date().toISOString();
while ((ruleMigrationsSlice = ruleMigrations.splice(0, BULK_MAX_SIZE)).length) {
await this.esClient
.bulk({
refresh: 'wait_for',
operations: ruleMigrationsSlice.flatMap((ruleMigration) => {
const { id, ...rest } = ruleMigration;
return [
{ update: { _index: index, _id: id } },
{
doc: {
...rest,
updated_by: this.username,
updated_at: updatedAt,
},
},
];
}),
})
.catch((error) => {
this.logger.error(`Error updating rule migrations: ${error.message}`);
throw error;
});
}
}
/** Retrieves an array of rule documents of a specific migrations */
async get(migrationId: string): Promise<StoredRuleMigration[]> {
const index = await this.getIndexName();
@ -94,8 +136,8 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
await this.esClient
.bulk({
refresh: 'wait_for',
operations: storedRuleMigrations.flatMap(({ _id, status }) => [
{ update: { _id, _index: index } },
operations: storedRuleMigrations.flatMap(({ id, status }) => [
{ update: { _id: id, _index: index } },
{
doc: { status, updated_by: this.username, updated_at: new Date().toISOString() },
},
@ -112,7 +154,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
}
/** Updates one rule migration with the provided data and sets the status to `completed` */
async saveCompleted({ _id, ...ruleMigration }: StoredRuleMigration): Promise<void> {
async saveCompleted({ id, ...ruleMigration }: StoredRuleMigration): Promise<void> {
const index = await this.getIndexName();
const doc = {
...ruleMigration,
@ -120,14 +162,14 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
updated_by: this.username,
updated_at: new Date().toISOString(),
};
await this.esClient.update({ index, id: _id, doc, refresh: 'wait_for' }).catch((error) => {
await this.esClient.update({ index, id, doc, refresh: 'wait_for' }).catch((error) => {
this.logger.error(`Error updating rule migration status to completed: ${error.message}`);
throw error;
});
}
/** Updates one rule migration with the provided data and sets the status to `failed` */
async saveError({ _id, ...ruleMigration }: StoredRuleMigration): Promise<void> {
async saveError({ id, ...ruleMigration }: StoredRuleMigration): Promise<void> {
const index = await this.getIndexName();
const doc = {
...ruleMigration,
@ -135,7 +177,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
updated_by: this.username,
updated_at: new Date().toISOString(),
};
await this.esClient.update({ index, id: _id, doc, refresh: 'wait_for' }).catch((error) => {
await this.esClient.update({ index, id, doc, refresh: 'wait_for' }).catch((error) => {
this.logger.error(`Error updating rule migration status to failed: ${error.message}`);
throw error;
});

View file

@ -11,7 +11,7 @@ import type {
RuleMigrationResource,
} from '../../../../../common/siem_migrations/model/rule_migration.gen';
export const ruleMigrationsFieldMap: FieldMap<SchemaFieldMapKeys<RuleMigration>> = {
export const ruleMigrationsFieldMap: FieldMap<SchemaFieldMapKeys<Omit<RuleMigration, 'id'>>> = {
'@timestamp': { type: 'date', required: false },
migration_id: { type: 'keyword', required: true },
created_by: { type: 'keyword', required: true },
@ -38,7 +38,9 @@ export const ruleMigrationsFieldMap: FieldMap<SchemaFieldMapKeys<RuleMigration>>
updated_by: { type: 'keyword', required: false },
};
export const ruleMigrationResourcesFieldMap: FieldMap<SchemaFieldMapKeys<RuleMigrationResource>> = {
export const ruleMigrationResourcesFieldMap: FieldMap<
SchemaFieldMapKeys<Omit<RuleMigrationResource, 'id'>>
> = {
migration_id: { type: 'keyword', required: true },
type: { type: 'keyword', required: true },
name: { type: 'keyword', required: true },

View file

@ -13,10 +13,8 @@ import type {
RuleMigrationAllTaskStats,
RuleMigrationTaskStats,
} from '../../../../../common/siem_migrations/model/rule_migration.gen';
import type {
RuleMigrationDataStats,
RuleMigrationsDataClient,
} from '../data/rule_migrations_data_client';
import type { RuleMigrationsDataClient } from '../data/rule_migrations_data_client';
import type { RuleMigrationDataStats } from '../data/rule_migrations_data_rules_client';
import type {
RuleMigrationTaskStartParams,
RuleMigrationTaskStartResult,

View file

@ -10,7 +10,7 @@ import type {
RuleMigrationResource,
} from '../../../../common/siem_migrations/model/rule_migration.gen';
export type Stored<T extends object> = T & { _id: string };
export type Stored<T extends object> = T & { id: string };
export type StoredRuleMigration = Stored<RuleMigration>;
export type StoredRuleMigrationResource = Stored<RuleMigrationResource>;

View file

@ -142,6 +142,7 @@ import { StopRuleMigrationRequestParamsInput } from '@kbn/security-solution-plug
import { SuggestUserProfilesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/users/suggest_user_profiles_route.gen';
import { TriggerRiskScoreCalculationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/risk_engine/entity_calculation_route.gen';
import { UpdateRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.gen';
import { UpdateRuleMigrationRequestBodyInput } from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen';
import {
UpsertRuleMigrationResourcesRequestParamsInput,
UpsertRuleMigrationResourcesRequestBodyInput,
@ -1413,6 +1414,17 @@ detection engine rules.
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send(props.body as object);
},
/**
* Updates rules migrations attributes
*/
updateRuleMigration(props: UpdateRuleMigrationProps, kibanaSpace: string = 'default') {
return supertest
.put(routeWithNamespace('/internal/siem_migrations/rules', kibanaSpace))
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send(props.body as object);
},
uploadAssetCriticalityRecords(kibanaSpace: string = 'default') {
return supertest
.post(routeWithNamespace('/api/asset_criticality/upload_csv', kibanaSpace))
@ -1712,6 +1724,9 @@ export interface TriggerRiskScoreCalculationProps {
export interface UpdateRuleProps {
body: UpdateRuleRequestBodyInput;
}
export interface UpdateRuleMigrationProps {
body: UpdateRuleMigrationRequestBodyInput;
}
export interface UpsertRuleMigrationResourcesProps {
params: UpsertRuleMigrationResourcesRequestParamsInput;
body: UpsertRuleMigrationResourcesRequestBodyInput;