Skip to content

Commit 76c1d3a

Browse files
dplumleeelasticmachinebanderror
authored
[Security Solution] Adds customized_fields and has_base_version fields to rule_source object schema (elastic#234793)
**Resolves: elastic/security-team#12507 (internal) ## Summary Adds two new fields to the existing `rule_source` object in our rule schema as described in elastic#230856. Also updates and adds test coverage for the new field logic. The new fields are: - `customized_fields`: an array of objects containing rule field names that have been modified from the base version of the prebuilt rule. - Defaults to empty array if prebuilt rule is not customized or if base version did not exist during diff calculation. - `has_base_version`: a boolean field that specifies if the base version of a prebuilt rule was able to be fetched and used during the customization calculation. This PR also adds related telemetry fields as described in elastic#230856. This includes a `customizations` object field which contains a slimmed down version of `customized_fields` and has a `num_functional_fields` number field that is created in the telemetry task pipeline by comparing the customized fields array to a constant list of field names that we are defining as "functional". This source of truth list can be found in the `x-pack/solutions/security/plugins/security_solution/common/detection_engine/constants.ts` file ### Examples ```json { "rule_source": { "type": "external", "is_customized": true, /* New fields */ "customized_fields": [ { "field_name": "tags", }, { "field_name": "query", } ], "has_base_version": true } } ``` ```json "customizations": { "customized_fields": ["tags", "query"], "num_functional_fields": 2, } ``` ## How to test telemetry Link to internal staging with example data: ([internal staging](https://analytics-staging.sde.elastic.dev/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:'2025-09-26T15:59:24.512Z',to:'2025-09-26T16:08:58.435Z'))&_a=(columns:!(),dataSource:(dataViewId:'4ca97040-d095-11ec-95a5-011050c1180f',type:dataView),filters:!(),interval:auto,query:(language:kuery,query:'customizations.num_functional_fields%20%3E%200'),sort:!(!('@timestamp',desc)),viewMode:documents))) 1. Set the prebuilt rule task type to something shorter than `1hr` in this file: `x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts` 2. Add the following to `kibana.dev.yml`: ``` telemetry.enabled: true telemetry.optIn: true // (Optional for checking to see if its working) logging: root: appenders: [default] level: warn loggers: - name: plugins.securitySolution level: debug - name: plugins.ruleRegistry - name: plugins.taskManager ``` 3. Start up both Elasticsearch and kibana (Has to be done _after_ updating task interval as task objects are stored in ES) 4. Install prebuilt rules 5. Modify prebuilt rules with different field customizations and enable those rules 6. Generate alerts that match these rules (resolver script generator, dev tools, query modification, etc.) 7. View the alerts getting sent to the internal staging telemetry cluster (https://analytics-staging.sde.elastic.dev) in the `detections_alert_telemetry_elastic*` index 8. Use the new `customizations` field to filter out/in customized rule alerts ## Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] [Rule customization tests](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/9317) --------- Co-authored-by: Elastic Machine <[email protected]> Co-authored-by: Georgii Gorbachev <[email protected]>
1 parent 1117104 commit 76c1d3a

File tree

55 files changed

+1386
-303
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1386
-303
lines changed

oas_docs/output/kibana.serverless.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70783,10 +70783,28 @@ components:
7078370783
- endpoint_host_isolation_exceptions
7078470784
- endpoint_blocklists
7078570785
type: string
70786+
Security_Detections_API_ExternalRuleCustomizedFields:
70787+
description: An array of customized field names — that is, fields that the user has modified from their base value. Defaults to an empty array.
70788+
items:
70789+
type: object
70790+
properties:
70791+
field_name:
70792+
description: Name of a user-modified field in the rule object.
70793+
type: string
70794+
required:
70795+
- field_name
70796+
type: array
70797+
Security_Detections_API_ExternalRuleHasBaseVersion:
70798+
description: Determines whether an external/prebuilt rule has its original, unmodified version present when the calculation of its customization status is performed (`rule_source.is_customized` and `rule_source.customized_fields`).
70799+
type: boolean
7078670800
Security_Detections_API_ExternalRuleSource:
7078770801
description: Type of rule source for externally sourced rules, i.e. rules that have an external source, such as the Elastic Prebuilt rules repo.
7078870802
type: object
7078970803
properties:
70804+
customized_fields:
70805+
$ref: '#/components/schemas/Security_Detections_API_ExternalRuleCustomizedFields'
70806+
has_base_version:
70807+
$ref: '#/components/schemas/Security_Detections_API_ExternalRuleHasBaseVersion'
7079070808
is_customized:
7079170809
$ref: '#/components/schemas/Security_Detections_API_IsExternalRuleCustomized'
7079270810
type:
@@ -70796,6 +70814,8 @@ components:
7079670814
required:
7079770815
- type
7079870816
- is_customized
70817+
- has_base_version
70818+
- customized_fields
7079970819
Security_Detections_API_FindRulesSortField:
7080070820
enum:
7080170821
- created_at

oas_docs/output/kibana.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83976,10 +83976,28 @@ components:
8397683976
- endpoint_host_isolation_exceptions
8397783977
- endpoint_blocklists
8397883978
type: string
83979+
Security_Detections_API_ExternalRuleCustomizedFields:
83980+
description: An array of customized field names — that is, fields that the user has modified from their base value. Defaults to an empty array.
83981+
items:
83982+
type: object
83983+
properties:
83984+
field_name:
83985+
description: Name of a user-modified field in the rule object.
83986+
type: string
83987+
required:
83988+
- field_name
83989+
type: array
83990+
Security_Detections_API_ExternalRuleHasBaseVersion:
83991+
description: Determines whether an external/prebuilt rule has its original, unmodified version present when the calculation of its customization status is performed (`rule_source.is_customized` and `rule_source.customized_fields`).
83992+
type: boolean
8397983993
Security_Detections_API_ExternalRuleSource:
8398083994
description: Type of rule source for externally sourced rules, i.e. rules that have an external source, such as the Elastic Prebuilt rules repo.
8398183995
type: object
8398283996
properties:
83997+
customized_fields:
83998+
$ref: '#/components/schemas/Security_Detections_API_ExternalRuleCustomizedFields'
83999+
has_base_version:
84000+
$ref: '#/components/schemas/Security_Detections_API_ExternalRuleHasBaseVersion'
8398384001
is_customized:
8398484002
$ref: '#/components/schemas/Security_Detections_API_IsExternalRuleCustomized'
8398584003
type:
@@ -83989,6 +84007,8 @@ components:
8398984007
required:
8399084008
- type
8399184009
- is_customized
84010+
- has_base_version
84011+
- customized_fields
8399284012
Security_Detections_API_FindRulesSortField:
8399384013
enum:
8399484014
- created_at

x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@ export const IsRuleImmutable = z.boolean();
8989
export type IsExternalRuleCustomized = z.infer<typeof IsExternalRuleCustomized>;
9090
export const IsExternalRuleCustomized = z.boolean();
9191

92+
/**
93+
* Determines whether an external/prebuilt rule has its original, unmodified version present when the calculation of its customization status is performed (`rule_source.is_customized` and `rule_source.customized_fields`).
94+
*/
95+
export type ExternalRuleHasBaseVersion = z.infer<typeof ExternalRuleHasBaseVersion>;
96+
export const ExternalRuleHasBaseVersion = z.boolean();
97+
98+
/**
99+
* An array of customized field names — that is, fields that the user has modified from their base value. Defaults to an empty array.
100+
*/
101+
export type ExternalRuleCustomizedFields = z.infer<typeof ExternalRuleCustomizedFields>;
102+
export const ExternalRuleCustomizedFields = z.array(
103+
z.object({
104+
/**
105+
* Name of a user-modified field in the rule object.
106+
*/
107+
field_name: z.string(),
108+
})
109+
);
110+
92111
/**
93112
* Type of rule source for internally sourced rules, i.e. created within the Kibana apps.
94113
*/
@@ -104,6 +123,8 @@ export type ExternalRuleSource = z.infer<typeof ExternalRuleSource>;
104123
export const ExternalRuleSource = z.object({
105124
type: z.literal('external'),
106125
is_customized: IsExternalRuleCustomized,
126+
has_base_version: ExternalRuleHasBaseVersion,
127+
customized_fields: ExternalRuleCustomizedFields,
107128
});
108129

109130
/**

x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,22 @@ components:
7070
type: boolean
7171
description: Determines whether an external/prebuilt rule has been customized by the user (i.e. any of its fields have been modified and diverged from the base value).
7272

73+
ExternalRuleHasBaseVersion:
74+
type: boolean
75+
description: Determines whether an external/prebuilt rule has its original, unmodified version present when the calculation of its customization status is performed (`rule_source.is_customized` and `rule_source.customized_fields`).
76+
77+
ExternalRuleCustomizedFields:
78+
type: array
79+
description: An array of customized field names — that is, fields that the user has modified from their base value. Defaults to an empty array.
80+
items:
81+
type: object
82+
properties:
83+
field_name:
84+
type: string
85+
description: Name of a user-modified field in the rule object.
86+
required:
87+
- field_name
88+
7389
InternalRuleSource:
7490
description: Type of rule source for internally sourced rules, i.e. created within the Kibana apps.
7591
type: object
@@ -91,9 +107,15 @@ components:
91107
- external
92108
is_customized:
93109
$ref: '#/components/schemas/IsExternalRuleCustomized'
110+
has_base_version:
111+
$ref: '#/components/schemas/ExternalRuleHasBaseVersion'
112+
customized_fields:
113+
$ref: '#/components/schemas/ExternalRuleCustomizedFields'
94114
required:
95115
- type
96116
- is_customized
117+
- has_base_version
118+
- customized_fields
97119

98120
RuleSource:
99121
description: Discriminated union that determines whether the rule is internally sourced (created within the Kibana app) or has an external source, such as the Elastic Prebuilt rules repo.

x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ describe('rule_source', () => {
259259
payload.rule_source = {
260260
type: 'external',
261261
is_customized: true,
262+
customized_fields: [{ field_name: 'name' }],
263+
has_base_version: true,
262264
};
263265

264266
const result = RuleResponse.safeParse(payload);

x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,8 @@ describe('RuleToImport', () => {
10561056
rule_source: {
10571057
type: 'external',
10581058
is_customized: true,
1059+
customized_fields: [{ field_name: 'name' }],
1060+
has_base_version: true,
10591061
},
10601062
});
10611063

x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6315,12 +6315,36 @@ components:
63156315
- endpoint_host_isolation_exceptions
63166316
- endpoint_blocklists
63176317
type: string
6318+
ExternalRuleCustomizedFields:
6319+
description: >-
6320+
An array of customized field names — that is, fields that the user has
6321+
modified from their base value. Defaults to an empty array.
6322+
items:
6323+
type: object
6324+
properties:
6325+
field_name:
6326+
description: Name of a user-modified field in the rule object.
6327+
type: string
6328+
required:
6329+
- field_name
6330+
type: array
6331+
ExternalRuleHasBaseVersion:
6332+
description: >-
6333+
Determines whether an external/prebuilt rule has its original,
6334+
unmodified version present when the calculation of its customization
6335+
status is performed (`rule_source.is_customized` and
6336+
`rule_source.customized_fields`).
6337+
type: boolean
63186338
ExternalRuleSource:
63196339
description: >-
63206340
Type of rule source for externally sourced rules, i.e. rules that have
63216341
an external source, such as the Elastic Prebuilt rules repo.
63226342
type: object
63236343
properties:
6344+
customized_fields:
6345+
$ref: '#/components/schemas/ExternalRuleCustomizedFields'
6346+
has_base_version:
6347+
$ref: '#/components/schemas/ExternalRuleHasBaseVersion'
63246348
is_customized:
63256349
$ref: '#/components/schemas/IsExternalRuleCustomized'
63266350
type:
@@ -6330,6 +6354,8 @@ components:
63306354
required:
63316355
- type
63326356
- is_customized
6357+
- has_base_version
6358+
- customized_fields
63336359
FindRulesSortField:
63346360
enum:
63356361
- created_at

x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5645,12 +5645,36 @@ components:
56455645
- endpoint_host_isolation_exceptions
56465646
- endpoint_blocklists
56475647
type: string
5648+
ExternalRuleCustomizedFields:
5649+
description: >-
5650+
An array of customized field names — that is, fields that the user has
5651+
modified from their base value. Defaults to an empty array.
5652+
items:
5653+
type: object
5654+
properties:
5655+
field_name:
5656+
description: Name of a user-modified field in the rule object.
5657+
type: string
5658+
required:
5659+
- field_name
5660+
type: array
5661+
ExternalRuleHasBaseVersion:
5662+
description: >-
5663+
Determines whether an external/prebuilt rule has its original,
5664+
unmodified version present when the calculation of its customization
5665+
status is performed (`rule_source.is_customized` and
5666+
`rule_source.customized_fields`).
5667+
type: boolean
56485668
ExternalRuleSource:
56495669
description: >-
56505670
Type of rule source for externally sourced rules, i.e. rules that have
56515671
an external source, such as the Elastic Prebuilt rules repo.
56525672
type: object
56535673
properties:
5674+
customized_fields:
5675+
$ref: '#/components/schemas/ExternalRuleCustomizedFields'
5676+
has_base_version:
5677+
$ref: '#/components/schemas/ExternalRuleHasBaseVersion'
56545678
is_customized:
56555679
$ref: '#/components/schemas/IsExternalRuleCustomized'
56565680
type:
@@ -5660,6 +5684,8 @@ components:
56605684
required:
56615685
- type
56625686
- is_customized
5687+
- has_base_version
5688+
- customized_fields
56635689
FindRulesSortField:
56645690
enum:
56655691
- created_at

x-pack/solutions/security/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/prebuilt_rule_customization.md

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one
4848
- [**Scenario: prebuilt rule's `is_customized` is set to true after it is customized when base version is missing**](#scenario-prebuilt-rules-is_customized-is-set-to-true-after-it-is-customized-when-base-version-is-missing)
4949
- [**Scenario: prebuilt rule's `is_customized` stays unchanged after it is saved unchanged when base version is missing**](#scenario-prebuilt-rules-is_customized-stays-unchanged-after-it-is-saved-unchanged-when-base-version-is-missing)
5050
- [**Scenario: prebuilt rule's `is_customized` value is not affected by specific fields when base version is missing**](#scenario-prebuilt-rules-is_customized-value-is-not-affected-by-specific-fields-when-base-version-is-missing)
51+
- [**Scenario: prebuilt rule's `customized_fields` resets to an empty array if rule was previously edited with base version present**](#scenario-prebuilt-rules-customized_fields-resets-to-an-empty-array-if-rule-was-previously-edited-with-base-version-present)
5152
- [Calculating the Modified badge in the UI](#calculating-the-modified-badge-in-the-ui)
5253
- [**Scenario: Modified badge should appear on the rule details page when prebuilt rule is customized**](#scenario-modified-badge-should-appear-on-the-rule-details-page-when-prebuilt-rule-is-customized)
5354
- [**Scenario: Modified badge should not appear on the rule details page when prebuilt rule isn't customized**](#scenario-modified-badge-should-not-appear-on-the-rule-details-page-when-prebuilt-rule-isnt-customized)
@@ -83,6 +84,8 @@ https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one
8384
- [Common terminology](./prebuilt_rules_common_info.md#common-terminology).
8485
- **Rule source**, or **`ruleSource`**: a rule field that defines the rule's origin. Can be `internal` or `external`. Currently, custom rules have `internal` rule source and prebuilt rules have `external` rule source.
8586
- **`is_customized`**: a field within `ruleSource` that exists when rule source is set to `external`. It is a boolean value based on if the rule has been changed from its base version.
87+
- **`customized_fields`**: a field within `ruleSource` that exists when rule source is set to `external`. It is an array of objects containing field names that have been changed from their base version counterparts.
88+
- **`has_base_version`**: a field within `ruleSource` that exists when rule source is set to `external`. It is a boolean value based on if the rule had a matching base version during rule source calculation.
8689
- **non-semantic change**: a change to a rule field that is functionally different. We normalize certain fields so for a time-related field such as `from`, `1m` vs `60s` are treated as the same value. We also trim leading and trailing whitespace for query fields.
8790
- **rule customization**: a change to a customizable field of a prebuilt rule. Full list of customizable rule fields can be found in [Common information about prebuilt rules](./prebuilt_rules_common_info.md#customizable-rule-fields).
8891
- **insufficient license**: a license or a product tier that doesn't allow rule customization. In Serverless environments customization is only allowed on Security Essentials product tier. In non-Serverless environments customization is only allowed on Trial and Enterprise licenses.
@@ -245,6 +248,8 @@ Given a prebuilt rule installed
245248
When user customizes the prebuilt rule by changing the <field_name> field so it differs from the base version
246249
Then the rule's `is_customized` value should be `true`
247250
And ruleSource should be "external"
251+
And the rule's `customized_fields` value should contain <field_name>
252+
And the rule's `has_base_version` value should be true
248253
```
249254

250255
#### **Scenario: prebuilt rule's `is_customized` stays unchanged after it is saved unchanged**
@@ -253,10 +258,12 @@ And ruleSource should be "external"
253258

254259
```Gherkin
255260
Given a prebuilt rule installed
256-
And the prebuilt rule doesn't have a matching base version
261+
And the prebuilt rule has a matching base version
257262
When user opens the corresponding rule editing page
258263
And saves the form unchanged
259264
Then the rule's `is_customized` value should stay unchanged (non-customized rule stays non-customized)
265+
And the rule's `customized_fields` value should be an empty array
266+
And the rule's `has_base_version` value should be true
260267
```
261268

262269
**Examples:**
@@ -272,6 +279,8 @@ Given a prebuilt rule installed
272279
And it is non-customized
273280
When a user changes the <field_name> field so it differs from the base version
274281
Then the rule's `is_customized` value should remain `false`
282+
And the rule's `customized_fields` value should be an empty array
283+
And the rule's `has_base_version` value should be true
275284
```
276285

277286
**Examples:**
@@ -308,6 +317,8 @@ Given a prebuilt rule installed
308317
And it is customized
309318
When a user changes the rule fields to match the base version
310319
Then the rule's `is_customized` value should be false
320+
And the rule's `customized_fields` value should be an empty array
321+
And the rule's `has_base_version` value should be true
311322
```
312323

313324
### Detecting rule customizations when base version is missing
@@ -324,6 +335,8 @@ And the prebuilt rule doesn't have a matching base version
324335
When user customizes the prebuilt rule by changing the <field_name> field so it differs from the base version
325336
Then the rule's `is_customized` value should be `true`
326337
And ruleSource should be "external"
338+
And the rule's `customized_fields` value should be an empty array
339+
And the rule's `has_base_version` value should be false
327340
```
328341

329342
**Examples:**
@@ -340,12 +353,10 @@ And the prebuilt rule doesn't have a matching base version
340353
When user opens the corresponding rule editing page
341354
And saves the form unchanged
342355
Then the rule's `is_customized` value should stay unchanged (non-customized rule stays non-customized)
356+
And the rule's `customized_fields` value should be an empty array
357+
And the rule's `has_base_version` value should be false
343358
```
344359

345-
**Examples:**
346-
347-
`<field_name>` = all customizable rule fields
348-
349360
#### **Scenario: prebuilt rule's `is_customized` value is not affected by specific fields when base version is missing**
350361

351362
**Automation**: one integration test per field.
@@ -356,6 +367,8 @@ And the prebuilt rule doesn't have a matching base version
356367
And it is non-customized
357368
When a user changes the <field_name> field so it differs from the base version
358369
Then the rule's `is_customized` value should remain `false`
370+
And the rule's `customized_fields` value should be an empty array
371+
And the rule's `has_base_version` value should be false
359372
```
360373

361374
**Examples:**
@@ -367,6 +380,21 @@ Then the rule's `is_customized` value should remain `false`
367380
| revision |
368381
| meta |
369382

383+
#### **Scenario: prebuilt rule's `customized_fields` resets to an empty array if rule was previously edited with base version present**
384+
385+
**Automation**: one integration test.
386+
387+
```Gherkin
388+
Given a prebuilt rule installed
389+
And the prebuilt rule has a populated `customized_fields` value
390+
And the prebuilt rule doesn't have a matching base version
391+
When user opens the corresponding rule editing page
392+
And saves the form unchanged
393+
Then the rule's `is_customized` value should remain true
394+
And the rule's `customized_fields` value should be an empty array
395+
And the rule's `has_base_version` value should be false
396+
```
397+
370398
### Calculating the Modified badge in the UI
371399

372400
#### **Scenario: Modified badge should appear on the rule details page when prebuilt rule is customized**

x-pack/solutions/security/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/prebuilt_rule_import.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ When the user imports these rules
274274
Then the rules should be created
275275
And the created rules should be correctly identified as prebuilt or custom
276276
And the created rules' is_customized field should be correctly calculated
277+
And the created rules' customized_fields field should be correctly calculated
277278
And the created rules' parameters should match the import payload
278279
```
279280

@@ -292,6 +293,7 @@ When the user imports these rules
292293
Then the rules should be updated
293294
And the updated rules should be correctly identified as prebuilt or custom
294295
And the updated rules' is_customized field should be correctly calculated
296+
And the created rules' customized_fields field should be correctly calculated
295297
And the updated rules' parameters should match the import payload
296298
```
297299

0 commit comments

Comments
 (0)