Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/kbn-check-saved-objects-cli/current_fields.json
Original file line number Diff line number Diff line change
Expand Up @@ -1056,7 +1056,11 @@
"provider"
],
"security-rule": [
"name",
"risk_score",
"rule_id",
"severity",
"tags",
"version"
],
"security-solution-signals-migration": [
Expand Down
18 changes: 18 additions & 0 deletions packages/kbn-check-saved-objects-cli/current_mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3442,9 +3442,27 @@
"security-rule": {
"dynamic": false,
"properties": {
"name": {
"fields": {
"keyword": {
"normalizer": "lowercase",
"type": "keyword"
}
},
"type": "text"
},
"risk_score": {
"type": "float"
},
"rule_id": {
"type": "keyword"
},
"severity": {
"type": "keyword"
},
"tags": {
"type": "keyword"
},
"version": {
"type": "long"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"10.0.0": [
{
"rule_id": "sample-rule-id",
"version": 1
}
],
"10.1.0": [
{
"rule_id": "sample-rule-id",
"version": 1
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"10.1.0": [
{
"rule_id": "sample-rule-id",
"version": 1
}
],
"10.2.0": [
{
"rule_id": "sample-rule-id",
"version": 1,
"name": "Sample rule name",
"tags": ["tag"],
"severity": "low",
"risk_score": 21
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"search-telemetry": "c152fc7e66d5ac7907e81c0926be9c219a15181e10b418b2fbb86bab2760627c",
"search_playground": "8facc7ad66d9ca130f71168704cb1d01bb3a9763125e6c42e3053de4502fd94e",
"security-ai-prompt": "ba1b779e1a4d20f9c3fbe9355cfbabf0fc6a3f4cf7a2f2ad36d2450528d3b7f6",
"security-rule": "f605ac9c68b8719df1ea2167eb17b937d5f4b060f44cd46099a837a4b631eaf2",
"security-rule": "881ba5b4a902009611ce6b19d75582dbb7682a47476539d4d17cc822ee438f78",
"security-solution-signals-migration": "738346c46260f118506c15a154adb6ef33a57290d8d6dac8340209e8ca33bdd4",
"security:reference-data": "3b76b7adf1799ba3420c34081145c3529113fb04e7e5a48e81d4ac412cf8d73a",
"siem-detection-engine-rule-actions": "4980ff33eb0061a925b702fb4fe41e3310da094765d8b49edcf1a7516abeb57c",
Expand Down Expand Up @@ -1086,9 +1086,11 @@ describe('checking migration metadata changes on all registered SO types', () =>
"security-ai-prompt|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709",
"====================================================================",
"security-rule|global: 95bb9ee9aa1f0169245c05491ea1ecdebbcfcb29",
"security-rule|mappings: 990fd7a260c7c9887d5df7c96844d11fd6e91513",
"security-rule|mappings: c4f3e2b47d2f27c79852ccf284ab93042abd31d8",
"security-rule|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709",
"===============================================================",
"security-rule|10.2.0: ce066da6d7ea7bcee17bacd9713200fd5a6277352e91d17b4cea6b65dc2962c8",
"security-rule|10.1.0: 40105e2b07e93a5a5c5f4dcd85bf2f3a18d838d188d4369988b4271b3c41a49f",
"======================================================================================",
"security-solution-signals-migration|global: 34c65c8a9d3df15c397fcf0e029df6b4211897c2",
"security-solution-signals-migration|mappings: 0fcac70734e9b58e72d63b9994c132dde151f00a",
"security-solution-signals-migration|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709",
Expand Down Expand Up @@ -1400,7 +1402,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"search-telemetry": "10.0.0",
"search_playground": "10.1.0",
"security-ai-prompt": "10.0.0",
"security-rule": "10.0.0",
"security-rule": "10.2.0",
"security-solution-signals-migration": "10.0.0",
"security:reference-data": "10.1.0",
"siem-detection-engine-rule-actions": "10.0.0",
Expand Down Expand Up @@ -1553,7 +1555,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"search-telemetry": "7.12.0",
"search_playground": "10.1.0",
"security-ai-prompt": "0.0.0",
"security-rule": "0.0.0",
"security-rule": "10.2.0",
"security-solution-signals-migration": "0.0.0",
"security:reference-data": "10.1.0",
"siem-detection-engine-rule-actions": "7.16.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 { z } from '@kbn/zod';

// API design document (internal): https://docs.google.com/document/d/1AYlt8wJMoLD-V_owAd4qL-h76IOVnfDCZ77VNmmf-Ks
export type PrebuiltRuleAssetsFilter = z.infer<typeof PrebuiltRuleAssetsFilter>;
export const PrebuiltRuleAssetsFilter = z.object({
fields: z.object({
name: z
.object({
include: z.object({ values: z.array(z.string()) }).optional(),
exclude: z.object({ values: z.array(z.string()) }).optional(),
})
.optional(),
tags: z
.object({
include: z.object({ values: z.array(z.string()) }).optional(),
exclude: z.object({ values: z.array(z.string()) }).optional(),
})
.optional(),
}),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 { z } from '@kbn/zod';
import { SortOrder } from '../../model';

export type PrebuiltRuleAssetsSortField = z.infer<typeof PrebuiltRuleAssetsSortField>;
export const PrebuiltRuleAssetsSortField = z.enum(['name', 'risk_score', 'severity']);

export type PrebuiltRuleAssetsSort = z.infer<typeof PrebuiltRuleAssetsSort>;
export const PrebuiltRuleAssetsSort = z.array(
z.object({
/**
* Field to sort by
*/
field: PrebuiltRuleAssetsSortField,
/**
* Sort order
*/
order: SortOrder,
})
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,55 @@
* 2.0.
*/

import { z } from '@kbn/zod';
import type { RuleTagArray } from '../../model';
import type { RuleResponse } from '../../model/rule_schema';
import { PrebuiltRuleAssetsFilter } from '../common/prebuilt_rule_assets_filter';
import { PrebuiltRuleAssetsSort } from '../common/prebuilt_rule_assets_sort';

export type ReviewRuleInstallationRequestBody = z.infer<typeof ReviewRuleInstallationRequestBody>;
export const ReviewRuleInstallationRequestBody = z
.object({
/**
* Page number starting from 1
*/
page: z.coerce.number().int().min(1).optional(),
/**
* Rules per page
*/
per_page: z.coerce.number().int().min(1).max(10_000).optional(),

/**
* Filtering criteria
*/
filter: PrebuiltRuleAssetsFilter.optional(),

/**
* Sorting criteria
*/
sort: PrebuiltRuleAssetsSort.optional(),
})
.partial();

export interface ReviewRuleInstallationResponseBody {
/** Current page number */
page: number;

/** Rules per page */
per_page: number;

/** Aggregated info about all rules available for installation */
stats: RuleInstallationStatsForReview;

/** Info about individual rules: one object per each rule available for installation */
rules: RuleResponse[];

/** The total number of rules available for installation that match the filter criteria */
total: number;
}

export interface RuleInstallationStatsForReview {
/** Number of prebuilt rules available for installation */
/** Number of prebuilt rules available for installation (before applying filters) */
num_rules_to_install: number;

/** A union of all tags of all rules available for installation */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one
- [**Scenario: User can filter prebuilt rules by rule name on the Rule Installation page**](#scenario-user-can-filter-prebuilt-rules-by-rule-name-on-the-rule-installation-page)
- [**Scenario: User can see a list of available tags on the Rule Installation page**](#scenario-user-can-see-a-list-of-available-tags-on-the-rule-installation-page)
- [**Scenario: User can filter prebuilt rules by a single tag on the Rule Installation page**](#scenario-user-can-filter-prebuilt-rules-by-a-single-tag-on-the-rule-installation-page)
- [**Scenario: User can filter prebuilt rules by tags with special characters on the Rule Installation page**](#scenario-user-can-filter-prebuilt-rules-by-tags-with-special-characters-on-the-rule-installation-page)
- [**Scenario: User can filter prebuilt rules by multiple tags using AND logic on the Rule Installation page**](#scenario-user-can-filter-prebuilt-rules-by-multiple-tags-using-and-logic-on-the-rule-installation-page)
- [**Scenario: User can filter prebuilt rules by name and tags at the same time**](#scenario-user-can-filter-prebuilt-rules-by-name-and-tags-at-the-same-time)
- [**Scenario: Empty state is shown when filters match no rules**](#scenario-empty-state-is-shown-when-filters-match-no-rules)
Expand All @@ -77,6 +78,8 @@ https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one
- [**Scenario: API does not install prebuilt rules if they are up to date**](#scenario-api-does-not-install-prebuilt-rules-if-they-are-up-to-date)
- [Error handling](#error-handling)
- [**Scenario: Error is handled when any installation operation on prebuilt rules fails**](#scenario-error-is-handled-when-any-installation-operation-on-prebuilt-rules-fails)
- [**Scenario: Installation review API endpoint rejects invalid `page` and `per_page` parameters**](#scenario-installation-review-api-endpoint-rejects-invalid-page-and-per_page-parameters)
- [**Scenario: Installation review API endpoint returns an empty response when `page` and `per_page` parameters select rules out of bounds**](#scenario-installation-review-api-endpoint-returns-an-empty-response-when-page-and-per_page-parameters-select-rules-out-of-bounds)
- [Authorization / RBAC](#authorization--rbac)
- [**Scenario: User with read privileges on Security Solution cannot install prebuilt rules**](#scenario-user-with-read-privileges-on-security-solution-cannot-install-prebuilt-rules)
- [**Scenario: ML rules are not shown in prebuilt rules installation table when user is not an ML admin**](#scenario-ml-rules-are-not-shown-in-prebuilt-rules-installation-table-when-user-is-not-an-ml-admin)
Expand Down Expand Up @@ -439,6 +442,23 @@ Then only the available prebuilt rules having this tag should be shown

`<tag>` = any tag from the list of available tags

#### **Scenario: User can filter prebuilt rules by tags with special characters on the Rule Installation page**

**Automation**: 1 API integration test with mock rules.

```Gherkin
Given multiple prebuilt rules available for installation
And at least one rule has a <tag_with_special_chars> tag
When user opens the Rule Installation page
And filters the available prebuilt rules by <tag_with_special_chars>
Then only the available prebuilt rules having this tag should be shown
```

**Examples:**

`<tag_with_special_chars>` = tag containing Elasticsearch Query DSL reserved characters (+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /)
More info in query DSL reserved characters: https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-query-string-query#_reserved_characters

#### **Scenario: User can filter prebuilt rules by multiple tags using AND logic on the Rule Installation page**

**Automation**: 1 API integration test with mock rules.
Expand Down Expand Up @@ -757,6 +777,34 @@ Examples:
| installing individual |
```

#### **Scenario: Installation review API endpoint rejects invalid `page` and `per_page` parameters**

**Automation**: 2 integration tests with mock rules - one per each parameter.

```Gherkin
Given a prebuilt rule asset exists in Kibana
When user calls the review installation endpoint with invalid value for <field>
Then the endpoint should return a 400 error with the correct error message

Examples:
| field | invalid value |
| page | empty string, 0, -1 |
| per_page | empty string, 0, -1, 10001 (max value is 10000) |
```


#### **Scenario: Installation review API endpoint returns an empty response when `page` and `per_page` parameters select rules out of bounds**

**Automation**: 1 integration test with mock rules.

```Gherkin
Given <num_prebuilt_rule_assets> non-installed prebuilt rule assets exist in Kibana
When user calls the review installation endpoint with <page> and <per_page> parameters
And <page> * <per_page> is greater than <num_prebuilt_rule_assets>
Then the endpoint should return an empty list
```


### Authorization / RBAC

#### **Scenario: User with read privileges on Security Solution cannot install prebuilt rules**
Expand Down Expand Up @@ -798,4 +846,4 @@ And a prebuilt rule asset with of a machine learning rule exists in Kibana
And this rule is not installed
When user opens the Rule Installation page
Then user should NOT see this ML rule in the table
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const registerPrebuiltRulesRoutes = (
getPrebuiltRulesStatusRoute(router);
performRuleInstallationRoute(router, logger);
performRuleUpgradeRoute(router, logger);
reviewRuleInstallationRoute(router);
reviewRuleInstallationRoute(router, logger);
reviewRuleUpgradeRoute(router);
bootstrapPrebuiltRulesRoute(router, logger);
getPrebuiltRuleBaseVersion(router);
Expand Down
Loading