mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution] Restructuring folders of Detection Engine + refactoring Rule Management (#142950)
**Partially addresses:** https://github.com/elastic/kibana/issues/138600, https://github.com/elastic/kibana/issues/92169, https://github.com/elastic/kibana/issues/138606 **Addresses:** https://github.com/elastic/kibana/issues/136957, https://github.com/elastic/kibana/issues/136962, https://github.com/elastic/kibana/issues/138614 ## Summary In this PR we are: - Splitting the Detection Engine into subdomains ([ticket](https://github.com/elastic/kibana/issues/138600)). Every subdomain got its own folder under `detection_engine`, and we moved some (not all) code into them. More on that is below. New subdomains introduced: - `fleet_integrations` - `prebuilt_rules` - `rule_actions_legacy` - `rule_exceptions` - `rule_management` - `rule_preview` - `rule_schema` - `rule_creation_ui` - `rule_details_ui` - `rule_management_ui` - `rule_exceptions_ui` - Updating the CODEOWNERS file accordingly. - Refactoring the Rule Management page and the Rules table. Our main focus was on the way how we communicate with the API endpoints, how we cache and invalidate the fetched data, and how this code is organized in the codebase. More on that is below. - Increasing the bundle size limit. This is going to be decreased back in a follow-up PR ([ticket](https://github.com/elastic/kibana/issues/143532)) ## Restructuring folders into subdomains For the background and problem statement, please refer to https://github.com/elastic/kibana/issues/138600 We were focusing on code that is closely related to the Rules area: either owned by us de facto (we work on it) or owned by us de jure (according to the CODEOWNERS file). Or goal was to explicitly extract code that we don't own de facto into separate subdomains, transfer ownership to other area teams, and reflect this in the CODEOWNERS file. On the other hand, we wanted the code that we own to also be organized in clear subdomains that we could easily own via CODEOWNERS. We didn't touch the code that is already explicitly owned by other area teams, e.g. `x-pack/plugins/security_solution/server/lib/detection_engine/rule_types`. This is a draft "domain map" - an architectural diagram that shows how the Detection Engine _could_ be split into subdomains. It's more a TO-BE idea/aspiration rather than an AS-IS statement. Any feedback, critiques, and suggestions would be extremely appreciated! <img width="2592" alt="Screenshot 2022-10-18 at 16 08 40" src="https://user-images.githubusercontent.com/7359339/196453965-b65f5b49-9a33-4d90-bb48-1347e9576223.png"> It shows the flow of dependencies between subdomains and proposes some rules: - The whole graph of dependencies between all subdomains should be a DAG. There should not be bi-directional or circular dependencies between them. - **Generic subdomains** represent some general knowledge that can be used/applied outside of the Detection Engine. - Can depend on some generic kbn packages, npm packages or utils. - Can't depend on any other Detection Engine subdomains. - **Crosscutting subdomains** represent some code that can be common to / shared between many other subdomains. This could be some very common domain models and API schemas. - Can depend on generic subdomains. - Can depend on other crosscutting subdomains (dependencies between them must form a DAG). - Can't depend on core or UI subdomains. - **Core subdomains** contain most of the "meat" of the Detection Engine: domain models, server-side and client-side business logic, server-side API endpoints, client-side UI (potentially shareable between several pages). - Can depend on crosscutting and generic subdomains. - Can depend on other core subdomains (dependencies between them must form a DAG). - Can't depend on UI subdomains. - **UI subdomains** contain the implementation of pages related to the Detection Engine. Every page can easily depend on several core subdomains, so these subdomain are on top of everything. - Can depend on any other subdomains. Dependencies must form a DAG. Dashed lines show some existing dependencies that we think should be eliminated. Ownership TO-BE is color-coded. We updated the CODEOWNERS file according to the new folders. The folder restructuring is not 100% finished but we did a big part of it. Most of the FE code continues to live in legacy folders, e.g. see `x-pack/plugins/security_solution/public/detections`. So this work is to be continued... ## Refactoring of Rule Management FE - [x] https://github.com/elastic/kibana/issues/136957 For effective HTTP requests caching and deduplication, we've migrated all data fetching logic to `useQuery` and `useMutation` hooks from `react-query`. That allowed us to introduce the following improvements to our codebase: * All outgoing HTTP requests are now automatically deduplicated. That means that data fetching hooks like `useRule` could be used on any level in the component tree to access response data directly. So, no need to put the hook on the top level anymore and use prop-drilling to make the response data available to all children components that require it. * All HTTP responses are now cached with the default TTL of 5 minutes—no more redundant requests. With a hot cache, transitions to some pages now happen immediately. - [x] https://github.com/elastic/kibana/issues/136962 Data fetching hooks of the Rules Area are now organized in one place. `security_solution/public/detection_engine/rule_management/api/hooks` contains abstraction layer on top of the Kibana's HTTP client. All data fetching should happen exclusively through that layer to ensure that: * Mutation queries automatically invalidate associated cache entries. * Optimistic updates or updates from mutation responses could be implemented centrally where possible. - [x] https://github.com/elastic/kibana/issues/92169 From some of the Rule Management components, logic was extracted to hooks located in `security_solution/public/detection_engine/rule_management/logic`. ### Checklist - [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
This commit is contained in:
parent
220f867b09
commit
21de750b3c
1423 changed files with 9889 additions and 10938 deletions
37
.github/CODEOWNERS
vendored
37
.github/CODEOWNERS
vendored
|
@ -477,26 +477,31 @@ x-pack/examples/files_example @elastic/kibana-app-services
|
|||
/x-pack/plugins/security_solution/common/detection_engine/schemas/alerts @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/common/field_maps @elastic/security-detections-response-alerts
|
||||
|
||||
/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/public/detections/pages/alerts @elastic/security-detections-response-alerts
|
||||
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/migrations @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/notifications @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/schemas @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/signals @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals @elastic/security-detections-response-alerts
|
||||
|
||||
## Security Solution sub teams - Detections and Response Rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/fleet_integrations @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/prebuilt_rules @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/rule_management @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/rule_monitoring @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/schemas/common @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/schemas/request @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/schemas/response @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/common/detection_engine/rule_schema @elastic/security-detections-response-rules @elastic/security-detections-response-alerts
|
||||
|
||||
/x-pack/plugins/security_solution/public/common/components/health_truncate_text @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/common/components/links_to_docs @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/common/components/ml_popover @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/common/components/popover_items @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/detection_engine/fleet_integrations @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/detection_engine/rule_management @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/detections/components/callouts @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/detections/components/modals/ml_job_upgrade_modal @elastic/security-detections-response-rules
|
||||
|
@ -507,17 +512,12 @@ x-pack/examples/files_example @elastic/kibana-app-services
|
|||
/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/public/rules @elastic/security-detections-response-rules
|
||||
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/fleet @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rule_exceptions_route* @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rule_exceptions_route* @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route* @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route* @elastic/security-detections-response-alerts
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rules @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/tags @elastic/security-detections-response-rules
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema @elastic/security-detections-response-rules @elastic/security-detections-response-alerts
|
||||
|
||||
/x-pack/plugins/security_solution/server/utils @elastic/security-detections-response-rules
|
||||
|
||||
## Security Solution sub teams - Security Platform
|
||||
|
@ -527,12 +527,17 @@ x-pack/examples/files_example @elastic/kibana-app-services
|
|||
/x-pack/plugins/security_solution/cypress/e2e/exceptions @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/cypress/e2e/value_lists @elastic/security-solution-platform
|
||||
|
||||
/x-pack/plugins/security_solution/common/detection_engine/rule_exceptions @elastic/security-solution-platform
|
||||
|
||||
/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions_ui @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/public/common/components/exceptions @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/public/exceptions @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/public/common/components/sourcerer @elastic/security-solution-platform
|
||||
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions @elastic/security-solution-platform
|
||||
/x-pack/plugins/security_solution/server/lib/sourcerer @elastic/security-solution-platform
|
||||
|
||||
## Security Threat Intelligence - Under Security Platform
|
||||
|
@ -595,7 +600,7 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience
|
|||
|
||||
|
||||
# Security Intelligence And Analytics
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules @elastic/security-intelligence-analytics
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/content/prepackaged_rules @elastic/security-intelligence-analytics
|
||||
|
||||
|
||||
# Security Asset Management
|
||||
|
|
|
@ -20,7 +20,6 @@ export * from './src/default_severity_mapping_array';
|
|||
export * from './src/default_threat_array';
|
||||
export * from './src/default_to_string';
|
||||
export * from './src/default_uuid';
|
||||
export * from './src/from';
|
||||
export * from './src/language';
|
||||
export * from './src/machine_learning_job_id';
|
||||
export * from './src/max_signals';
|
||||
|
@ -28,6 +27,7 @@ export * from './src/normalized_ml_job_id';
|
|||
export * from './src/references_default_array';
|
||||
export * from './src/risk_score';
|
||||
export * from './src/risk_score_mapping';
|
||||
export * from './src/rule_schedule';
|
||||
export * from './src/saved_object_attributes';
|
||||
export * from './src/severity';
|
||||
export * from './src/severity_mapping';
|
||||
|
|
|
@ -6,42 +6,47 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { saved_object_attributes } from '../saved_object_attributes';
|
||||
|
||||
export type RuleActionGroup = t.TypeOf<typeof RuleActionGroup>;
|
||||
export const RuleActionGroup = t.string;
|
||||
|
||||
export type RuleActionId = t.TypeOf<typeof RuleActionId>;
|
||||
export const RuleActionId = t.string;
|
||||
|
||||
export type RuleActionTypeId = t.TypeOf<typeof RuleActionTypeId>;
|
||||
export const RuleActionTypeId = t.string;
|
||||
|
||||
/**
|
||||
* Params is an "object", since it is a type of RuleActionParams which is action templates.
|
||||
* @see x-pack/plugins/alerting/common/rule.ts
|
||||
*/
|
||||
export const action_group = t.string;
|
||||
export const action_id = t.string;
|
||||
export const action_action_type_id = t.string;
|
||||
export const action_params = saved_object_attributes;
|
||||
export type RuleActionParams = t.TypeOf<typeof RuleActionParams>;
|
||||
export const RuleActionParams = saved_object_attributes;
|
||||
|
||||
export const action = t.exact(
|
||||
export type RuleAction = t.TypeOf<typeof RuleAction>;
|
||||
export const RuleAction = t.exact(
|
||||
t.type({
|
||||
group: action_group,
|
||||
id: action_id,
|
||||
action_type_id: action_action_type_id,
|
||||
params: action_params,
|
||||
group: RuleActionGroup,
|
||||
id: RuleActionId,
|
||||
action_type_id: RuleActionTypeId,
|
||||
params: RuleActionParams,
|
||||
})
|
||||
);
|
||||
|
||||
export type Action = t.TypeOf<typeof action>;
|
||||
export type RuleActionArray = t.TypeOf<typeof RuleActionArray>;
|
||||
export const RuleActionArray = t.array(RuleAction);
|
||||
|
||||
export const actions = t.array(action);
|
||||
export type Actions = t.TypeOf<typeof actions>;
|
||||
|
||||
export const actionsCamel = t.array(
|
||||
t.exact(
|
||||
t.type({
|
||||
group: action_group,
|
||||
id: action_id,
|
||||
actionTypeId: action_action_type_id,
|
||||
params: action_params,
|
||||
})
|
||||
)
|
||||
export type RuleActionCamel = t.TypeOf<typeof RuleActionCamel>;
|
||||
export const RuleActionCamel = t.exact(
|
||||
t.type({
|
||||
group: RuleActionGroup,
|
||||
id: RuleActionId,
|
||||
actionTypeId: RuleActionTypeId,
|
||||
params: RuleActionParams,
|
||||
})
|
||||
);
|
||||
export type ActionsCamel = t.TypeOf<typeof actions>;
|
||||
|
||||
export type RuleActionArrayCamel = t.TypeOf<typeof RuleActionArrayCamel>;
|
||||
export const RuleActionArrayCamel = t.array(RuleActionCamel);
|
||||
|
|
|
@ -8,12 +8,16 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
import { Either } from 'fp-ts/lib/Either';
|
||||
import { actions, Actions } from '../actions';
|
||||
import { RuleActionArray } from '../actions';
|
||||
|
||||
export const DefaultActionsArray = new t.Type<Actions, Actions | undefined, unknown>(
|
||||
export const DefaultActionsArray = new t.Type<
|
||||
RuleActionArray,
|
||||
RuleActionArray | undefined,
|
||||
unknown
|
||||
>(
|
||||
'DefaultActionsArray',
|
||||
actions.is,
|
||||
(input, context): Either<t.Errors, Actions> =>
|
||||
input == null ? t.success([]) : actions.validate(input, context),
|
||||
RuleActionArray.is,
|
||||
(input, context): Either<t.Errors, RuleActionArray> =>
|
||||
input == null ? t.success([]) : RuleActionArray.validate(input, context),
|
||||
t.identity
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
import { Either } from 'fp-ts/lib/Either';
|
||||
import { from } from '../from';
|
||||
import { From } from '../from';
|
||||
|
||||
/**
|
||||
* Types the DefaultFromString as:
|
||||
|
@ -21,7 +21,7 @@ export const DefaultFromString = new t.Type<string, string | undefined, unknown>
|
|||
if (input == null) {
|
||||
return t.success('now-6m');
|
||||
}
|
||||
return from.validate(input, context);
|
||||
return From.validate(input, context);
|
||||
},
|
||||
t.identity
|
||||
);
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
import { Either } from 'fp-ts/lib/Either';
|
||||
import { RiskScoreMapping, risk_score_mapping } from '../risk_score_mapping';
|
||||
import { RiskScoreMapping } from '../risk_score_mapping';
|
||||
|
||||
/**
|
||||
* Types the DefaultStringArray as:
|
||||
* - If null or undefined, then a default risk_score_mapping array will be set
|
||||
* - If null or undefined, then a default RiskScoreMapping array will be set
|
||||
*/
|
||||
export const DefaultRiskScoreMappingArray = new t.Type<
|
||||
RiskScoreMapping,
|
||||
|
@ -20,8 +20,8 @@ export const DefaultRiskScoreMappingArray = new t.Type<
|
|||
unknown
|
||||
>(
|
||||
'DefaultRiskScoreMappingArray',
|
||||
risk_score_mapping.is,
|
||||
RiskScoreMapping.is,
|
||||
(input, context): Either<t.Errors, RiskScoreMapping> =>
|
||||
input == null ? t.success([]) : risk_score_mapping.validate(input, context),
|
||||
input == null ? t.success([]) : RiskScoreMapping.validate(input, context),
|
||||
t.identity
|
||||
);
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
import { Either } from 'fp-ts/lib/Either';
|
||||
import { SeverityMapping, severity_mapping } from '../severity_mapping';
|
||||
import { SeverityMapping } from '../severity_mapping';
|
||||
|
||||
/**
|
||||
* Types the DefaultStringArray as:
|
||||
* - If null or undefined, then a default severity_mapping array will be set
|
||||
* - If null or undefined, then a default SeverityMapping array will be set
|
||||
*/
|
||||
export const DefaultSeverityMappingArray = new t.Type<
|
||||
SeverityMapping,
|
||||
|
@ -20,8 +20,8 @@ export const DefaultSeverityMappingArray = new t.Type<
|
|||
unknown
|
||||
>(
|
||||
'DefaultSeverityMappingArray',
|
||||
severity_mapping.is,
|
||||
SeverityMapping.is,
|
||||
(input, context): Either<t.Errors, SeverityMapping> =>
|
||||
input == null ? t.success([]) : severity_mapping.validate(input, context),
|
||||
input == null ? t.success([]) : SeverityMapping.validate(input, context),
|
||||
t.identity
|
||||
);
|
||||
|
|
|
@ -12,7 +12,8 @@ import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils';
|
|||
|
||||
const stringValidator = (input: unknown): input is string => typeof input === 'string';
|
||||
|
||||
export const from = new t.Type<string, string, unknown>(
|
||||
export type From = t.TypeOf<typeof From>;
|
||||
export const From = new t.Type<string, string, unknown>(
|
||||
'From',
|
||||
t.string.is,
|
||||
(input, context): Either<t.Errors, string> => {
|
||||
|
@ -23,7 +24,3 @@ export const from = new t.Type<string, string, unknown>(
|
|||
},
|
||||
t.identity
|
||||
);
|
||||
export type From = t.TypeOf<typeof from>;
|
||||
|
||||
export const fromOrUndefined = t.union([from, t.undefined]);
|
||||
export type FromOrUndefined = t.TypeOf<typeof fromOrUndefined>;
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { Either } from 'fp-ts/lib/Either';
|
||||
|
||||
|
@ -16,6 +14,7 @@ import { Either } from 'fp-ts/lib/Either';
|
|||
* - Natural Number (positive integer and not a float),
|
||||
* - Between the values [0 and 100] inclusive.
|
||||
*/
|
||||
export type RiskScore = t.TypeOf<typeof RiskScore>;
|
||||
export const RiskScore = new t.Type<number, number, unknown>(
|
||||
'RiskScore',
|
||||
t.number.is,
|
||||
|
@ -26,11 +25,3 @@ export const RiskScore = new t.Type<number, number, unknown>(
|
|||
},
|
||||
t.identity
|
||||
);
|
||||
|
||||
export type RiskScoreC = typeof RiskScore;
|
||||
|
||||
export const risk_score = RiskScore;
|
||||
export type RiskScore = t.TypeOf<typeof risk_score>;
|
||||
|
||||
export const riskScoreOrUndefined = t.union([risk_score, t.undefined]);
|
||||
export type RiskScoreOrUndefined = t.TypeOf<typeof riskScoreOrUndefined>;
|
||||
|
|
|
@ -6,25 +6,19 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { operator } from '@kbn/securitysolution-io-ts-types';
|
||||
import { riskScoreOrUndefined } from '../risk_score';
|
||||
import { RiskScore } from '../risk_score';
|
||||
|
||||
export const risk_score_mapping_field = t.string;
|
||||
export const risk_score_mapping_value = t.string;
|
||||
export const risk_score_mapping_item = t.exact(
|
||||
export type RiskScoreMappingItem = t.TypeOf<typeof RiskScoreMappingItem>;
|
||||
export const RiskScoreMappingItem = t.exact(
|
||||
t.type({
|
||||
field: risk_score_mapping_field,
|
||||
value: risk_score_mapping_value,
|
||||
field: t.string,
|
||||
value: t.string,
|
||||
operator,
|
||||
risk_score: riskScoreOrUndefined,
|
||||
risk_score: t.union([RiskScore, t.undefined]),
|
||||
})
|
||||
);
|
||||
|
||||
export const risk_score_mapping = t.array(risk_score_mapping_item);
|
||||
export type RiskScoreMapping = t.TypeOf<typeof risk_score_mapping>;
|
||||
|
||||
export const riskScoreMappingOrUndefined = t.union([risk_score_mapping, t.undefined]);
|
||||
export type RiskScoreMappingOrUndefined = t.TypeOf<typeof riskScoreMappingOrUndefined>;
|
||||
export type RiskScoreMapping = t.TypeOf<typeof RiskScoreMapping>;
|
||||
export const RiskScoreMapping = t.array(RiskScoreMappingItem);
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { From } from '../from';
|
||||
|
||||
export type RuleInterval = t.TypeOf<typeof RuleInterval>;
|
||||
export const RuleInterval = t.string; // we need a more specific schema
|
||||
|
||||
export type RuleIntervalFrom = t.TypeOf<typeof RuleIntervalFrom>;
|
||||
export const RuleIntervalFrom = From;
|
||||
|
||||
/**
|
||||
* TODO: Create a regular expression type or custom date math part type here
|
||||
*/
|
||||
export type RuleIntervalTo = t.TypeOf<typeof RuleIntervalTo>;
|
||||
export const RuleIntervalTo = t.string; // we need a more specific schema
|
|
@ -8,8 +8,5 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
export const severity = t.keyof({ low: null, medium: null, high: null, critical: null });
|
||||
export type Severity = t.TypeOf<typeof severity>;
|
||||
|
||||
export const severityOrUndefined = t.union([severity, t.undefined]);
|
||||
export type SeverityOrUndefined = t.TypeOf<typeof severityOrUndefined>;
|
||||
export type Severity = t.TypeOf<typeof Severity>;
|
||||
export const Severity = t.keyof({ low: null, medium: null, high: null, critical: null });
|
||||
|
|
|
@ -6,27 +6,20 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { operator } from '@kbn/securitysolution-io-ts-types';
|
||||
import { severity } from '../severity';
|
||||
import { Severity } from '../severity';
|
||||
|
||||
export const severity_mapping_field = t.string;
|
||||
export const severity_mapping_value = t.string;
|
||||
export const severity_mapping_item = t.exact(
|
||||
export type SeverityMappingItem = t.TypeOf<typeof SeverityMappingItem>;
|
||||
export const SeverityMappingItem = t.exact(
|
||||
t.type({
|
||||
field: severity_mapping_field,
|
||||
field: t.string,
|
||||
operator,
|
||||
value: severity_mapping_value,
|
||||
severity,
|
||||
value: t.string,
|
||||
severity: Severity,
|
||||
})
|
||||
);
|
||||
export type SeverityMappingItem = t.TypeOf<typeof severity_mapping_item>;
|
||||
|
||||
export const severity_mapping = t.array(severity_mapping_item);
|
||||
export type SeverityMapping = t.TypeOf<typeof severity_mapping>;
|
||||
|
||||
export const severityMappingOrUndefined = t.union([severity_mapping, t.undefined]);
|
||||
export type SeverityMappingOrUndefined = t.TypeOf<typeof severityMappingOrUndefined>;
|
||||
export type SeverityMapping = t.TypeOf<typeof SeverityMapping>;
|
||||
export const SeverityMapping = t.array(SeverityMappingItem);
|
||||
|
|
|
@ -6,15 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { TimeDuration } from '@kbn/securitysolution-io-ts-types';
|
||||
import * as t from 'io-ts';
|
||||
import { TimeDuration } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
export const throttle = t.union([
|
||||
export type RuleActionThrottle = t.TypeOf<typeof RuleActionThrottle>;
|
||||
export const RuleActionThrottle = t.union([
|
||||
t.literal('no_actions'),
|
||||
t.literal('rule'),
|
||||
TimeDuration({ allowedUnits: ['h', 'd'] }),
|
||||
]);
|
||||
export type Throttle = t.TypeOf<typeof throttle>;
|
||||
|
||||
export const throttleOrNull = t.union([throttle, t.null]);
|
||||
export type ThrottleOrNull = t.TypeOf<typeof throttleOrNull>;
|
||||
|
|
|
@ -260,16 +260,12 @@ export const UPDATE_OR_CREATE_LEGACY_ACTIONS = '/internal/api/detection/legacy/n
|
|||
* Detection engine routes
|
||||
*/
|
||||
export const DETECTION_ENGINE_URL = '/api/detection_engine' as const;
|
||||
export const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules` as const;
|
||||
export const DETECTION_ENGINE_PREPACKAGED_URL =
|
||||
`${DETECTION_ENGINE_RULES_URL}/prepackaged` as const;
|
||||
export const DETECTION_ENGINE_PRIVILEGES_URL = `${DETECTION_ENGINE_URL}/privileges` as const;
|
||||
export const DETECTION_ENGINE_INDEX_URL = `${DETECTION_ENGINE_URL}/index` as const;
|
||||
|
||||
export const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules` as const;
|
||||
export const DETECTION_ENGINE_RULES_URL_FIND = `${DETECTION_ENGINE_RULES_URL}/_find` as const;
|
||||
export const DETECTION_ENGINE_TAGS_URL = `${DETECTION_ENGINE_URL}/tags` as const;
|
||||
export const DETECTION_ENGINE_PREPACKAGED_RULES_STATUS_URL =
|
||||
`${DETECTION_ENGINE_RULES_URL}/prepackaged/_status` as const;
|
||||
export const DETECTION_ENGINE_RULES_BULK_ACTION =
|
||||
`${DETECTION_ENGINE_RULES_URL}/_bulk_action` as const;
|
||||
export const DETECTION_ENGINE_RULES_PREVIEW = `${DETECTION_ENGINE_RULES_URL}/preview` as const;
|
||||
|
@ -300,13 +296,9 @@ export const RISK_SCORE_DELETE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/store
|
|||
* Internal detection engine routes
|
||||
*/
|
||||
export const INTERNAL_DETECTION_ENGINE_URL = '/internal/detection_engine' as const;
|
||||
export const INTERNAL_DETECTION_ENGINE_RULES_URL = '/internal/detection_engine/rules' as const;
|
||||
export const DETECTION_ENGINE_INSTALLED_INTEGRATIONS_URL =
|
||||
`${INTERNAL_DETECTION_ENGINE_URL}/fleet/integrations/installed` as const;
|
||||
export const DETECTION_ENGINE_ALERTS_INDEX_URL =
|
||||
`${INTERNAL_DETECTION_ENGINE_URL}/signal/index` as const;
|
||||
export const DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL =
|
||||
`${INTERNAL_DETECTION_ENGINE_RULES_URL}/exceptions/_find_references` as const;
|
||||
|
||||
/**
|
||||
* Telemetry detection endpoint for any previews requested of what data we are
|
||||
* providing through UI/UX and for e2e tests.
|
||||
|
@ -456,6 +448,7 @@ export const NEW_FEATURES_TOUR_STORAGE_KEYS = {
|
|||
export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY =
|
||||
'securitySolution.ruleDetails.ruleExecutionLog.showMetrics.v8.2';
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/pull/142950
|
||||
/**
|
||||
* Error codes that can be thrown during _bulk_action API dry_run call and be processed and displayed to end user
|
||||
*/
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { InstalledIntegrationArray } from '../common';
|
||||
import type { InstalledIntegrationArray } from '../../model/installed_integrations';
|
||||
|
||||
export interface GetInstalledIntegrationsResponse {
|
||||
installed_integrations: InstalledIntegrationArray;
|
|
@ -5,8 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { updateRulesSchema } from './rule_schemas';
|
||||
import { INTERNAL_DETECTION_ENGINE_URL as INTERNAL_URL } from '../../../constants';
|
||||
|
||||
export const updateRulesBulkSchema = t.array(updateRulesSchema);
|
||||
export type UpdateRulesBulkSchema = t.TypeOf<typeof updateRulesBulkSchema>;
|
||||
export const GET_INSTALLED_INTEGRATIONS_URL =
|
||||
`${INTERNAL_URL}/fleet/integrations/installed` as const;
|
|
@ -5,9 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
export * from './api/get_installed_integrations/response_schema';
|
||||
export * from './api/urls';
|
||||
|
||||
import { createRulesSchema } from './rule_schemas';
|
||||
|
||||
export const createRulesBulkSchema = t.array(createRulesSchema);
|
||||
export type CreateRulesBulkSchema = t.TypeOf<typeof createRulesBulkSchema>;
|
||||
export * from './model/installed_integrations';
|
|
@ -5,15 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import type { PrePackagedRulesAndTimelinesStatusSchema } from './prepackaged_rules_status_schema';
|
||||
import { prePackagedRulesAndTimelinesStatusSchema } from './prepackaged_rules_status_schema';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
describe('prepackaged_rules_schema', () => {
|
||||
import { GetPrebuiltRulesAndTimelinesStatusResponse } from './response_schema';
|
||||
|
||||
describe('Get prebuilt rules and timelines status response schema', () => {
|
||||
test('it should validate an empty prepackaged response with defaults', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesStatusSchema = {
|
||||
const payload: GetPrebuiltRulesAndTimelinesStatusResponse = {
|
||||
rules_installed: 0,
|
||||
rules_not_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
|
@ -22,7 +22,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
timelines_not_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesStatusSchema.decode(payload);
|
||||
const decoded = GetPrebuiltRulesAndTimelinesStatusResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -31,7 +31,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should not validate an extra invalid field added', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesStatusSchema & { invalid_field: string } = {
|
||||
const payload: GetPrebuiltRulesAndTimelinesStatusResponse & { invalid_field: string } = {
|
||||
rules_installed: 0,
|
||||
rules_not_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
|
@ -41,7 +41,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
timelines_not_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesStatusSchema.decode(payload);
|
||||
const decoded = GetPrebuiltRulesAndTimelinesStatusResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -50,7 +50,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_installed" number', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesStatusSchema = {
|
||||
const payload: GetPrebuiltRulesAndTimelinesStatusResponse = {
|
||||
rules_installed: -1,
|
||||
rules_not_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
|
@ -59,7 +59,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
timelines_not_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesStatusSchema.decode(payload);
|
||||
const decoded = GetPrebuiltRulesAndTimelinesStatusResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -70,7 +70,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_not_installed"', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesStatusSchema = {
|
||||
const payload: GetPrebuiltRulesAndTimelinesStatusResponse = {
|
||||
rules_installed: 0,
|
||||
rules_not_installed: -1,
|
||||
rules_not_updated: 0,
|
||||
|
@ -79,7 +79,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
timelines_not_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesStatusSchema.decode(payload);
|
||||
const decoded = GetPrebuiltRulesAndTimelinesStatusResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -90,7 +90,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_not_updated"', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesStatusSchema = {
|
||||
const payload: GetPrebuiltRulesAndTimelinesStatusResponse = {
|
||||
rules_installed: 0,
|
||||
rules_not_installed: 0,
|
||||
rules_not_updated: -1,
|
||||
|
@ -99,7 +99,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
timelines_not_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesStatusSchema.decode(payload);
|
||||
const decoded = GetPrebuiltRulesAndTimelinesStatusResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -110,7 +110,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_custom_installed"', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesStatusSchema = {
|
||||
const payload: GetPrebuiltRulesAndTimelinesStatusResponse = {
|
||||
rules_installed: 0,
|
||||
rules_not_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
|
@ -119,7 +119,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
timelines_not_installed: 0,
|
||||
timelines_not_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesStatusSchema.decode(payload);
|
||||
const decoded = GetPrebuiltRulesAndTimelinesStatusResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -130,7 +130,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response if "rules_installed" is not there', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesStatusSchema = {
|
||||
const payload: GetPrebuiltRulesAndTimelinesStatusResponse = {
|
||||
rules_installed: 0,
|
||||
rules_not_installed: 0,
|
||||
rules_not_updated: 0,
|
||||
|
@ -141,7 +141,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
};
|
||||
// @ts-expect-error
|
||||
delete payload.rules_installed;
|
||||
const decoded = prePackagedRulesAndTimelinesStatusSchema.decode(payload);
|
||||
const decoded = GetPrebuiltRulesAndTimelinesStatusResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { PositiveInteger } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
export type GetPrebuiltRulesAndTimelinesStatusResponse = t.TypeOf<
|
||||
typeof GetPrebuiltRulesAndTimelinesStatusResponse
|
||||
>;
|
||||
export const GetPrebuiltRulesAndTimelinesStatusResponse = t.exact(
|
||||
t.type({
|
||||
rules_custom_installed: PositiveInteger,
|
||||
rules_installed: PositiveInteger,
|
||||
rules_not_installed: PositiveInteger,
|
||||
rules_not_updated: PositiveInteger,
|
||||
|
||||
timelines_installed: PositiveInteger,
|
||||
timelines_not_installed: PositiveInteger,
|
||||
timelines_not_updated: PositiveInteger,
|
||||
})
|
||||
);
|
|
@ -5,21 +5,21 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import type { PrePackagedRulesAndTimelinesSchema } from './prepackaged_rules_schema';
|
||||
import { prePackagedRulesAndTimelinesSchema } from './prepackaged_rules_schema';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
describe('prepackaged_rules_schema', () => {
|
||||
import { InstallPrebuiltRulesAndTimelinesResponse } from './response_schema';
|
||||
|
||||
describe('Install prebuilt rules and timelines response schema', () => {
|
||||
test('it should validate an empty prepackaged response with defaults', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesSchema = {
|
||||
const payload: InstallPrebuiltRulesAndTimelinesResponse = {
|
||||
rules_installed: 0,
|
||||
rules_updated: 0,
|
||||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesSchema.decode(payload);
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -28,14 +28,14 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should not validate an extra invalid field added', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesSchema & { invalid_field: string } = {
|
||||
const payload: InstallPrebuiltRulesAndTimelinesResponse & { invalid_field: string } = {
|
||||
rules_installed: 0,
|
||||
rules_updated: 0,
|
||||
invalid_field: 'invalid',
|
||||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesSchema.decode(payload);
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -44,13 +44,13 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_installed" number', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesSchema = {
|
||||
const payload: InstallPrebuiltRulesAndTimelinesResponse = {
|
||||
rules_installed: -1,
|
||||
rules_updated: 0,
|
||||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesSchema.decode(payload);
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -61,13 +61,13 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_updated"', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesSchema = {
|
||||
const payload: InstallPrebuiltRulesAndTimelinesResponse = {
|
||||
rules_installed: 0,
|
||||
rules_updated: -1,
|
||||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = prePackagedRulesAndTimelinesSchema.decode(payload);
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -78,7 +78,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response if "rules_installed" is not there', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesSchema = {
|
||||
const payload: InstallPrebuiltRulesAndTimelinesResponse = {
|
||||
rules_installed: 0,
|
||||
rules_updated: 0,
|
||||
timelines_installed: 0,
|
||||
|
@ -86,7 +86,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
};
|
||||
// @ts-expect-error
|
||||
delete payload.rules_installed;
|
||||
const decoded = prePackagedRulesAndTimelinesSchema.decode(payload);
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -97,7 +97,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response if "rules_updated" is not there', () => {
|
||||
const payload: PrePackagedRulesAndTimelinesSchema = {
|
||||
const payload: InstallPrebuiltRulesAndTimelinesResponse = {
|
||||
rules_installed: 0,
|
||||
rules_updated: 0,
|
||||
timelines_installed: 0,
|
||||
|
@ -105,7 +105,7 @@ describe('prepackaged_rules_schema', () => {
|
|||
};
|
||||
// @ts-expect-error
|
||||
delete payload.rules_updated;
|
||||
const decoded = prePackagedRulesAndTimelinesSchema.decode(payload);
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { PositiveInteger } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
export type InstallPrebuiltRulesAndTimelinesResponse = t.TypeOf<
|
||||
typeof InstallPrebuiltRulesAndTimelinesResponse
|
||||
>;
|
||||
export const InstallPrebuiltRulesAndTimelinesResponse = t.exact(
|
||||
t.type({
|
||||
rules_installed: PositiveInteger,
|
||||
rules_updated: PositiveInteger,
|
||||
|
||||
timelines_installed: PositiveInteger,
|
||||
timelines_updated: PositiveInteger,
|
||||
})
|
||||
);
|
|
@ -5,9 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { DETECTION_ENGINE_RULES_URL as RULES } from '../../../constants';
|
||||
|
||||
import { patchRulesSchema } from './patch_rules_schema';
|
||||
|
||||
export const patchRulesBulkSchema = t.array(patchRulesSchema);
|
||||
export type PatchRulesBulkSchema = t.TypeOf<typeof patchRulesBulkSchema>;
|
||||
export const PREBUILT_RULES_URL = `${RULES}/prepackaged` as const;
|
||||
export const PREBUILT_RULES_STATUS_URL = `${RULES}/prepackaged/_status` as const;
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './api/get_prebuilt_rules_and_timelines_status/response_schema';
|
||||
export * from './api/install_prebuilt_rules_and_timelines/response_schema';
|
||||
export * from './api/urls';
|
||||
|
||||
export * from './model/prebuilt_rule';
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './model/prebuilt_rule.mock';
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AddPrepackagedRulesSchema } from './add_prepackaged_rules_schema';
|
||||
import type { PrebuiltRuleToInstall } from './prebuilt_rule';
|
||||
|
||||
export const getAddPrepackagedRulesSchemaMock = (): AddPrepackagedRulesSchema => ({
|
||||
export const getPrebuiltRuleMock = (): PrebuiltRuleToInstall => ({
|
||||
description: 'some description',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
|
@ -19,7 +19,7 @@ export const getAddPrepackagedRulesSchemaMock = (): AddPrepackagedRulesSchema =>
|
|||
version: 1,
|
||||
});
|
||||
|
||||
export const getAddPrepackagedThreatMatchRulesSchemaMock = (): AddPrepackagedRulesSchema => ({
|
||||
export const getPrebuiltThreatMatchRuleMock = (): PrebuiltRuleToInstall => ({
|
||||
description: 'some description',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
|
@ -5,23 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AddPrepackagedRulesSchema } from './add_prepackaged_rules_schema';
|
||||
import { addPrepackagedRulesSchema } from './add_prepackaged_rules_schema';
|
||||
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import {
|
||||
getAddPrepackagedRulesSchemaMock,
|
||||
getAddPrepackagedThreatMatchRulesSchemaMock,
|
||||
} from './add_prepackaged_rules_schema.mock';
|
||||
import { getListArrayMock } from '../types/lists.mock';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
describe('add prepackaged rules schema', () => {
|
||||
import { getListArrayMock } from '../../schemas/types/lists.mock';
|
||||
|
||||
import { PrebuiltRuleToInstall } from './prebuilt_rule';
|
||||
import { getPrebuiltRuleMock, getPrebuiltThreatMatchRuleMock } from './prebuilt_rule.mock';
|
||||
|
||||
describe('Prebuilt rule schema', () => {
|
||||
test('empty objects do not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {};
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toContain(
|
||||
|
@ -46,12 +43,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('made up values do not validate', () => {
|
||||
const payload: AddPrepackagedRulesSchema & { madeUp: string } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall & { madeUp: string } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
madeUp: 'hi',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "madeUp"']);
|
||||
|
@ -59,11 +56,11 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toContain(
|
||||
|
@ -85,12 +82,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toContain(
|
||||
|
@ -109,13 +106,13 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toContain(
|
||||
|
@ -134,14 +131,14 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
to: 'now',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toContain(
|
||||
|
@ -160,7 +157,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -168,7 +165,7 @@ describe('add prepackaged rules schema', () => {
|
|||
name: 'some-name',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toContain(
|
||||
|
@ -184,7 +181,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -193,7 +190,7 @@ describe('add prepackaged rules schema', () => {
|
|||
severity: 'low',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toContain(
|
||||
|
@ -206,7 +203,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -216,7 +213,7 @@ describe('add prepackaged rules schema', () => {
|
|||
type: 'query',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -227,7 +224,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type, interval] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -238,7 +235,7 @@ describe('add prepackaged rules schema', () => {
|
|||
type: 'query',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -249,7 +246,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type, interval, index] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -261,7 +258,7 @@ describe('add prepackaged rules schema', () => {
|
|||
index: ['index-1'],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -272,7 +269,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type, query, index, interval, version] does validate', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
rule_id: 'rule-1',
|
||||
risk_score: 50,
|
||||
description: 'some description',
|
||||
|
@ -287,14 +284,14 @@ describe('add prepackaged rules schema', () => {
|
|||
version: 1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, query, language] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -309,7 +306,7 @@ describe('add prepackaged rules schema', () => {
|
|||
risk_score: 50,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -319,7 +316,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, version] does validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -335,14 +332,14 @@ describe('add prepackaged rules schema', () => {
|
|||
version: 1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score, output_index] does not validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> & { output_index: string } = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> & { output_index: string } = {
|
||||
rule_id: 'rule-1',
|
||||
output_index: '.siem-signals',
|
||||
risk_score: 50,
|
||||
|
@ -358,7 +355,7 @@ describe('add prepackaged rules schema', () => {
|
|||
language: 'kuery',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -368,7 +365,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version] does validate', () => {
|
||||
const payload: Partial<AddPrepackagedRulesSchema> = {
|
||||
const payload: Partial<PrebuiltRuleToInstall> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -382,38 +379,38 @@ describe('add prepackaged rules schema', () => {
|
|||
version: 1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can send in a namespace', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
namespace: 'a namespace',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can send in an empty array to threat', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
threat: [],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threat] does validate', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
rule_id: 'rule-1',
|
||||
risk_score: 50,
|
||||
description: 'some description',
|
||||
|
@ -444,31 +441,31 @@ describe('add prepackaged rules schema', () => {
|
|||
version: 1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('allows references to be sent as valid', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
references: ['index-1'],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('immutable cannot be set in a pre-packaged rule', () => {
|
||||
const payload: AddPrepackagedRulesSchema & { immutable: boolean } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall & { immutable: boolean } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
immutable: true,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "immutable"']);
|
||||
|
@ -476,11 +473,11 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('rule_id is required', () => {
|
||||
const payload: AddPrepackagedRulesSchema = getAddPrepackagedRulesSchemaMock();
|
||||
const payload: PrebuiltRuleToInstall = getPrebuiltRuleMock();
|
||||
// @ts-expect-error
|
||||
delete payload.rule_id;
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -490,12 +487,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('references cannot be numbers', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'references'> & { references: number[] } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'references'> & { references: number[] } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
references: [5],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "references"']);
|
||||
|
@ -503,12 +500,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('indexes cannot be numbers', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'index'> & { index: number[] } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'index'> & { index: number[] } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
index: [5],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "index"']);
|
||||
|
@ -517,23 +514,23 @@ describe('add prepackaged rules schema', () => {
|
|||
|
||||
test('saved_query type can have filters with it', () => {
|
||||
const payload = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
filters: [],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('filters cannot be a string', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'filters'> & { filters: string } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'filters'> & { filters: string } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
filters: 'some string',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -544,11 +541,11 @@ describe('add prepackaged rules schema', () => {
|
|||
|
||||
test('language validates with kuery', () => {
|
||||
const payload = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
language: 'kuery',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -556,23 +553,23 @@ describe('add prepackaged rules schema', () => {
|
|||
|
||||
test('language validates with lucene', () => {
|
||||
const payload = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
language: 'lucene',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('language does not validate with something made up', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'language'> & { language: string } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'language'> & { language: string } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
language: 'something-made-up',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -582,12 +579,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('max_signals cannot be negative', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
max_signals: -1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -597,12 +594,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('max_signals cannot be zero', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
max_signals: 0,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "0" supplied to "max_signals"']);
|
||||
|
@ -610,36 +607,36 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('max_signals can be 1', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
max_signals: 1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can optionally send in an array of tags', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
tags: ['tag_1', 'tag_2'],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You cannot send in an array of tags that are numbers', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'tags'> & { tags: number[] } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'tags'> & { tags: number[] } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
tags: [0, 1, 2],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -651,10 +648,10 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot send in an array of threat that are missing "framework"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'threat'> & {
|
||||
threat: Array<Partial<Omit<AddPrepackagedRulesSchema['threat'], 'framework'>>>;
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'threat'> & {
|
||||
threat: Array<Partial<Omit<PrebuiltRuleToInstall['threat'], 'framework'>>>;
|
||||
} = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
threat: [
|
||||
{
|
||||
tactic: {
|
||||
|
@ -673,7 +670,7 @@ describe('add prepackaged rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -683,10 +680,10 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot send in an array of threat that are missing "tactic"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'threat'> & {
|
||||
threat: Array<Partial<Omit<AddPrepackagedRulesSchema['threat'], 'tactic'>>>;
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'threat'> & {
|
||||
threat: Array<Partial<Omit<PrebuiltRuleToInstall['threat'], 'tactic'>>>;
|
||||
} = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
threat: [
|
||||
{
|
||||
framework: 'fake',
|
||||
|
@ -701,7 +698,7 @@ describe('add prepackaged rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -711,10 +708,10 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can send in an array of threat that are missing "technique"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'threat'> & {
|
||||
threat: Array<Partial<Omit<AddPrepackagedRulesSchema['threat'], 'technique'>>>;
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'threat'> & {
|
||||
threat: Array<Partial<Omit<PrebuiltRuleToInstall['threat'], 'technique'>>>;
|
||||
} = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
threat: [
|
||||
{
|
||||
framework: 'fake',
|
||||
|
@ -727,33 +724,33 @@ describe('add prepackaged rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can optionally send in an array of false positives', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
false_positives: ['false_1', 'false_2'],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You cannot send in an array of false positives that are numbers', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'false_positives'> & {
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'false_positives'> & {
|
||||
false_positives: number[];
|
||||
} = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
false_positives: [5, 4],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -763,12 +760,12 @@ describe('add prepackaged rules schema', () => {
|
|||
expect(message.schema).toEqual({});
|
||||
});
|
||||
test('You cannot set the risk_score to 101', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
risk_score: 101,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -778,12 +775,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot set the risk_score to -1', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
risk_score: -1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "-1" supplied to "risk_score"']);
|
||||
|
@ -791,50 +788,50 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can set the risk_score to 0', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
risk_score: 0,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can set the risk_score to 100', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
risk_score: 100,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can set meta to any object you want', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
meta: {
|
||||
somethingMadeUp: { somethingElse: true },
|
||||
},
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You cannot create meta as a string', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'meta'> & { meta: string } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'meta'> & { meta: string } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
meta: 'should not work',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -844,25 +841,25 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('validates with timeline_id and timeline_title', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
timeline_id: 'timeline-id',
|
||||
timeline_title: 'timeline-title',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You cannot set the severity to a value other than low, medium, high, or critical', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'severity'> & { severity: string } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'severity'> & { severity: string } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
severity: 'junk',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "junk" supplied to "severity"']);
|
||||
|
@ -870,12 +867,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot send in an array of actions that are missing "group"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema['actions'], 'group'> = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall['actions'], 'group'> = {
|
||||
...getPrebuiltRuleMock(),
|
||||
actions: [{ id: 'id', action_type_id: 'action_type_id', params: {} }],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -885,12 +882,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot send in an array of actions that are missing "id"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema['actions'], 'id'> = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall['actions'], 'id'> = {
|
||||
...getPrebuiltRuleMock(),
|
||||
actions: [{ group: 'group', action_type_id: 'action_type_id', params: {} }],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -900,12 +897,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot send in an array of actions that are missing "action_type_id"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema['actions'], 'action_type_id'> = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall['actions'], 'action_type_id'> = {
|
||||
...getPrebuiltRuleMock(),
|
||||
actions: [{ group: 'group', id: 'id', params: {} }],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -915,12 +912,12 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot send in an array of actions that are missing "params"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema['actions'], 'params'> = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall['actions'], 'params'> = {
|
||||
...getPrebuiltRuleMock(),
|
||||
actions: [{ group: 'group', id: 'id', action_type_id: 'action_type_id' }],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -930,8 +927,8 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot send in an array of actions that are including "actionTypeId"', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema['actions'], 'actions'> = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall['actions'], 'actions'> = {
|
||||
...getPrebuiltRuleMock(),
|
||||
actions: [
|
||||
{
|
||||
group: 'group',
|
||||
|
@ -942,7 +939,7 @@ describe('add prepackaged rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -953,38 +950,38 @@ describe('add prepackaged rules schema', () => {
|
|||
|
||||
describe('note', () => {
|
||||
test('You can set note to a string', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
note: '# documentation markdown here',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can set note to an empty string', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
note: '',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('You cannot create note as an object', () => {
|
||||
const payload: Omit<AddPrepackagedRulesSchema, 'note'> & { note: {} } = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const payload: Omit<PrebuiltRuleToInstall, 'note'> & { note: {} } = {
|
||||
...getPrebuiltRuleMock(),
|
||||
note: {
|
||||
somethingHere: 'something else',
|
||||
},
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -994,7 +991,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, note] does validate', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -1009,7 +1006,7 @@ describe('add prepackaged rules schema', () => {
|
|||
version: 1,
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1018,7 +1015,7 @@ describe('add prepackaged rules schema', () => {
|
|||
|
||||
describe('exception_list', () => {
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filters, risk_score, note, version, and exceptions_list] does validate', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -1035,14 +1032,14 @@ describe('add prepackaged rules schema', () => {
|
|||
exceptions_list: getListArrayMock(),
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, note, version, and empty exceptions_list] does validate', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -1059,7 +1056,7 @@ describe('add prepackaged rules schema', () => {
|
|||
exceptions_list: [],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1083,7 +1080,7 @@ describe('add prepackaged rules schema', () => {
|
|||
exceptions_list: [{ id: 'uuid_here', namespace_type: 'not a namespace type' }],
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1095,7 +1092,7 @@ describe('add prepackaged rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filters, risk_score, note, version, and non-existent exceptions_list] does validate with empty exceptions_list', () => {
|
||||
const payload: AddPrepackagedRulesSchema = {
|
||||
const payload: PrebuiltRuleToInstall = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -1111,7 +1108,7 @@ describe('add prepackaged rules schema', () => {
|
|||
note: '# some markdown',
|
||||
};
|
||||
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1120,8 +1117,8 @@ describe('add prepackaged rules schema', () => {
|
|||
|
||||
describe('threat_mapping', () => {
|
||||
test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => {
|
||||
const payload = getAddPrepackagedThreatMatchRulesSchemaMock();
|
||||
const decoded = addPrepackagedRulesSchema.decode(payload);
|
||||
const payload = getPrebuiltThreatMatchRuleMock();
|
||||
const decoded = PrebuiltRuleToInstall.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
|
@ -6,23 +6,33 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { version } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
import { rule_id, RelatedIntegrationArray, RequiredFieldArray, SetupGuide } from '../common';
|
||||
import { baseCreateParams, createTypeSpecific } from './rule_schemas';
|
||||
import {
|
||||
RelatedIntegrationArray,
|
||||
RequiredFieldArray,
|
||||
SetupGuide,
|
||||
RuleSignatureId,
|
||||
RuleVersion,
|
||||
BaseCreateProps,
|
||||
TypeSpecificCreateProps,
|
||||
} from '../../rule_schema';
|
||||
|
||||
/**
|
||||
* Big differences between this schema and the createRulesSchema
|
||||
* - rule_id is required here
|
||||
* - version is a required field that must exist
|
||||
*/
|
||||
export const addPrepackagedRulesSchema = t.intersection([
|
||||
baseCreateParams,
|
||||
createTypeSpecific,
|
||||
// version is required in addPrepackagedRulesSchema, so this supercedes the defaultable
|
||||
export type PrebuiltRuleToInstall = t.TypeOf<typeof PrebuiltRuleToInstall>;
|
||||
export const PrebuiltRuleToInstall = t.intersection([
|
||||
BaseCreateProps,
|
||||
TypeSpecificCreateProps,
|
||||
// version is required in PrebuiltRuleToInstall, so this supercedes the defaultable
|
||||
// version in baseParams
|
||||
t.exact(t.type({ rule_id, version })),
|
||||
t.exact(
|
||||
t.type({
|
||||
rule_id: RuleSignatureId,
|
||||
version: RuleVersion,
|
||||
})
|
||||
),
|
||||
t.exact(
|
||||
t.partial({
|
||||
related_integrations: RelatedIntegrationArray,
|
||||
|
@ -31,4 +41,3 @@ export const addPrepackagedRulesSchema = t.intersection([
|
|||
})
|
||||
),
|
||||
]);
|
||||
export type AddPrepackagedRulesSchema = t.TypeOf<typeof addPrepackagedRulesSchema>;
|
|
@ -5,14 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AddPrepackagedRulesSchema } from './add_prepackaged_rules_schema';
|
||||
import { addPrepackagedRuleValidateTypeDependents } from './add_prepackaged_rules_type_dependents';
|
||||
import { getAddPrepackagedRulesSchemaMock } from './add_prepackaged_rules_schema.mock';
|
||||
import type { PrebuiltRuleToInstall } from './prebuilt_rule';
|
||||
import { addPrepackagedRuleValidateTypeDependents } from './prebuilt_rule_validate_type_dependents';
|
||||
import { getPrebuiltRuleMock } from './prebuilt_rule.mock';
|
||||
|
||||
describe('add_prepackaged_rules_type_dependents', () => {
|
||||
describe('addPrepackagedRuleValidateTypeDependents', () => {
|
||||
test('You cannot omit timeline_title when timeline_id is present', () => {
|
||||
const schema: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const schema: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
timeline_id: '123',
|
||||
};
|
||||
delete schema.timeline_title;
|
||||
|
@ -21,8 +21,8 @@ describe('add_prepackaged_rules_type_dependents', () => {
|
|||
});
|
||||
|
||||
test('You cannot have empty string for timeline_title when timeline_id is present', () => {
|
||||
const schema: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const schema: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
timeline_id: '123',
|
||||
timeline_title: '',
|
||||
};
|
||||
|
@ -31,8 +31,8 @@ describe('add_prepackaged_rules_type_dependents', () => {
|
|||
});
|
||||
|
||||
test('You cannot have timeline_title with an empty timeline_id', () => {
|
||||
const schema: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const schema: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
timeline_id: '',
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
|
@ -41,8 +41,8 @@ describe('add_prepackaged_rules_type_dependents', () => {
|
|||
});
|
||||
|
||||
test('You cannot have timeline_title without timeline_id', () => {
|
||||
const schema: AddPrepackagedRulesSchema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
const schema: PrebuiltRuleToInstall = {
|
||||
...getPrebuiltRuleMock(),
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
delete schema.timeline_id;
|
||||
|
@ -52,33 +52,33 @@ describe('add_prepackaged_rules_type_dependents', () => {
|
|||
|
||||
test('threshold.value is required and has to be bigger than 0 when type is threshold and validates with it', () => {
|
||||
const schema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
type: 'threshold',
|
||||
threshold: {
|
||||
field: '',
|
||||
value: -1,
|
||||
},
|
||||
};
|
||||
const errors = addPrepackagedRuleValidateTypeDependents(schema as AddPrepackagedRulesSchema);
|
||||
const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleToInstall);
|
||||
expect(errors).toEqual(['"threshold.value" has to be bigger than 0']);
|
||||
});
|
||||
|
||||
test('threshold.field should contain 3 items or less', () => {
|
||||
const schema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
type: 'threshold',
|
||||
threshold: {
|
||||
field: ['field-1', 'field-2', 'field-3', 'field-4'],
|
||||
value: 1,
|
||||
},
|
||||
};
|
||||
const errors = addPrepackagedRuleValidateTypeDependents(schema as AddPrepackagedRulesSchema);
|
||||
const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleToInstall);
|
||||
expect(errors).toEqual(['Number of fields must be 3 or less']);
|
||||
});
|
||||
|
||||
test('threshold.cardinality[0].field should not be in threshold.field', () => {
|
||||
const schema = {
|
||||
...getAddPrepackagedRulesSchemaMock(),
|
||||
...getPrebuiltRuleMock(),
|
||||
type: 'threshold',
|
||||
threshold: {
|
||||
field: ['field-1', 'field-2', 'field-3'],
|
||||
|
@ -91,7 +91,7 @@ describe('add_prepackaged_rules_type_dependents', () => {
|
|||
],
|
||||
},
|
||||
};
|
||||
const errors = addPrepackagedRuleValidateTypeDependents(schema as AddPrepackagedRulesSchema);
|
||||
const errors = addPrepackagedRuleValidateTypeDependents(schema as PrebuiltRuleToInstall);
|
||||
expect(errors).toEqual(['Cardinality of a field that is being aggregated on is always 1']);
|
||||
});
|
||||
});
|
|
@ -5,9 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AddPrepackagedRulesSchema } from './add_prepackaged_rules_schema';
|
||||
import type { PrebuiltRuleToInstall } from './prebuilt_rule';
|
||||
|
||||
export const validateTimelineId = (rule: AddPrepackagedRulesSchema): string[] => {
|
||||
export const addPrepackagedRuleValidateTypeDependents = (rule: PrebuiltRuleToInstall): string[] => {
|
||||
return [...validateTimelineId(rule), ...validateTimelineTitle(rule), ...validateThreshold(rule)];
|
||||
};
|
||||
|
||||
const validateTimelineId = (rule: PrebuiltRuleToInstall): string[] => {
|
||||
if (rule.timeline_id != null) {
|
||||
if (rule.timeline_title == null) {
|
||||
return ['when "timeline_id" exists, "timeline_title" must also exist'];
|
||||
|
@ -20,7 +24,7 @@ export const validateTimelineId = (rule: AddPrepackagedRulesSchema): string[] =>
|
|||
return [];
|
||||
};
|
||||
|
||||
export const validateTimelineTitle = (rule: AddPrepackagedRulesSchema): string[] => {
|
||||
const validateTimelineTitle = (rule: PrebuiltRuleToInstall): string[] => {
|
||||
if (rule.timeline_title != null) {
|
||||
if (rule.timeline_id == null) {
|
||||
return ['when "timeline_title" exists, "timeline_id" must also exist'];
|
||||
|
@ -33,7 +37,7 @@ export const validateTimelineTitle = (rule: AddPrepackagedRulesSchema): string[]
|
|||
return [];
|
||||
};
|
||||
|
||||
export const validateThreshold = (rule: AddPrepackagedRulesSchema): string[] => {
|
||||
const validateThreshold = (rule: PrebuiltRuleToInstall): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (rule.type === 'threshold') {
|
||||
if (!rule.threshold) {
|
||||
|
@ -55,9 +59,3 @@ export const validateThreshold = (rule: AddPrepackagedRulesSchema): string[] =>
|
|||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
export const addPrepackagedRuleValidateTypeDependents = (
|
||||
rule: AddPrepackagedRulesSchema
|
||||
): string[] => {
|
||||
return [...validateTimelineId(rule), ...validateTimelineTitle(rule), ...validateThreshold(rule)];
|
||||
};
|
|
@ -5,19 +5,47 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { createRuleExceptionsSchema } from './create_rule_exception_schema';
|
||||
import type { CreateRuleExceptionSchema } from './create_rule_exception_schema';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import { getCreateExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock';
|
||||
import {
|
||||
CreateRuleExceptionsRequestBody,
|
||||
CreateRuleExceptionsRequestParams,
|
||||
} from './request_schema';
|
||||
|
||||
describe('createRuleExceptionsSchema', () => {
|
||||
describe('CreateRuleExceptionsRequestParams', () => {
|
||||
test('empty objects do not validate', () => {
|
||||
const payload: CreateRuleExceptionSchema = {} as CreateRuleExceptionSchema;
|
||||
const payload: Partial<CreateRuleExceptionsRequestParams> = {};
|
||||
|
||||
const decoded = createRuleExceptionsSchema.decode(payload);
|
||||
const decoded = CreateRuleExceptionsRequestParams.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']);
|
||||
expect(message.schema).toEqual({});
|
||||
});
|
||||
|
||||
test('validates string for id', () => {
|
||||
const payload: Partial<CreateRuleExceptionsRequestParams> = {
|
||||
id: '4656dc92-5832-11ea-8e2d-0242ac130003',
|
||||
};
|
||||
|
||||
const decoded = CreateRuleExceptionsRequestParams.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual({
|
||||
id: '4656dc92-5832-11ea-8e2d-0242ac130003',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('CreateRuleExceptionsRequestBody', () => {
|
||||
test('empty objects do not validate', () => {
|
||||
const payload: CreateRuleExceptionsRequestBody = {} as CreateRuleExceptionsRequestBody;
|
||||
|
||||
const decoded = CreateRuleExceptionsRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -27,7 +55,7 @@ describe('createRuleExceptionsSchema', () => {
|
|||
});
|
||||
|
||||
test('items without list_id validate', () => {
|
||||
const payload: CreateRuleExceptionSchema = {
|
||||
const payload: CreateRuleExceptionsRequestBody = {
|
||||
items: [
|
||||
{
|
||||
description: 'Exception item for rule default exception list',
|
||||
|
@ -45,12 +73,12 @@ describe('createRuleExceptionsSchema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = createRuleExceptionsSchema.decode(payload);
|
||||
const decoded = CreateRuleExceptionsRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as CreateRuleExceptionSchema).items[0]).toEqual(
|
||||
expect((message.schema as CreateRuleExceptionsRequestBody).items[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
comments: [],
|
||||
description: 'Exception item for rule default exception list',
|
||||
|
@ -73,9 +101,9 @@ describe('createRuleExceptionsSchema', () => {
|
|||
test('items with list_id do not validate', () => {
|
||||
const payload = {
|
||||
items: [getCreateExceptionListItemSchemaMock()],
|
||||
} as unknown as CreateRuleExceptionSchema;
|
||||
} as unknown as CreateRuleExceptionsRequestBody;
|
||||
|
||||
const decoded = createRuleExceptionsSchema.decode(payload);
|
||||
const decoded = CreateRuleExceptionsRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -85,7 +113,7 @@ describe('createRuleExceptionsSchema', () => {
|
|||
});
|
||||
|
||||
test('made up parameters do not validate', () => {
|
||||
const payload: Partial<CreateRuleExceptionSchema> & { madeUp: string } = {
|
||||
const payload: Partial<CreateRuleExceptionsRequestBody> & { madeUp: string } = {
|
||||
items: [
|
||||
{
|
||||
description: 'Exception item for rule default exception list',
|
||||
|
@ -104,7 +132,7 @@ describe('createRuleExceptionsSchema', () => {
|
|||
madeUp: 'invalid value',
|
||||
};
|
||||
|
||||
const decoded = createRuleExceptionsSchema.decode(payload);
|
||||
const decoded = CreateRuleExceptionsRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "madeUp"']);
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
|
||||
import type { CreateRuleExceptionListItemSchemaDecoded } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { createRuleExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import type { RequiredKeepUndefined } from '@kbn/osquery-plugin/common/types';
|
||||
|
||||
import { RuleObjectId } from '../../../rule_schema';
|
||||
|
||||
/**
|
||||
* URL path parameters of the API route.
|
||||
*/
|
||||
export type CreateRuleExceptionsRequestParams = t.TypeOf<typeof CreateRuleExceptionsRequestParams>;
|
||||
export const CreateRuleExceptionsRequestParams = t.exact(
|
||||
t.type({
|
||||
id: RuleObjectId,
|
||||
})
|
||||
);
|
||||
|
||||
export type CreateRuleExceptionsRequestParamsDecoded = CreateRuleExceptionsRequestParams;
|
||||
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type CreateRuleExceptionsRequestBody = t.TypeOf<typeof CreateRuleExceptionsRequestBody>;
|
||||
export const CreateRuleExceptionsRequestBody = t.exact(
|
||||
t.type({
|
||||
items: t.array(createRuleExceptionListItemSchema),
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* This type is used after a decode since some things are defaults after a decode.
|
||||
*/
|
||||
export type CreateRuleExceptionsRequestBodyDecoded = Omit<
|
||||
RequiredKeepUndefined<CreateRuleExceptionsRequestBody>,
|
||||
'items'
|
||||
> & {
|
||||
items: CreateRuleExceptionListItemSchemaDecoded[];
|
||||
};
|
|
@ -6,10 +6,10 @@
|
|||
*/
|
||||
|
||||
import { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { findExceptionReferencesOnRuleSchema } from './find_exception_list_references_schema';
|
||||
import type { FindExceptionReferencesOnRuleSchema } from './find_exception_list_references_schema';
|
||||
import { findExceptionReferencesOnRuleSchema } from './request_schema';
|
||||
import type { FindExceptionReferencesOnRuleSchema } from './request_schema';
|
||||
|
||||
describe('find_exception_list_references_schema', () => {
|
||||
describe('Find exception list references schema', () => {
|
||||
test('validates all fields', () => {
|
||||
const payload: FindExceptionReferencesOnRuleSchema = {
|
||||
ids: 'abc,def',
|
|
@ -6,17 +6,17 @@
|
|||
*/
|
||||
|
||||
import { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock';
|
||||
import {
|
||||
exceptionListRuleReferencesSchema,
|
||||
rulesReferencedByExceptionListsSchema,
|
||||
} from './find_exception_list_references_schema';
|
||||
} from './response_schema';
|
||||
import type {
|
||||
ExceptionListRuleReferencesSchema,
|
||||
RulesReferencedByExceptionListsSchema,
|
||||
} from './find_exception_list_references_schema';
|
||||
import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock';
|
||||
} from './response_schema';
|
||||
|
||||
describe('find_exception_list_references_schema', () => {
|
||||
describe('Find exception list references response schema', () => {
|
||||
describe('exceptionListRuleReferencesSchema', () => {
|
||||
test('validates all fields', () => {
|
||||
const payload: ExceptionListRuleReferencesSchema = {
|
|
@ -6,16 +6,14 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { exceptionListSchema, listArray, list_id } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { rule_id, id, name } from '../common/schemas';
|
||||
import { RuleName, RuleObjectId, RuleSignatureId } from '../../../rule_schema';
|
||||
|
||||
export const ruleReferenceRuleInfoSchema = t.exact(
|
||||
t.type({
|
||||
name,
|
||||
id,
|
||||
rule_id,
|
||||
name: RuleName,
|
||||
id: RuleObjectId,
|
||||
rule_id: RuleSignatureId,
|
||||
exception_lists: listArray,
|
||||
})
|
||||
);
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 {
|
||||
DETECTION_ENGINE_RULES_URL as PUBLIC_RULES_URL,
|
||||
INTERNAL_DETECTION_ENGINE_URL as INTERNAL_URL,
|
||||
} from '../../../constants';
|
||||
|
||||
const INTERNAL_RULES_URL = `${INTERNAL_URL}/rules`;
|
||||
|
||||
export const CREATE_RULE_EXCEPTIONS_URL = `${PUBLIC_RULES_URL}/{id}/exceptions`;
|
||||
export const DETECTION_ENGINE_RULES_EXCEPTIONS_REFERENCE_URL =
|
||||
`${INTERNAL_RULES_URL}/exceptions/_find_references` as const;
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './api/create_rule_exceptions/request_schema';
|
||||
export * from './api/find_exception_references/request_schema';
|
||||
export * from './api/find_exception_references/response_schema';
|
||||
export * from './api/urls';
|
|
@ -5,16 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { BulkAction, BulkActionEditType } from './perform_bulk_action_schema';
|
||||
import type { PerformBulkActionSchema } from './perform_bulk_action_schema';
|
||||
import { BulkAction, BulkActionEditType } from './request_schema';
|
||||
import type { PerformBulkActionRequestBody } from './request_schema';
|
||||
|
||||
export const getPerformBulkActionSchemaMock = (): PerformBulkActionSchema => ({
|
||||
export const getPerformBulkActionSchemaMock = (): PerformBulkActionRequestBody => ({
|
||||
query: '',
|
||||
ids: undefined,
|
||||
action: BulkAction.disable,
|
||||
});
|
||||
|
||||
export const getPerformBulkActionEditSchemaMock = (): PerformBulkActionSchema => ({
|
||||
export const getPerformBulkActionEditSchemaMock = (): PerformBulkActionRequestBody => ({
|
||||
query: '',
|
||||
ids: undefined,
|
||||
action: BulkAction.edit,
|
|
@ -5,26 +5,21 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PerformBulkActionSchema } from './perform_bulk_action_schema';
|
||||
import {
|
||||
performBulkActionSchema,
|
||||
BulkAction,
|
||||
BulkActionEditType,
|
||||
} from './perform_bulk_action_schema';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { PerformBulkActionRequestBody, BulkAction, BulkActionEditType } from './request_schema';
|
||||
|
||||
const retrieveValidationMessage = (payload: unknown) => {
|
||||
const decoded = performBulkActionSchema.decode(payload);
|
||||
const decoded = PerformBulkActionRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
return foldLeftRight(checked);
|
||||
};
|
||||
|
||||
describe('perform_bulk_action_schema', () => {
|
||||
describe('Perform bulk action request schema', () => {
|
||||
describe('cases common to every bulk action', () => {
|
||||
// missing query means it will request for all rules
|
||||
test('valid request: missing query', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: undefined,
|
||||
action: BulkAction.enable,
|
||||
};
|
||||
|
@ -35,7 +30,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('invalid request: missing action', () => {
|
||||
const payload: Omit<PerformBulkActionSchema, 'action'> = {
|
||||
const payload: Omit<PerformBulkActionRequestBody, 'action'> = {
|
||||
query: 'name: test',
|
||||
};
|
||||
const message = retrieveValidationMessage(payload);
|
||||
|
@ -48,7 +43,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('invalid request: unknown action', () => {
|
||||
const payload: Omit<PerformBulkActionSchema, 'action'> & { action: 'unknown' } = {
|
||||
const payload: Omit<PerformBulkActionRequestBody, 'action'> & { action: 'unknown' } = {
|
||||
query: 'name: test',
|
||||
action: 'unknown',
|
||||
};
|
||||
|
@ -87,7 +82,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
|
||||
describe('bulk enable', () => {
|
||||
test('valid request', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.enable,
|
||||
};
|
||||
|
@ -99,7 +94,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
|
||||
describe('bulk disable', () => {
|
||||
test('valid request', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.disable,
|
||||
};
|
||||
|
@ -111,7 +106,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
|
||||
describe('bulk export', () => {
|
||||
test('valid request', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.export,
|
||||
};
|
||||
|
@ -123,7 +118,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
|
||||
describe('bulk delete', () => {
|
||||
test('valid request', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.delete,
|
||||
};
|
||||
|
@ -135,7 +130,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
|
||||
describe('bulk duplicate', () => {
|
||||
test('valid request', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.duplicate,
|
||||
};
|
||||
|
@ -271,7 +266,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('valid request: set_index_patterns edit action', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [{ type: BulkActionEditType.set_index_patterns, value: ['logs-*'] }],
|
||||
|
@ -284,7 +279,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('valid request: add_index_patterns edit action', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [{ type: BulkActionEditType.add_index_patterns, value: ['logs-*'] }],
|
||||
|
@ -297,7 +292,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('valid request: delete_index_patterns edit action', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
|
@ -356,7 +351,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('valid request: set_timeline edit action', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
|
@ -408,7 +403,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
} as PerformBulkActionSchema;
|
||||
} as PerformBulkActionRequestBody;
|
||||
|
||||
const message = retrieveValidationMessage(payload);
|
||||
|
||||
|
@ -434,7 +429,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
} as PerformBulkActionSchema;
|
||||
} as PerformBulkActionRequestBody;
|
||||
|
||||
const message = retrieveValidationMessage(payload);
|
||||
|
||||
|
@ -460,7 +455,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
} as PerformBulkActionSchema;
|
||||
} as PerformBulkActionRequestBody;
|
||||
|
||||
const message = retrieveValidationMessage(payload);
|
||||
|
||||
|
@ -475,7 +470,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('valid request: set_schedule edit action', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
|
@ -487,7 +482,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
} as PerformBulkActionSchema;
|
||||
} as PerformBulkActionRequestBody;
|
||||
|
||||
const message = retrieveValidationMessage(payload);
|
||||
|
||||
|
@ -590,7 +585,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('valid request: add_rule_actions edit action', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
|
@ -621,7 +616,7 @@ describe('perform_bulk_action_schema', () => {
|
|||
});
|
||||
|
||||
test('valid request: set_rule_actions edit action', () => {
|
||||
const payload: PerformBulkActionSchema = {
|
||||
const payload: PerformBulkActionRequestBody = {
|
||||
query: 'name: test',
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
|
@ -6,15 +6,21 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { NonEmptyArray, TimeDuration, enumeration } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
import { NonEmptyArray, TimeDuration } from '@kbn/securitysolution-io-ts-types';
|
||||
import {
|
||||
action_group as actionGroup,
|
||||
action_params as actionParams,
|
||||
action_id as actionId,
|
||||
RuleActionGroup,
|
||||
RuleActionId,
|
||||
RuleActionParams,
|
||||
} from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
|
||||
import { queryOrUndefined, tags, index, timeline_id, timeline_title } from '../common/schemas';
|
||||
import {
|
||||
IndexPatternArray,
|
||||
RuleQuery,
|
||||
RuleTagArray,
|
||||
TimelineTemplateId,
|
||||
TimelineTemplateTitle,
|
||||
} from '../../../../rule_schema';
|
||||
|
||||
export enum BulkAction {
|
||||
'enable' = 'enable',
|
||||
|
@ -25,8 +31,6 @@ export enum BulkAction {
|
|||
'edit' = 'edit',
|
||||
}
|
||||
|
||||
export const bulkAction = enumeration('BulkAction', BulkAction);
|
||||
|
||||
export enum BulkActionEditType {
|
||||
'add_tags' = 'add_tags',
|
||||
'delete_tags' = 'delete_tags',
|
||||
|
@ -40,7 +44,8 @@ export enum BulkActionEditType {
|
|||
'set_schedule' = 'set_schedule',
|
||||
}
|
||||
|
||||
export const throttleForBulkActions = t.union([
|
||||
export type ThrottleForBulkActions = t.TypeOf<typeof ThrottleForBulkActions>;
|
||||
export const ThrottleForBulkActions = t.union([
|
||||
t.literal('rule'),
|
||||
TimeDuration({
|
||||
allowedDurations: [
|
||||
|
@ -50,89 +55,82 @@ export const throttleForBulkActions = t.union([
|
|||
],
|
||||
}),
|
||||
]);
|
||||
export type ThrottleForBulkActions = t.TypeOf<typeof throttleForBulkActions>;
|
||||
|
||||
const bulkActionEditPayloadTags = t.type({
|
||||
type BulkActionEditPayloadTags = t.TypeOf<typeof BulkActionEditPayloadTags>;
|
||||
const BulkActionEditPayloadTags = t.type({
|
||||
type: t.union([
|
||||
t.literal(BulkActionEditType.add_tags),
|
||||
t.literal(BulkActionEditType.delete_tags),
|
||||
t.literal(BulkActionEditType.set_tags),
|
||||
]),
|
||||
value: tags,
|
||||
value: RuleTagArray,
|
||||
});
|
||||
|
||||
export type BulkActionEditPayloadTags = t.TypeOf<typeof bulkActionEditPayloadTags>;
|
||||
|
||||
const bulkActionEditPayloadIndexPatterns = t.intersection([
|
||||
type BulkActionEditPayloadIndexPatterns = t.TypeOf<typeof BulkActionEditPayloadIndexPatterns>;
|
||||
const BulkActionEditPayloadIndexPatterns = t.intersection([
|
||||
t.type({
|
||||
type: t.union([
|
||||
t.literal(BulkActionEditType.add_index_patterns),
|
||||
t.literal(BulkActionEditType.delete_index_patterns),
|
||||
t.literal(BulkActionEditType.set_index_patterns),
|
||||
]),
|
||||
value: index,
|
||||
value: IndexPatternArray,
|
||||
}),
|
||||
t.exact(t.partial({ overwrite_data_views: t.boolean })),
|
||||
]);
|
||||
|
||||
export type BulkActionEditPayloadIndexPatterns = t.TypeOf<
|
||||
typeof bulkActionEditPayloadIndexPatterns
|
||||
>;
|
||||
|
||||
const bulkActionEditPayloadTimeline = t.type({
|
||||
type BulkActionEditPayloadTimeline = t.TypeOf<typeof BulkActionEditPayloadTimeline>;
|
||||
const BulkActionEditPayloadTimeline = t.type({
|
||||
type: t.literal(BulkActionEditType.set_timeline),
|
||||
value: t.type({
|
||||
timeline_id,
|
||||
timeline_title,
|
||||
timeline_id: TimelineTemplateId,
|
||||
timeline_title: TimelineTemplateTitle,
|
||||
}),
|
||||
});
|
||||
|
||||
export type BulkActionEditPayloadTimeline = t.TypeOf<typeof bulkActionEditPayloadTimeline>;
|
||||
|
||||
/**
|
||||
* per rulesClient.bulkEdit rules actions operation contract (x-pack/plugins/alerting/server/rules_client/rules_client.ts)
|
||||
* normalized rule action object is expected (NormalizedAlertAction) as value for the edit operation
|
||||
*/
|
||||
const normalizedRuleAction = t.exact(
|
||||
type NormalizedRuleAction = t.TypeOf<typeof NormalizedRuleAction>;
|
||||
const NormalizedRuleAction = t.exact(
|
||||
t.type({
|
||||
group: actionGroup,
|
||||
id: actionId,
|
||||
params: actionParams,
|
||||
group: RuleActionGroup,
|
||||
id: RuleActionId,
|
||||
params: RuleActionParams,
|
||||
})
|
||||
);
|
||||
|
||||
const bulkActionEditPayloadRuleActions = t.type({
|
||||
export type BulkActionEditPayloadRuleActions = t.TypeOf<typeof BulkActionEditPayloadRuleActions>;
|
||||
export const BulkActionEditPayloadRuleActions = t.type({
|
||||
type: t.union([
|
||||
t.literal(BulkActionEditType.add_rule_actions),
|
||||
t.literal(BulkActionEditType.set_rule_actions),
|
||||
]),
|
||||
value: t.type({
|
||||
throttle: throttleForBulkActions,
|
||||
actions: t.array(normalizedRuleAction),
|
||||
throttle: ThrottleForBulkActions,
|
||||
actions: t.array(NormalizedRuleAction),
|
||||
}),
|
||||
});
|
||||
|
||||
export type BulkActionEditPayloadRuleActions = t.TypeOf<typeof bulkActionEditPayloadRuleActions>;
|
||||
|
||||
const bulkActionEditPayloadSchedule = t.type({
|
||||
type BulkActionEditPayloadSchedule = t.TypeOf<typeof BulkActionEditPayloadSchedule>;
|
||||
const BulkActionEditPayloadSchedule = t.type({
|
||||
type: t.literal(BulkActionEditType.set_schedule),
|
||||
value: t.type({
|
||||
interval: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }),
|
||||
lookback: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }),
|
||||
}),
|
||||
});
|
||||
export type BulkActionEditPayloadSchedule = t.TypeOf<typeof bulkActionEditPayloadSchedule>;
|
||||
|
||||
export const bulkActionEditPayload = t.union([
|
||||
bulkActionEditPayloadTags,
|
||||
bulkActionEditPayloadIndexPatterns,
|
||||
bulkActionEditPayloadTimeline,
|
||||
bulkActionEditPayloadRuleActions,
|
||||
bulkActionEditPayloadSchedule,
|
||||
export type BulkActionEditPayload = t.TypeOf<typeof BulkActionEditPayload>;
|
||||
export const BulkActionEditPayload = t.union([
|
||||
BulkActionEditPayloadTags,
|
||||
BulkActionEditPayloadIndexPatterns,
|
||||
BulkActionEditPayloadTimeline,
|
||||
BulkActionEditPayloadRuleActions,
|
||||
BulkActionEditPayloadSchedule,
|
||||
]);
|
||||
|
||||
export type BulkActionEditPayload = t.TypeOf<typeof bulkActionEditPayload>;
|
||||
|
||||
/**
|
||||
* actions that modify rules attributes
|
||||
*/
|
||||
|
@ -149,10 +147,14 @@ export type BulkActionEditForRuleParams =
|
|||
| BulkActionEditPayloadTimeline
|
||||
| BulkActionEditPayloadSchedule;
|
||||
|
||||
export const performBulkActionSchema = t.intersection([
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type PerformBulkActionRequestBody = t.TypeOf<typeof PerformBulkActionRequestBody>;
|
||||
export const PerformBulkActionRequestBody = t.intersection([
|
||||
t.exact(
|
||||
t.type({
|
||||
query: queryOrUndefined,
|
||||
query: t.union([RuleQuery, t.undefined]),
|
||||
})
|
||||
),
|
||||
t.exact(t.partial({ ids: NonEmptyArray(t.string) })),
|
||||
|
@ -171,16 +173,18 @@ export const performBulkActionSchema = t.intersection([
|
|||
t.exact(
|
||||
t.type({
|
||||
action: t.literal(BulkAction.edit),
|
||||
[BulkAction.edit]: NonEmptyArray(bulkActionEditPayload),
|
||||
[BulkAction.edit]: NonEmptyArray(BulkActionEditPayload),
|
||||
})
|
||||
),
|
||||
]),
|
||||
]);
|
||||
|
||||
export const performBulkActionQuerySchema = t.exact(
|
||||
/**
|
||||
* Query string parameters of the API route.
|
||||
*/
|
||||
export type PerformBulkActionRequestQuery = t.TypeOf<typeof PerformBulkActionRequestQuery>;
|
||||
export const PerformBulkActionRequestQuery = t.exact(
|
||||
t.partial({
|
||||
dry_run: t.union([t.literal('true'), t.literal('false')]),
|
||||
})
|
||||
);
|
||||
|
||||
export type PerformBulkActionSchema = t.TypeOf<typeof performBulkActionSchema>;
|
|
@ -5,19 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CreateRulesBulkSchema } from './create_rules_bulk_schema';
|
||||
import { createRulesBulkSchema } from './create_rules_bulk_schema';
|
||||
import { BulkCreateRulesRequestBody } from './request_schema';
|
||||
import { exactCheck, foldLeftRight, formatErrors } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { getCreateRulesSchemaMock } from './rule_schemas.mock';
|
||||
import { getCreateRulesSchemaMock } from '../../../../../rule_schema/mocks';
|
||||
|
||||
// only the basics of testing are here.
|
||||
// see: rule_schemas.test.ts for the bulk of the validation tests
|
||||
// this just wraps createRulesSchema in an array
|
||||
describe('create_rules_bulk_schema', () => {
|
||||
describe('Bulk create rules request schema', () => {
|
||||
test('can take an empty array and validate it', () => {
|
||||
const payload: CreateRulesBulkSchema = [];
|
||||
const payload: BulkCreateRulesRequestBody = [];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(output.errors).toEqual([]);
|
||||
|
@ -27,7 +26,7 @@ describe('create_rules_bulk_schema', () => {
|
|||
test('made up values do not validate for a single element', () => {
|
||||
const payload: Array<{ madeUp: string }> = [{ madeUp: 'hi' }];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toContain(
|
||||
|
@ -44,9 +43,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('single array element does validate', () => {
|
||||
const payload: CreateRulesBulkSchema = [getCreateRulesSchemaMock()];
|
||||
const payload: BulkCreateRulesRequestBody = [getCreateRulesSchemaMock()];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -54,9 +53,12 @@ describe('create_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('two array elements do validate', () => {
|
||||
const payload: CreateRulesBulkSchema = [getCreateRulesSchemaMock(), getCreateRulesSchemaMock()];
|
||||
const payload: BulkCreateRulesRequestBody = [
|
||||
getCreateRulesSchemaMock(),
|
||||
getCreateRulesSchemaMock(),
|
||||
];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -67,9 +69,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
const singleItem = getCreateRulesSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete singleItem.risk_score;
|
||||
const payload: CreateRulesBulkSchema = [singleItem];
|
||||
const payload: BulkCreateRulesRequestBody = [singleItem];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -83,9 +85,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
const secondItem = getCreateRulesSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete secondItem.risk_score;
|
||||
const payload: CreateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkCreateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -99,9 +101,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
const secondItem = getCreateRulesSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete singleItem.risk_score;
|
||||
const payload: CreateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkCreateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -117,9 +119,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
delete singleItem.risk_score;
|
||||
// @ts-expect-error
|
||||
delete secondItem.risk_score;
|
||||
const payload: CreateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkCreateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -134,9 +136,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
madeUpValue: 'something',
|
||||
};
|
||||
const secondItem = getCreateRulesSchemaMock();
|
||||
const payload: CreateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkCreateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['invalid keys "madeUpValue"']);
|
||||
|
@ -149,9 +151,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
...getCreateRulesSchemaMock(),
|
||||
madeUpValue: 'something',
|
||||
};
|
||||
const payload: CreateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkCreateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['invalid keys "madeUpValue"']);
|
||||
|
@ -167,9 +169,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
...getCreateRulesSchemaMock(),
|
||||
madeUpValue: 'something',
|
||||
};
|
||||
const payload: CreateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkCreateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['invalid keys "madeUpValue,madeUpValue"']);
|
||||
|
@ -180,7 +182,7 @@ describe('create_rules_bulk_schema', () => {
|
|||
const badSeverity = { ...getCreateRulesSchemaMock(), severity: 'madeup' };
|
||||
const payload = [badSeverity];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['Invalid value "madeup" supplied to "severity"']);
|
||||
|
@ -188,11 +190,11 @@ describe('create_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('You can set "note" to a string', () => {
|
||||
const payload: CreateRulesBulkSchema = [
|
||||
const payload: BulkCreateRulesRequestBody = [
|
||||
{ ...getCreateRulesSchemaMock(), note: '# test markdown' },
|
||||
];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -200,9 +202,9 @@ describe('create_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('You can set "note" to an empty string', () => {
|
||||
const payload: CreateRulesBulkSchema = [{ ...getCreateRulesSchemaMock(), note: '' }];
|
||||
const payload: BulkCreateRulesRequestBody = [{ ...getCreateRulesSchemaMock(), note: '' }];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -219,7 +221,7 @@ describe('create_rules_bulk_schema', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const decoded = createRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkCreateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { RuleCreateProps } from '../../../../../rule_schema';
|
||||
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type BulkCreateRulesRequestBody = t.TypeOf<typeof BulkCreateRulesRequestBody>;
|
||||
export const BulkCreateRulesRequestBody = t.array(RuleCreateProps);
|
|
@ -5,18 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { QueryRulesBulkSchema } from './query_rules_bulk_schema';
|
||||
import { queryRulesBulkSchema } from './query_rules_bulk_schema';
|
||||
import { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { BulkDeleteRulesRequestBody } from './request_schema';
|
||||
|
||||
// only the basics of testing are here.
|
||||
// see: query_rules_schema.test.ts for the bulk of the validation tests
|
||||
// this just wraps queryRulesSchema in an array
|
||||
describe('query_rules_bulk_schema', () => {
|
||||
describe('Bulk delete rules request schema', () => {
|
||||
test('can take an empty array and validate it', () => {
|
||||
const payload: QueryRulesBulkSchema = [];
|
||||
const payload: BulkDeleteRulesRequestBody = [];
|
||||
|
||||
const decoded = queryRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkDeleteRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -24,13 +23,13 @@ describe('query_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('non uuid being supplied to id does not validate', () => {
|
||||
const payload: QueryRulesBulkSchema = [
|
||||
const payload: BulkDeleteRulesRequestBody = [
|
||||
{
|
||||
id: '1',
|
||||
},
|
||||
];
|
||||
|
||||
const decoded = queryRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkDeleteRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['Invalid value "1" supplied to "id"']);
|
||||
|
@ -38,14 +37,14 @@ describe('query_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('both rule_id and id being supplied do validate', () => {
|
||||
const payload: QueryRulesBulkSchema = [
|
||||
const payload: BulkDeleteRulesRequestBody = [
|
||||
{
|
||||
rule_id: '1',
|
||||
id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f',
|
||||
},
|
||||
];
|
||||
|
||||
const decoded = queryRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkDeleteRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -53,12 +52,12 @@ describe('query_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('only id validates with two elements', () => {
|
||||
const payload: QueryRulesBulkSchema = [
|
||||
const payload: BulkDeleteRulesRequestBody = [
|
||||
{ id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f' },
|
||||
{ id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f' },
|
||||
];
|
||||
|
||||
const decoded = queryRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkDeleteRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -66,9 +65,11 @@ describe('query_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('only rule_id validates', () => {
|
||||
const payload: QueryRulesBulkSchema = [{ rule_id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f' }];
|
||||
const payload: BulkDeleteRulesRequestBody = [
|
||||
{ rule_id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f' },
|
||||
];
|
||||
|
||||
const decoded = queryRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkDeleteRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -76,12 +77,12 @@ describe('query_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('only rule_id validates with two elements', () => {
|
||||
const payload: QueryRulesBulkSchema = [
|
||||
const payload: BulkDeleteRulesRequestBody = [
|
||||
{ rule_id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f' },
|
||||
{ rule_id: '2' },
|
||||
];
|
||||
|
||||
const decoded = queryRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkDeleteRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -89,12 +90,12 @@ describe('query_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('both id and rule_id validates with two separate elements', () => {
|
||||
const payload: QueryRulesBulkSchema = [
|
||||
const payload: BulkDeleteRulesRequestBody = [
|
||||
{ id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f' },
|
||||
{ rule_id: '2' },
|
||||
];
|
||||
|
||||
const decoded = queryRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkDeleteRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { QueryRuleByIds } from '../../crud/read_rule/query_rule_by_ids';
|
||||
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type BulkDeleteRulesRequestBody = t.TypeOf<typeof BulkDeleteRulesRequestBody>;
|
||||
export const BulkDeleteRulesRequestBody = t.array(QueryRuleByIds);
|
|
@ -5,19 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PatchRulesBulkSchema } from './patch_rules_bulk_schema';
|
||||
import { patchRulesBulkSchema } from './patch_rules_bulk_schema';
|
||||
import { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils';
|
||||
import type { PatchRulesSchema } from './patch_rules_schema';
|
||||
import type { PatchRuleRequestBody } from '../../crud/patch_rule/request_schema';
|
||||
import { BulkPatchRulesRequestBody } from './request_schema';
|
||||
|
||||
// only the basics of testing are here.
|
||||
// see: patch_rules_schema.test.ts for the bulk of the validation tests
|
||||
// this just wraps patchRulesSchema in an array
|
||||
describe('patch_rules_bulk_schema', () => {
|
||||
describe('Bulk patch rules request schema', () => {
|
||||
test('can take an empty array and validate it', () => {
|
||||
const payload: PatchRulesBulkSchema = [];
|
||||
const payload: BulkPatchRulesRequestBody = [];
|
||||
|
||||
const decoded = patchRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkPatchRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(output.errors).toEqual([]);
|
||||
|
@ -25,9 +24,9 @@ describe('patch_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('single array of [id] does validate', () => {
|
||||
const payload: PatchRulesBulkSchema = [{ id: '4125761e-51da-4de9-a0c8-42824f532ddb' }];
|
||||
const payload: BulkPatchRulesRequestBody = [{ id: '4125761e-51da-4de9-a0c8-42824f532ddb' }];
|
||||
|
||||
const decoded = patchRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkPatchRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -35,12 +34,12 @@ describe('patch_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('two arrays of [id] validate', () => {
|
||||
const payload: PatchRulesBulkSchema = [
|
||||
const payload: BulkPatchRulesRequestBody = [
|
||||
{ id: '4125761e-51da-4de9-a0c8-42824f532ddb' },
|
||||
{ id: '192f403d-b285-4251-9e8b-785fcfcf22e8' },
|
||||
];
|
||||
|
||||
const decoded = patchRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkPatchRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -48,12 +47,12 @@ describe('patch_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('can set "note" to be a string', () => {
|
||||
const payload: PatchRulesBulkSchema = [
|
||||
const payload: BulkPatchRulesRequestBody = [
|
||||
{ id: '4125761e-51da-4de9-a0c8-42824f532ddb' },
|
||||
{ note: 'hi' },
|
||||
];
|
||||
|
||||
const decoded = patchRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkPatchRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -61,12 +60,12 @@ describe('patch_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('can set "note" to be an empty string', () => {
|
||||
const payload: PatchRulesBulkSchema = [
|
||||
const payload: BulkPatchRulesRequestBody = [
|
||||
{ id: '4125761e-51da-4de9-a0c8-42824f532ddb' },
|
||||
{ note: '' },
|
||||
];
|
||||
|
||||
const decoded = patchRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkPatchRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -74,12 +73,12 @@ describe('patch_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('cannot set "note" to be anything other than a string', () => {
|
||||
const payload: Array<Omit<PatchRulesSchema, 'note'> & { note?: object }> = [
|
||||
const payload: Array<Omit<PatchRuleRequestBody, 'note'> & { note?: object }> = [
|
||||
{ id: '4125761e-51da-4de9-a0c8-42824f532ddb' },
|
||||
{ note: { someprop: 'some value here' } },
|
||||
];
|
||||
|
||||
const decoded = patchRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkPatchRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { PatchRuleRequestBody } from '../../crud/patch_rule/request_schema';
|
||||
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type BulkPatchRulesRequestBody = t.TypeOf<typeof BulkPatchRulesRequestBody>;
|
||||
export const BulkPatchRulesRequestBody = t.array(PatchRuleRequestBody);
|
|
@ -5,20 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { UpdateRulesBulkSchema } from './update_rules_bulk_schema';
|
||||
import { updateRulesBulkSchema } from './update_rules_bulk_schema';
|
||||
import { exactCheck, formatErrors, foldLeftRight } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { getUpdateRulesSchemaMock } from './rule_schemas.mock';
|
||||
import type { UpdateRulesSchema } from './rule_schemas';
|
||||
import type { RuleUpdateProps } from '../../../../../rule_schema';
|
||||
import { getUpdateRulesSchemaMock } from '../../../../../rule_schema/mocks';
|
||||
import { BulkUpdateRulesRequestBody } from './request_schema';
|
||||
|
||||
// only the basics of testing are here.
|
||||
// see: update_rules_schema.test.ts for the bulk of the validation tests
|
||||
// this just wraps updateRulesSchema in an array
|
||||
describe('update_rules_bulk_schema', () => {
|
||||
describe('Bulk update rules request schema', () => {
|
||||
test('can take an empty array and validate it', () => {
|
||||
const payload: UpdateRulesBulkSchema = [];
|
||||
const payload: BulkUpdateRulesRequestBody = [];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(output.errors).toEqual([]);
|
||||
|
@ -28,7 +27,7 @@ describe('update_rules_bulk_schema', () => {
|
|||
test('made up values do not validate for a single element', () => {
|
||||
const payload: Array<{ madeUp: string }> = [{ madeUp: 'hi' }];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toContain(
|
||||
|
@ -45,9 +44,9 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('single array element does validate', () => {
|
||||
const payload: UpdateRulesBulkSchema = [getUpdateRulesSchemaMock()];
|
||||
const payload: BulkUpdateRulesRequestBody = [getUpdateRulesSchemaMock()];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -55,9 +54,12 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('two array elements do validate', () => {
|
||||
const payload: UpdateRulesBulkSchema = [getUpdateRulesSchemaMock(), getUpdateRulesSchemaMock()];
|
||||
const payload: BulkUpdateRulesRequestBody = [
|
||||
getUpdateRulesSchemaMock(),
|
||||
getUpdateRulesSchemaMock(),
|
||||
];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -68,9 +70,9 @@ describe('update_rules_bulk_schema', () => {
|
|||
const singleItem = getUpdateRulesSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete singleItem.risk_score;
|
||||
const payload: UpdateRulesBulkSchema = [singleItem];
|
||||
const payload: BulkUpdateRulesRequestBody = [singleItem];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -84,9 +86,9 @@ describe('update_rules_bulk_schema', () => {
|
|||
const secondItem = getUpdateRulesSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete secondItem.risk_score;
|
||||
const payload: UpdateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkUpdateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -100,9 +102,9 @@ describe('update_rules_bulk_schema', () => {
|
|||
const secondItem = getUpdateRulesSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete singleItem.risk_score;
|
||||
const payload: UpdateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkUpdateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -118,9 +120,9 @@ describe('update_rules_bulk_schema', () => {
|
|||
delete singleItem.risk_score;
|
||||
// @ts-expect-error
|
||||
delete secondItem.risk_score;
|
||||
const payload: UpdateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkUpdateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
||||
|
@ -130,14 +132,14 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('two array elements where the first is invalid (extra key and value) but the second is valid will not validate', () => {
|
||||
const singleItem: UpdateRulesSchema & { madeUpValue: string } = {
|
||||
const singleItem: RuleUpdateProps & { madeUpValue: string } = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
madeUpValue: 'something',
|
||||
};
|
||||
const secondItem = getUpdateRulesSchemaMock();
|
||||
const payload = [singleItem, secondItem];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['invalid keys "madeUpValue"']);
|
||||
|
@ -145,14 +147,14 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('two array elements where the second is invalid (extra key and value) but the first is valid will not validate', () => {
|
||||
const singleItem: UpdateRulesSchema = getUpdateRulesSchemaMock();
|
||||
const secondItem: UpdateRulesSchema & { madeUpValue: string } = {
|
||||
const singleItem: RuleUpdateProps = getUpdateRulesSchemaMock();
|
||||
const secondItem: RuleUpdateProps & { madeUpValue: string } = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
madeUpValue: 'something',
|
||||
};
|
||||
const payload: UpdateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkUpdateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['invalid keys "madeUpValue"']);
|
||||
|
@ -160,17 +162,17 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('two array elements where both are invalid (extra key and value) will not validate', () => {
|
||||
const singleItem: UpdateRulesSchema & { madeUpValue: string } = {
|
||||
const singleItem: RuleUpdateProps & { madeUpValue: string } = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
madeUpValue: 'something',
|
||||
};
|
||||
const secondItem: UpdateRulesSchema & { madeUpValue: string } = {
|
||||
const secondItem: RuleUpdateProps & { madeUpValue: string } = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
madeUpValue: 'something',
|
||||
};
|
||||
const payload: UpdateRulesBulkSchema = [singleItem, secondItem];
|
||||
const payload: BulkUpdateRulesRequestBody = [singleItem, secondItem];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['invalid keys "madeUpValue,madeUpValue"']);
|
||||
|
@ -181,7 +183,7 @@ describe('update_rules_bulk_schema', () => {
|
|||
const badSeverity = { ...getUpdateRulesSchemaMock(), severity: 'madeup' };
|
||||
const payload = [badSeverity];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual(['Invalid value "madeup" supplied to "severity"']);
|
||||
|
@ -189,11 +191,11 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('You can set "namespace" to a string', () => {
|
||||
const payload: UpdateRulesBulkSchema = [
|
||||
const payload: BulkUpdateRulesRequestBody = [
|
||||
{ ...getUpdateRulesSchemaMock(), namespace: 'a namespace' },
|
||||
];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -201,11 +203,11 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('You can set "note" to a string', () => {
|
||||
const payload: UpdateRulesBulkSchema = [
|
||||
const payload: BulkUpdateRulesRequestBody = [
|
||||
{ ...getUpdateRulesSchemaMock(), note: '# test markdown' },
|
||||
];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -213,9 +215,9 @@ describe('update_rules_bulk_schema', () => {
|
|||
});
|
||||
|
||||
test('You can set "note" to an empty string', () => {
|
||||
const payload: UpdateRulesBulkSchema = [{ ...getUpdateRulesSchemaMock(), note: '' }];
|
||||
const payload: BulkUpdateRulesRequestBody = [{ ...getUpdateRulesSchemaMock(), note: '' }];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([]);
|
||||
|
@ -232,7 +234,7 @@ describe('update_rules_bulk_schema', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const decoded = updateRulesBulkSchema.decode(payload);
|
||||
const decoded = BulkUpdateRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const output = foldLeftRight(checked);
|
||||
expect(formatErrors(output.errors)).toEqual([
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { RuleUpdateProps } from '../../../../../rule_schema';
|
||||
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type BulkUpdateRulesRequestBody = t.TypeOf<typeof BulkUpdateRulesRequestBody>;
|
||||
export const BulkUpdateRulesRequestBody = t.array(RuleUpdateProps);
|
|
@ -7,20 +7,19 @@
|
|||
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
|
||||
import type { RulesBulkSchema } from './rules_bulk_schema';
|
||||
import { rulesBulkSchema } from './rules_bulk_schema';
|
||||
import type { ErrorSchema } from './error_schema';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import { getRulesSchemaMock } from './rules_schema.mocks';
|
||||
import { getErrorSchemaMock } from './error_schema.mocks';
|
||||
import type { FullResponseSchema } from '../request';
|
||||
import type { RuleResponse } from '../../../../rule_schema';
|
||||
import { getRulesSchemaMock } from '../../../../rule_schema/mocks';
|
||||
import type { ErrorSchema } from '../../../../schemas/response/error_schema';
|
||||
import { getErrorSchemaMock } from '../../../../schemas/response/error_schema.mocks';
|
||||
|
||||
describe('prepackaged_rule_schema', () => {
|
||||
import { BulkCrudRulesResponse } from './response_schema';
|
||||
|
||||
describe('Bulk CRUD rules response schema', () => {
|
||||
test('it should validate a regular message and and error together with a uuid', () => {
|
||||
const payload: RulesBulkSchema = [getRulesSchemaMock(), getErrorSchemaMock()];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [getRulesSchemaMock(), getErrorSchemaMock()];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -29,8 +28,8 @@ describe('prepackaged_rule_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate a regular message and and error together when the error has a non UUID', () => {
|
||||
const payload: RulesBulkSchema = [getRulesSchemaMock(), getErrorSchemaMock('fake id')];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [getRulesSchemaMock(), getErrorSchemaMock('fake id')];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -39,8 +38,8 @@ describe('prepackaged_rule_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate an error', () => {
|
||||
const payload: RulesBulkSchema = [getErrorSchemaMock('fake id')];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [getErrorSchemaMock('fake id')];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -52,8 +51,8 @@ describe('prepackaged_rule_schema', () => {
|
|||
const rule = getRulesSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete rule.name;
|
||||
const payload: RulesBulkSchema = [rule];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [rule];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -68,8 +67,8 @@ describe('prepackaged_rule_schema', () => {
|
|||
const error = getErrorSchemaMock('fake id');
|
||||
// @ts-expect-error
|
||||
delete error.error;
|
||||
const payload: RulesBulkSchema = [error];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [error];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -80,10 +79,10 @@ describe('prepackaged_rule_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a type of "query" when it has extra data', () => {
|
||||
const rule: FullResponseSchema & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
const rule: RuleResponse & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
rule.invalid_extra_data = 'invalid_extra_data';
|
||||
const payload: RulesBulkSchema = [rule];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [rule];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -92,10 +91,10 @@ describe('prepackaged_rule_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a type of "query" when it has extra data next to a valid error', () => {
|
||||
const rule: FullResponseSchema & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
const rule: RuleResponse & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
rule.invalid_extra_data = 'invalid_extra_data';
|
||||
const payload: RulesBulkSchema = [getErrorSchemaMock(), rule];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [getErrorSchemaMock(), rule];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -107,8 +106,8 @@ describe('prepackaged_rule_schema', () => {
|
|||
type InvalidError = ErrorSchema & { invalid_extra_data?: string };
|
||||
const error: InvalidError = getErrorSchemaMock();
|
||||
error.invalid_extra_data = 'invalid';
|
||||
const payload: RulesBulkSchema = [error];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [error];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -120,8 +119,8 @@ describe('prepackaged_rule_schema', () => {
|
|||
type InvalidError = ErrorSchema & { invalid_extra_data?: string };
|
||||
const error: InvalidError = getErrorSchemaMock();
|
||||
error.invalid_extra_data = 'invalid';
|
||||
const payload: RulesBulkSchema = [getRulesSchemaMock(), error];
|
||||
const decoded = rulesBulkSchema.decode(payload);
|
||||
const payload: BulkCrudRulesResponse = [getRulesSchemaMock(), error];
|
||||
const decoded = BulkCrudRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
|
||||
import { RuleResponse } from '../../../../rule_schema';
|
||||
import { errorSchema } from '../../../../schemas/response/error_schema';
|
||||
|
||||
export type BulkCrudRulesResponse = t.TypeOf<typeof BulkCrudRulesResponse>;
|
||||
export const BulkCrudRulesResponse = t.array(t.union([RuleResponse, errorSchema]));
|
|
@ -5,78 +5,81 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getCreateRulesSchemaMock, getCreateThreatMatchRulesSchemaMock } from './rule_schemas.mock';
|
||||
import type { CreateRulesSchema } from './rule_schemas';
|
||||
import { createRuleValidateTypeDependents } from './create_rules_type_dependents';
|
||||
import type { RuleCreateProps } from '../../../../../rule_schema';
|
||||
import {
|
||||
getCreateRulesSchemaMock,
|
||||
getCreateThreatMatchRulesSchemaMock,
|
||||
} from '../../../../../rule_schema/mocks';
|
||||
import { validateCreateRuleProps } from './request_schema_validation';
|
||||
|
||||
describe('create_rules_type_dependents', () => {
|
||||
describe('Create rule request schema, additional validation', () => {
|
||||
test('You cannot omit timeline_title when timeline_id is present', () => {
|
||||
const schema: CreateRulesSchema = {
|
||||
const schema: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
};
|
||||
delete schema.timeline_title;
|
||||
const errors = createRuleValidateTypeDependents(schema);
|
||||
const errors = validateCreateRuleProps(schema);
|
||||
expect(errors).toEqual(['when "timeline_id" exists, "timeline_title" must also exist']);
|
||||
});
|
||||
|
||||
test('You cannot have empty string for timeline_title when timeline_id is present', () => {
|
||||
const schema: CreateRulesSchema = {
|
||||
const schema: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
timeline_title: '',
|
||||
};
|
||||
const errors = createRuleValidateTypeDependents(schema);
|
||||
const errors = validateCreateRuleProps(schema);
|
||||
expect(errors).toEqual(['"timeline_title" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title with an empty timeline_id', () => {
|
||||
const schema: CreateRulesSchema = {
|
||||
const schema: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
timeline_id: '',
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
const errors = createRuleValidateTypeDependents(schema);
|
||||
const errors = validateCreateRuleProps(schema);
|
||||
expect(errors).toEqual(['"timeline_id" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title without timeline_id', () => {
|
||||
const schema: CreateRulesSchema = {
|
||||
const schema: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
delete schema.timeline_id;
|
||||
const errors = createRuleValidateTypeDependents(schema);
|
||||
const errors = validateCreateRuleProps(schema);
|
||||
expect(errors).toEqual(['when "timeline_title" exists, "timeline_id" must also exist']);
|
||||
});
|
||||
|
||||
test('validates that both "items_per_search" and "concurrent_searches" works when together', () => {
|
||||
const schema: CreateRulesSchema = {
|
||||
const schema: RuleCreateProps = {
|
||||
...getCreateThreatMatchRulesSchemaMock(),
|
||||
concurrent_searches: 10,
|
||||
items_per_search: 10,
|
||||
};
|
||||
const errors = createRuleValidateTypeDependents(schema);
|
||||
const errors = validateCreateRuleProps(schema);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test('does NOT validate when only "items_per_search" is present', () => {
|
||||
const schema: CreateRulesSchema = {
|
||||
const schema: RuleCreateProps = {
|
||||
...getCreateThreatMatchRulesSchemaMock(),
|
||||
items_per_search: 10,
|
||||
};
|
||||
const errors = createRuleValidateTypeDependents(schema);
|
||||
const errors = validateCreateRuleProps(schema);
|
||||
expect(errors).toEqual([
|
||||
'when "items_per_search" exists, "concurrent_searches" must also exist',
|
||||
]);
|
||||
});
|
||||
|
||||
test('does NOT validate when only "concurrent_searches" is present', () => {
|
||||
const schema: CreateRulesSchema = {
|
||||
const schema: RuleCreateProps = {
|
||||
...getCreateThreatMatchRulesSchemaMock(),
|
||||
concurrent_searches: 10,
|
||||
};
|
||||
const errors = createRuleValidateTypeDependents(schema);
|
||||
const errors = validateCreateRuleProps(schema);
|
||||
expect(errors).toEqual([
|
||||
'when "concurrent_searches" exists, "items_per_search" must also exist',
|
||||
]);
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 { RuleCreateProps } from '../../../../../rule_schema';
|
||||
|
||||
/**
|
||||
* Additional validation that is implemented outside of the schema itself.
|
||||
*/
|
||||
export const validateCreateRuleProps = (props: RuleCreateProps): string[] => {
|
||||
return [
|
||||
...validateTimelineId(props),
|
||||
...validateTimelineTitle(props),
|
||||
...validateThreatMapping(props),
|
||||
...validateThreshold(props),
|
||||
];
|
||||
};
|
||||
|
||||
const validateTimelineId = (props: RuleCreateProps): string[] => {
|
||||
if (props.timeline_id != null) {
|
||||
if (props.timeline_title == null) {
|
||||
return ['when "timeline_id" exists, "timeline_title" must also exist'];
|
||||
} else if (props.timeline_id === '') {
|
||||
return ['"timeline_id" cannot be an empty string'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const validateTimelineTitle = (props: RuleCreateProps): string[] => {
|
||||
if (props.timeline_title != null) {
|
||||
if (props.timeline_id == null) {
|
||||
return ['when "timeline_title" exists, "timeline_id" must also exist'];
|
||||
} else if (props.timeline_title === '') {
|
||||
return ['"timeline_title" cannot be an empty string'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const validateThreatMapping = (props: RuleCreateProps): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (props.type === 'threat_match') {
|
||||
if (props.concurrent_searches != null && props.items_per_search == null) {
|
||||
errors.push('when "concurrent_searches" exists, "items_per_search" must also exist');
|
||||
}
|
||||
if (props.concurrent_searches == null && props.items_per_search != null) {
|
||||
errors.push('when "items_per_search" exists, "concurrent_searches" must also exist');
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
const validateThreshold = (props: RuleCreateProps): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (props.type === 'threshold') {
|
||||
if (!props.threshold) {
|
||||
errors.push('when "type" is "threshold", "threshold" is required');
|
||||
} else {
|
||||
if (
|
||||
props.threshold.cardinality?.length &&
|
||||
props.threshold.field.includes(props.threshold.cardinality[0].field)
|
||||
) {
|
||||
errors.push('Cardinality of a field that is being aggregated on is always 1');
|
||||
}
|
||||
if (Array.isArray(props.threshold.field) && props.threshold.field.length > 3) {
|
||||
errors.push('Number of fields must be 3 or less');
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
};
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PatchRulesSchema, ThresholdPatchSchema } from './patch_rules_schema';
|
||||
import type { PatchRuleRequestBody, ThresholdPatchRuleRequestBody } from './request_schema';
|
||||
|
||||
export const getPatchRulesSchemaMock = (): PatchRulesSchema => ({
|
||||
export const getPatchRulesSchemaMock = (): PatchRuleRequestBody => ({
|
||||
description: 'some description',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
|
@ -18,7 +18,7 @@ export const getPatchRulesSchemaMock = (): PatchRulesSchema => ({
|
|||
rule_id: 'rule-1',
|
||||
});
|
||||
|
||||
export const getPatchThresholdRulesSchemaMock = (): ThresholdPatchSchema => ({
|
||||
export const getPatchThresholdRulesSchemaMock = (): ThresholdPatchRuleRequestBody => ({
|
||||
description: 'some description',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 { RulePatchProps, ThresholdRulePatchProps } from '../../../../../rule_schema';
|
||||
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
* All of the patch elements should default to undefined if not set.
|
||||
*/
|
||||
export type PatchRuleRequestBody = RulePatchProps;
|
||||
export const PatchRuleRequestBody = RulePatchProps;
|
||||
|
||||
export type ThresholdPatchRuleRequestBody = ThresholdRulePatchProps;
|
||||
export const ThresholdPatchRuleRequestBody = ThresholdRulePatchProps;
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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 { PatchRuleRequestBody, ThresholdPatchRuleRequestBody } from './request_schema';
|
||||
import { getPatchRulesSchemaMock, getPatchThresholdRulesSchemaMock } from './request_schema.mock';
|
||||
import { validatePatchRuleRequestBody } from './request_schema_validation';
|
||||
|
||||
describe('Patch rule request schema, additional validation', () => {
|
||||
describe('validatePatchRuleRequestBody', () => {
|
||||
test('You cannot omit timeline_title when timeline_id is present', () => {
|
||||
const schema: PatchRuleRequestBody = {
|
||||
...getPatchRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
};
|
||||
delete schema.timeline_title;
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['when "timeline_id" exists, "timeline_title" must also exist']);
|
||||
});
|
||||
|
||||
test('You cannot have empty string for timeline_title when timeline_id is present', () => {
|
||||
const schema: PatchRuleRequestBody = {
|
||||
...getPatchRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
timeline_title: '',
|
||||
};
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['"timeline_title" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title with an empty timeline_id', () => {
|
||||
const schema: PatchRuleRequestBody = {
|
||||
...getPatchRulesSchemaMock(),
|
||||
timeline_id: '',
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['"timeline_id" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title without timeline_id', () => {
|
||||
const schema: PatchRuleRequestBody = {
|
||||
...getPatchRulesSchemaMock(),
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
delete schema.timeline_id;
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['when "timeline_title" exists, "timeline_id" must also exist']);
|
||||
});
|
||||
|
||||
test('You cannot have both an id and a rule_id', () => {
|
||||
const schema: PatchRuleRequestBody = {
|
||||
...getPatchRulesSchemaMock(),
|
||||
id: 'some-id',
|
||||
rule_id: 'some-rule-id',
|
||||
};
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['both "id" and "rule_id" cannot exist, choose one or the other']);
|
||||
});
|
||||
|
||||
test('You must set either an id or a rule_id', () => {
|
||||
const schema: PatchRuleRequestBody = {
|
||||
...getPatchRulesSchemaMock(),
|
||||
};
|
||||
delete schema.id;
|
||||
delete schema.rule_id;
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['either "id" or "rule_id" must be set']);
|
||||
});
|
||||
|
||||
test('threshold.value is required and has to be bigger than 0 when type is threshold and validates with it', () => {
|
||||
const schema: ThresholdPatchRuleRequestBody = {
|
||||
...getPatchThresholdRulesSchemaMock(),
|
||||
threshold: {
|
||||
field: '',
|
||||
value: -1,
|
||||
},
|
||||
};
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['"threshold.value" has to be bigger than 0']);
|
||||
});
|
||||
|
||||
test('threshold.field should contain 3 items or less', () => {
|
||||
const schema: ThresholdPatchRuleRequestBody = {
|
||||
...getPatchThresholdRulesSchemaMock(),
|
||||
threshold: {
|
||||
field: ['field-1', 'field-2', 'field-3', 'field-4'],
|
||||
value: 1,
|
||||
},
|
||||
};
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['Number of fields must be 3 or less']);
|
||||
});
|
||||
|
||||
test('threshold.cardinality[0].field should not be in threshold.field', () => {
|
||||
const schema: ThresholdPatchRuleRequestBody = {
|
||||
...getPatchThresholdRulesSchemaMock(),
|
||||
threshold: {
|
||||
field: ['field-1', 'field-2', 'field-3'],
|
||||
value: 1,
|
||||
cardinality: [
|
||||
{
|
||||
field: 'field-1',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const errors = validatePatchRuleRequestBody(schema);
|
||||
expect(errors).toEqual(['Cardinality of a field that is being aggregated on is always 1']);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,9 +5,31 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PatchRulesSchema } from './patch_rules_schema';
|
||||
import type { PatchRuleRequestBody } from './request_schema';
|
||||
|
||||
export const validateTimelineId = (rule: PatchRulesSchema): string[] => {
|
||||
/**
|
||||
* Additional validation that is implemented outside of the schema itself.
|
||||
*/
|
||||
export const validatePatchRuleRequestBody = (rule: PatchRuleRequestBody): string[] => {
|
||||
return [
|
||||
...validateId(rule),
|
||||
...validateTimelineId(rule),
|
||||
...validateTimelineTitle(rule),
|
||||
...validateThreshold(rule),
|
||||
];
|
||||
};
|
||||
|
||||
const validateId = (rule: PatchRuleRequestBody): string[] => {
|
||||
if (rule.id != null && rule.rule_id != null) {
|
||||
return ['both "id" and "rule_id" cannot exist, choose one or the other'];
|
||||
} else if (rule.id == null && rule.rule_id == null) {
|
||||
return ['either "id" or "rule_id" must be set'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const validateTimelineId = (rule: PatchRuleRequestBody): string[] => {
|
||||
if (rule.timeline_id != null) {
|
||||
if (rule.timeline_title == null) {
|
||||
return ['when "timeline_id" exists, "timeline_title" must also exist'];
|
||||
|
@ -20,7 +42,7 @@ export const validateTimelineId = (rule: PatchRulesSchema): string[] => {
|
|||
return [];
|
||||
};
|
||||
|
||||
export const validateTimelineTitle = (rule: PatchRulesSchema): string[] => {
|
||||
const validateTimelineTitle = (rule: PatchRuleRequestBody): string[] => {
|
||||
if (rule.timeline_title != null) {
|
||||
if (rule.timeline_id == null) {
|
||||
return ['when "timeline_title" exists, "timeline_id" must also exist'];
|
||||
|
@ -33,17 +55,7 @@ export const validateTimelineTitle = (rule: PatchRulesSchema): string[] => {
|
|||
return [];
|
||||
};
|
||||
|
||||
export const validateId = (rule: PatchRulesSchema): string[] => {
|
||||
if (rule.id != null && rule.rule_id != null) {
|
||||
return ['both "id" and "rule_id" cannot exist, choose one or the other'];
|
||||
} else if (rule.id == null && rule.rule_id == null) {
|
||||
return ['either "id" or "rule_id" must be set'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const validateThreshold = (rule: PatchRulesSchema): string[] => {
|
||||
const validateThreshold = (rule: PatchRuleRequestBody): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (rule.type === 'threshold') {
|
||||
if (!rule.threshold) {
|
||||
|
@ -65,12 +77,3 @@ export const validateThreshold = (rule: PatchRulesSchema): string[] => {
|
|||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
export const patchRuleValidateTypeDependents = (rule: PatchRulesSchema): string[] => {
|
||||
return [
|
||||
...validateId(rule),
|
||||
...validateTimelineId(rule),
|
||||
...validateTimelineTitle(rule),
|
||||
...validateThreshold(rule),
|
||||
];
|
||||
};
|
|
@ -5,17 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { QueryRulesSchema } from './query_rules_schema';
|
||||
import { queryRulesSchema } from './query_rules_schema';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { QueryRuleByIds } from './query_rule_by_ids';
|
||||
|
||||
describe('query_rules_schema', () => {
|
||||
describe('Query rule by IDs schema', () => {
|
||||
test('empty objects do validate', () => {
|
||||
const payload: Partial<QueryRulesSchema> = {};
|
||||
const payload: Partial<QueryRuleByIds> = {};
|
||||
|
||||
const decoded = queryRulesSchema.decode(payload);
|
||||
const decoded = QueryRuleByIds.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
|
@ -6,15 +6,12 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { RuleObjectId, RuleSignatureId } from '../../../../../rule_schema';
|
||||
|
||||
import { rule_id, id } from '../common/schemas';
|
||||
|
||||
export const queryRulesSchema = t.exact(
|
||||
export type QueryRuleByIds = t.TypeOf<typeof QueryRuleByIds>;
|
||||
export const QueryRuleByIds = t.exact(
|
||||
t.partial({
|
||||
rule_id,
|
||||
id,
|
||||
rule_id: RuleSignatureId,
|
||||
id: RuleObjectId,
|
||||
})
|
||||
);
|
||||
|
||||
export type QueryRulesSchema = t.TypeOf<typeof queryRulesSchema>;
|
||||
export type QueryRulesSchemaDecoded = QueryRulesSchema;
|
|
@ -5,22 +5,22 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { QueryRulesSchema } from './query_rules_schema';
|
||||
import { queryRuleValidateTypeDependents } from './query_rules_type_dependents';
|
||||
import type { QueryRuleByIds } from './query_rule_by_ids';
|
||||
import { validateQueryRuleByIds } from './query_rule_by_ids_validation';
|
||||
|
||||
describe('query_rules_type_dependents', () => {
|
||||
describe('Query rule by IDs schema, additional validation', () => {
|
||||
test('You cannot have both an id and a rule_id', () => {
|
||||
const schema: QueryRulesSchema = {
|
||||
const schema: QueryRuleByIds = {
|
||||
id: 'some-id',
|
||||
rule_id: 'some-rule-id',
|
||||
};
|
||||
const errors = queryRuleValidateTypeDependents(schema);
|
||||
const errors = validateQueryRuleByIds(schema);
|
||||
expect(errors).toEqual(['both "id" and "rule_id" cannot exist, choose one or the other']);
|
||||
});
|
||||
|
||||
test('You must set either an id or a rule_id', () => {
|
||||
const schema: QueryRulesSchema = {};
|
||||
const errors = queryRuleValidateTypeDependents(schema);
|
||||
const schema: QueryRuleByIds = {};
|
||||
const errors = validateQueryRuleByIds(schema);
|
||||
expect(errors).toEqual(['either "id" or "rule_id" must be set']);
|
||||
});
|
||||
});
|
|
@ -5,9 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { QueryRulesSchema } from './query_rules_schema';
|
||||
import type { QueryRuleByIds } from './query_rule_by_ids';
|
||||
|
||||
export const validateId = (rule: QueryRulesSchema): string[] => {
|
||||
/**
|
||||
* Additional validation that is implemented outside of the schema itself.
|
||||
*/
|
||||
export const validateQueryRuleByIds = (schema: QueryRuleByIds): string[] => {
|
||||
return [...validateId(schema)];
|
||||
};
|
||||
|
||||
const validateId = (rule: QueryRuleByIds): string[] => {
|
||||
if (rule.id != null && rule.rule_id != null) {
|
||||
return ['both "id" and "rule_id" cannot exist, choose one or the other'];
|
||||
} else if (rule.id == null && rule.rule_id == null) {
|
||||
|
@ -16,7 +23,3 @@ export const validateId = (rule: QueryRulesSchema): string[] => {
|
|||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const queryRuleValidateTypeDependents = (schema: QueryRulesSchema): string[] => {
|
||||
return [...validateId(schema)];
|
||||
};
|
|
@ -5,68 +5,68 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getUpdateRulesSchemaMock } from './rule_schemas.mock';
|
||||
import type { UpdateRulesSchema } from './rule_schemas';
|
||||
import { updateRuleValidateTypeDependents } from './update_rules_type_dependents';
|
||||
import type { RuleUpdateProps } from '../../../../../rule_schema';
|
||||
import { getUpdateRulesSchemaMock } from '../../../../../rule_schema/mocks';
|
||||
import { validateUpdateRuleProps } from './request_schema_validation';
|
||||
|
||||
describe('update_rules_type_dependents', () => {
|
||||
describe('Update rule request schema, additional validation', () => {
|
||||
test('You cannot omit timeline_title when timeline_id is present', () => {
|
||||
const schema: UpdateRulesSchema = {
|
||||
const schema: RuleUpdateProps = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
};
|
||||
delete schema.timeline_title;
|
||||
const errors = updateRuleValidateTypeDependents(schema);
|
||||
const errors = validateUpdateRuleProps(schema);
|
||||
expect(errors).toEqual(['when "timeline_id" exists, "timeline_title" must also exist']);
|
||||
});
|
||||
|
||||
test('You cannot have empty string for timeline_title when timeline_id is present', () => {
|
||||
const schema: UpdateRulesSchema = {
|
||||
const schema: RuleUpdateProps = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
timeline_title: '',
|
||||
};
|
||||
const errors = updateRuleValidateTypeDependents(schema);
|
||||
const errors = validateUpdateRuleProps(schema);
|
||||
expect(errors).toEqual(['"timeline_title" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title with an empty timeline_id', () => {
|
||||
const schema: UpdateRulesSchema = {
|
||||
const schema: RuleUpdateProps = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
timeline_id: '',
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
const errors = updateRuleValidateTypeDependents(schema);
|
||||
const errors = validateUpdateRuleProps(schema);
|
||||
expect(errors).toEqual(['"timeline_id" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title without timeline_id', () => {
|
||||
const schema: UpdateRulesSchema = {
|
||||
const schema: RuleUpdateProps = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
delete schema.timeline_id;
|
||||
const errors = updateRuleValidateTypeDependents(schema);
|
||||
const errors = validateUpdateRuleProps(schema);
|
||||
expect(errors).toEqual(['when "timeline_title" exists, "timeline_id" must also exist']);
|
||||
});
|
||||
|
||||
test('You cannot have both an id and a rule_id', () => {
|
||||
const schema: UpdateRulesSchema = {
|
||||
const schema: RuleUpdateProps = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
id: 'some-id',
|
||||
rule_id: 'some-rule-id',
|
||||
};
|
||||
const errors = updateRuleValidateTypeDependents(schema);
|
||||
const errors = validateUpdateRuleProps(schema);
|
||||
expect(errors).toEqual(['both "id" and "rule_id" cannot exist, choose one or the other']);
|
||||
});
|
||||
|
||||
test('You must set either an id or a rule_id', () => {
|
||||
const schema: UpdateRulesSchema = {
|
||||
const schema: RuleUpdateProps = {
|
||||
...getUpdateRulesSchemaMock(),
|
||||
};
|
||||
delete schema.id;
|
||||
delete schema.rule_id;
|
||||
const errors = updateRuleValidateTypeDependents(schema);
|
||||
const errors = validateUpdateRuleProps(schema);
|
||||
expect(errors).toEqual(['either "id" or "rule_id" must be set']);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 { RuleUpdateProps } from '../../../../../rule_schema';
|
||||
|
||||
/**
|
||||
* Additional validation that is implemented outside of the schema itself.
|
||||
*/
|
||||
export const validateUpdateRuleProps = (props: RuleUpdateProps): string[] => {
|
||||
return [
|
||||
...validateId(props),
|
||||
...validateTimelineId(props),
|
||||
...validateTimelineTitle(props),
|
||||
...validateThreshold(props),
|
||||
];
|
||||
};
|
||||
|
||||
const validateId = (props: RuleUpdateProps): string[] => {
|
||||
if (props.id != null && props.rule_id != null) {
|
||||
return ['both "id" and "rule_id" cannot exist, choose one or the other'];
|
||||
} else if (props.id == null && props.rule_id == null) {
|
||||
return ['either "id" or "rule_id" must be set'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const validateTimelineId = (props: RuleUpdateProps): string[] => {
|
||||
if (props.timeline_id != null) {
|
||||
if (props.timeline_title == null) {
|
||||
return ['when "timeline_id" exists, "timeline_title" must also exist'];
|
||||
} else if (props.timeline_id === '') {
|
||||
return ['"timeline_id" cannot be an empty string'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const validateTimelineTitle = (props: RuleUpdateProps): string[] => {
|
||||
if (props.timeline_title != null) {
|
||||
if (props.timeline_id == null) {
|
||||
return ['when "timeline_title" exists, "timeline_id" must also exist'];
|
||||
} else if (props.timeline_title === '') {
|
||||
return ['"timeline_title" cannot be an empty string'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const validateThreshold = (props: RuleUpdateProps): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (props.type === 'threshold') {
|
||||
if (!props.threshold) {
|
||||
errors.push('when "type" is "threshold", "threshold" is required');
|
||||
} else {
|
||||
if (
|
||||
props.threshold.cardinality?.length &&
|
||||
props.threshold.field.includes(props.threshold.cardinality[0].field)
|
||||
) {
|
||||
errors.push('Cardinality of a field that is being aggregated on is always 1');
|
||||
}
|
||||
if (Array.isArray(props.threshold.field) && props.threshold.field.length > 3) {
|
||||
errors.push('Number of fields must be 3 or less');
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
};
|
|
@ -5,22 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
ExportRulesSchema,
|
||||
ExportRulesQuerySchema,
|
||||
ExportRulesQuerySchemaDecoded,
|
||||
} from './export_rules_schema';
|
||||
import { exportRulesQuerySchema, exportRulesSchema } from './export_rules_schema';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
describe('create rules schema', () => {
|
||||
describe('exportRulesSchema', () => {
|
||||
import { ExportRulesRequestBody, ExportRulesRequestQuery } from './request_schema';
|
||||
import type { ExportRulesRequestQueryDecoded } from './request_schema';
|
||||
|
||||
describe('Export rules request schema', () => {
|
||||
describe('ExportRulesRequestBody', () => {
|
||||
test('null value or absent values validate', () => {
|
||||
const payload: Partial<ExportRulesSchema> = null;
|
||||
const payload: Partial<ExportRulesRequestBody> = null;
|
||||
|
||||
const decoded = exportRulesSchema.decode(payload);
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -30,7 +27,7 @@ describe('create rules schema', () => {
|
|||
test('empty object does not validate', () => {
|
||||
const payload = {};
|
||||
|
||||
const decoded = exportRulesSchema.decode(payload);
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -41,9 +38,9 @@ describe('create rules schema', () => {
|
|||
});
|
||||
|
||||
test('empty object array does validate', () => {
|
||||
const payload: ExportRulesSchema = { objects: [] };
|
||||
const payload: ExportRulesRequestBody = { objects: [] };
|
||||
|
||||
const decoded = exportRulesSchema.decode(payload);
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -51,9 +48,9 @@ describe('create rules schema', () => {
|
|||
});
|
||||
|
||||
test('array with rule_id validates', () => {
|
||||
const payload: ExportRulesSchema = { objects: [{ rule_id: 'test-1' }] };
|
||||
const payload: ExportRulesRequestBody = { objects: [{ rule_id: 'test-1' }] };
|
||||
|
||||
const decoded = exportRulesSchema.decode(payload);
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -61,11 +58,11 @@ describe('create rules schema', () => {
|
|||
});
|
||||
|
||||
test('array with id does not validate as we do not allow that on purpose since we export rule_id', () => {
|
||||
const payload: Omit<ExportRulesSchema, 'objects'> & { objects: [{ id: string }] } = {
|
||||
const payload: Omit<ExportRulesRequestBody, 'objects'> & { objects: [{ id: string }] } = {
|
||||
objects: [{ id: '4a7ff83d-3055-4bb2-ba68-587b9c6c15a4' }],
|
||||
};
|
||||
|
||||
const decoded = exportRulesSchema.decode(payload);
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -76,15 +73,15 @@ describe('create rules schema', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('exportRulesQuerySchema', () => {
|
||||
describe('ExportRulesRequestQuery', () => {
|
||||
test('default value for file_name is export.ndjson and default for exclude_export_details is false', () => {
|
||||
const payload: Partial<ExportRulesQuerySchema> = {};
|
||||
const payload: Partial<ExportRulesRequestQuery> = {};
|
||||
|
||||
const decoded = exportRulesQuerySchema.decode(payload);
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
const expected: ExportRulesQuerySchemaDecoded = {
|
||||
const expected: ExportRulesRequestQueryDecoded = {
|
||||
file_name: 'export.ndjson',
|
||||
exclude_export_details: false,
|
||||
};
|
||||
|
@ -93,15 +90,15 @@ describe('create rules schema', () => {
|
|||
});
|
||||
|
||||
test('file_name validates', () => {
|
||||
const payload: ExportRulesQuerySchema = {
|
||||
const payload: ExportRulesRequestQuery = {
|
||||
file_name: 'test.ndjson',
|
||||
};
|
||||
|
||||
const decoded = exportRulesQuerySchema.decode(payload);
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
const expected: ExportRulesQuerySchemaDecoded = {
|
||||
const expected: ExportRulesRequestQueryDecoded = {
|
||||
file_name: 'test.ndjson',
|
||||
exclude_export_details: false,
|
||||
};
|
||||
|
@ -110,11 +107,11 @@ describe('create rules schema', () => {
|
|||
});
|
||||
|
||||
test('file_name does not validate with a number', () => {
|
||||
const payload: Omit<ExportRulesQuerySchema, 'file_name'> & { file_name: number } = {
|
||||
const payload: Omit<ExportRulesRequestQuery, 'file_name'> & { file_name: number } = {
|
||||
file_name: 10,
|
||||
};
|
||||
|
||||
const decoded = exportRulesQuerySchema.decode(payload);
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -124,15 +121,15 @@ describe('create rules schema', () => {
|
|||
});
|
||||
|
||||
test('exclude_export_details validates with a boolean true', () => {
|
||||
const payload: ExportRulesQuerySchema = {
|
||||
const payload: ExportRulesRequestQuery = {
|
||||
exclude_export_details: true,
|
||||
};
|
||||
|
||||
const decoded = exportRulesQuerySchema.decode(payload);
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
const expected: ExportRulesQuerySchemaDecoded = {
|
||||
const expected: ExportRulesRequestQueryDecoded = {
|
||||
exclude_export_details: true,
|
||||
file_name: 'export.ndjson',
|
||||
};
|
||||
|
@ -140,13 +137,13 @@ describe('create rules schema', () => {
|
|||
});
|
||||
|
||||
test('exclude_export_details does not validate with a string', () => {
|
||||
const payload: Omit<ExportRulesQuerySchema, 'exclude_export_details'> & {
|
||||
const payload: Omit<ExportRulesRequestQuery, 'exclude_export_details'> & {
|
||||
exclude_export_details: string;
|
||||
} = {
|
||||
exclude_export_details: 'invalid string',
|
||||
};
|
||||
|
||||
const decoded = exportRulesQuerySchema.decode(payload);
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { DefaultExportFileName } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
import { DefaultStringBooleanFalse } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
import type { FileName, ExcludeExportDetails } from '../../../../schemas/common/schemas';
|
||||
import { RuleSignatureId } from '../../../../rule_schema';
|
||||
|
||||
const ObjectsWithRuleId = t.array(t.exact(t.type({ rule_id: RuleSignatureId })));
|
||||
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type ExportRulesRequestBody = t.TypeOf<typeof ExportRulesRequestBody>;
|
||||
export const ExportRulesRequestBody = t.union([
|
||||
t.exact(t.type({ objects: ObjectsWithRuleId })),
|
||||
t.null,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Query string parameters of the API route.
|
||||
*/
|
||||
export type ExportRulesRequestQuery = t.TypeOf<typeof ExportRulesRequestQuery>;
|
||||
export const ExportRulesRequestQuery = t.exact(
|
||||
t.partial({ file_name: DefaultExportFileName, exclude_export_details: DefaultStringBooleanFalse })
|
||||
);
|
||||
|
||||
export type ExportRulesRequestQueryDecoded = Omit<
|
||||
ExportRulesRequestQuery,
|
||||
'file_name' | 'exclude_export_details'
|
||||
> & {
|
||||
file_name: FileName;
|
||||
exclude_export_details: ExcludeExportDetails;
|
||||
};
|
|
@ -5,17 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import type { FindRulesSchema } from './find_rules_schema';
|
||||
import { findRulesSchema } from './find_rules_schema';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
describe('find_rules_schema', () => {
|
||||
import { FindRulesRequestQuery } from './request_schema';
|
||||
|
||||
describe('Find rules request schema', () => {
|
||||
test('empty objects do validate', () => {
|
||||
const payload: FindRulesSchema = {};
|
||||
const payload: FindRulesRequestQuery = {};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -26,7 +26,7 @@ describe('find_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('all values validate', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
per_page: 5,
|
||||
page: 1,
|
||||
sort_field: 'some field',
|
||||
|
@ -35,7 +35,7 @@ describe('find_rules_schema', () => {
|
|||
sort_order: 'asc',
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -43,9 +43,11 @@ describe('find_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('made up parameters do not validate', () => {
|
||||
const payload: Partial<FindRulesSchema> & { madeUp: string } = { madeUp: 'invalid value' };
|
||||
const payload: Partial<FindRulesRequestQuery> & { madeUp: string } = {
|
||||
madeUp: 'invalid value',
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "madeUp"']);
|
||||
|
@ -53,71 +55,71 @@ describe('find_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('per_page validates', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
per_page: 5,
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).per_page).toEqual(payload.per_page);
|
||||
expect((message.schema as FindRulesRequestQuery).per_page).toEqual(payload.per_page);
|
||||
});
|
||||
|
||||
test('page validates', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
page: 5,
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).page).toEqual(payload.page);
|
||||
expect((message.schema as FindRulesRequestQuery).page).toEqual(payload.page);
|
||||
});
|
||||
|
||||
test('sort_field validates', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
sort_field: 'value',
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).sort_field).toEqual('value');
|
||||
expect((message.schema as FindRulesRequestQuery).sort_field).toEqual('value');
|
||||
});
|
||||
|
||||
test('fields validates with a string', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
fields: ['some value'],
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).fields).toEqual(payload.fields);
|
||||
expect((message.schema as FindRulesRequestQuery).fields).toEqual(payload.fields);
|
||||
});
|
||||
|
||||
test('fields validates with multiple strings', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
fields: ['some value 1', 'some value 2'],
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).fields).toEqual(payload.fields);
|
||||
expect((message.schema as FindRulesRequestQuery).fields).toEqual(payload.fields);
|
||||
});
|
||||
|
||||
test('fields does not validate with a number', () => {
|
||||
const payload: Omit<FindRulesSchema, 'fields'> & { fields: number } = {
|
||||
const payload: Omit<FindRulesRequestQuery, 'fields'> & { fields: number } = {
|
||||
fields: 5,
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "fields"']);
|
||||
|
@ -125,43 +127,43 @@ describe('find_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('per_page has a default of 20', () => {
|
||||
const payload: FindRulesSchema = {};
|
||||
const payload: FindRulesRequestQuery = {};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).per_page).toEqual(20);
|
||||
expect((message.schema as FindRulesRequestQuery).per_page).toEqual(20);
|
||||
});
|
||||
|
||||
test('page has a default of 1', () => {
|
||||
const payload: FindRulesSchema = {};
|
||||
const payload: FindRulesRequestQuery = {};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).page).toEqual(1);
|
||||
expect((message.schema as FindRulesRequestQuery).page).toEqual(1);
|
||||
});
|
||||
|
||||
test('filter works with a string', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
filter: 'some value 1',
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).filter).toEqual(payload.filter);
|
||||
expect((message.schema as FindRulesRequestQuery).filter).toEqual(payload.filter);
|
||||
});
|
||||
|
||||
test('filter does not work with a number', () => {
|
||||
const payload: Omit<FindRulesSchema, 'filter'> & { filter: number } = {
|
||||
const payload: Omit<FindRulesRequestQuery, 'filter'> & { filter: number } = {
|
||||
filter: 5,
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "filter"']);
|
||||
|
@ -169,26 +171,26 @@ describe('find_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('sort_order validates with desc and sort_field', () => {
|
||||
const payload: FindRulesSchema = {
|
||||
const payload: FindRulesRequestQuery = {
|
||||
sort_order: 'desc',
|
||||
sort_field: 'some field',
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect((message.schema as FindRulesSchema).sort_order).toEqual(payload.sort_order);
|
||||
expect((message.schema as FindRulesSchema).sort_field).toEqual(payload.sort_field);
|
||||
expect((message.schema as FindRulesRequestQuery).sort_order).toEqual(payload.sort_order);
|
||||
expect((message.schema as FindRulesRequestQuery).sort_field).toEqual(payload.sort_field);
|
||||
});
|
||||
|
||||
test('sort_order does not validate with a string other than asc and desc', () => {
|
||||
const payload: Omit<FindRulesSchema, 'sort_order'> & { sort_order: string } = {
|
||||
const payload: Omit<FindRulesRequestQuery, 'sort_order'> & { sort_order: string } = {
|
||||
sort_order: 'some other string',
|
||||
sort_field: 'some field',
|
||||
};
|
||||
|
||||
const decoded = findRulesSchema.decode(payload);
|
||||
const decoded = FindRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
|
@ -6,12 +6,15 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { DefaultPerPage, DefaultPage } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
import type { PerPage, Page } from '../common';
|
||||
import { queryFilter, fields, SortField, SortOrder } from '../common';
|
||||
import type { PerPage, Page } from '../../../../schemas/common';
|
||||
import { queryFilter, fields, SortField, SortOrder } from '../../../../schemas/common';
|
||||
|
||||
export const findRulesSchema = t.exact(
|
||||
/**
|
||||
* Query string parameters of the API route.
|
||||
*/
|
||||
export type FindRulesRequestQuery = t.TypeOf<typeof FindRulesRequestQuery>;
|
||||
export const FindRulesRequestQuery = t.exact(
|
||||
t.partial({
|
||||
fields,
|
||||
filter: queryFilter,
|
||||
|
@ -22,8 +25,7 @@ export const findRulesSchema = t.exact(
|
|||
})
|
||||
);
|
||||
|
||||
export type FindRulesSchema = t.TypeOf<typeof findRulesSchema>;
|
||||
export type FindRulesSchemaDecoded = Omit<FindRulesSchema, 'per_page'> & {
|
||||
export type FindRulesRequestQueryDecoded = Omit<FindRulesRequestQuery, 'per_page'> & {
|
||||
page: Page;
|
||||
per_page: PerPage;
|
||||
};
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 { FindRulesRequestQuery } from './request_schema';
|
||||
import { validateFindRulesRequestQuery } from './request_schema_validation';
|
||||
|
||||
describe('Find rules request schema, additional validation', () => {
|
||||
describe('validateFindRulesRequestQuery', () => {
|
||||
test('You can have an empty sort_field and empty sort_order', () => {
|
||||
const schema: FindRulesRequestQuery = {};
|
||||
const errors = validateFindRulesRequestQuery(schema);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test('You can have both a sort_field and and a sort_order', () => {
|
||||
const schema: FindRulesRequestQuery = {
|
||||
sort_field: 'some field',
|
||||
sort_order: 'asc',
|
||||
};
|
||||
const errors = validateFindRulesRequestQuery(schema);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test('You cannot have sort_field without sort_order', () => {
|
||||
const schema: FindRulesRequestQuery = {
|
||||
sort_field: 'some field',
|
||||
};
|
||||
const errors = validateFindRulesRequestQuery(schema);
|
||||
expect(errors).toEqual([
|
||||
'when "sort_order" and "sort_field" must exist together or not at all',
|
||||
]);
|
||||
});
|
||||
|
||||
test('You cannot have sort_order without sort_field', () => {
|
||||
const schema: FindRulesRequestQuery = {
|
||||
sort_order: 'asc',
|
||||
};
|
||||
const errors = validateFindRulesRequestQuery(schema);
|
||||
expect(errors).toEqual([
|
||||
'when "sort_order" and "sort_field" must exist together or not at all',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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 type { FindRulesRequestQuery } from './request_schema';
|
||||
|
||||
/**
|
||||
* Additional validation that is implemented outside of the schema itself.
|
||||
*/
|
||||
export const validateFindRulesRequestQuery = (query: FindRulesRequestQuery): string[] => {
|
||||
return [...validateSortOrder(query)];
|
||||
};
|
||||
|
||||
const validateSortOrder = (query: FindRulesRequestQuery): string[] => {
|
||||
if (query.sort_order != null || query.sort_field != null) {
|
||||
if (query.sort_order == null || query.sort_field == null) {
|
||||
return ['when "sort_order" and "sort_field" must exist together or not at all'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
|
@ -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 { left } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import { ImportRulesRequestBody } from './request_schema';
|
||||
|
||||
describe('Import rules schema', () => {
|
||||
describe('ImportRulesRequestBody', () => {
|
||||
test('does not validate with an empty object', () => {
|
||||
const payload = {};
|
||||
|
||||
const decoded = ImportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "undefined" supplied to "file"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
});
|
||||
|
||||
test('does not validate with a made string', () => {
|
||||
const payload: Omit<ImportRulesRequestBody, 'file'> & { madeUpKey: string } = {
|
||||
madeUpKey: 'madeupstring',
|
||||
};
|
||||
|
||||
const decoded = ImportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "undefined" supplied to "file"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
});
|
||||
|
||||
test('does validate with a file object', () => {
|
||||
const payload: ImportRulesRequestBody = { file: {} };
|
||||
|
||||
const decoded = ImportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -7,13 +7,12 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { id } from '../common/schemas';
|
||||
|
||||
export const queryRuleByIdSchema = t.exact(
|
||||
/**
|
||||
* Request body parameters of the API route.
|
||||
*/
|
||||
export type ImportRulesRequestBody = t.TypeOf<typeof ImportRulesRequestBody>;
|
||||
export const ImportRulesRequestBody = t.exact(
|
||||
t.type({
|
||||
id,
|
||||
file: t.object,
|
||||
})
|
||||
);
|
||||
|
||||
export type QueryRuleByIdSchema = t.TypeOf<typeof queryRuleByIdSchema>;
|
||||
export type QueryRuleByIdSchemaDecoded = QueryRuleByIdSchema;
|
|
@ -8,15 +8,15 @@
|
|||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import type { Either } from 'fp-ts/lib/Either';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import type { ImportRulesSchema } from './import_rules_schema';
|
||||
import { importRulesSchema } from './import_rules_schema';
|
||||
import type { ErrorSchema } from './error_schema';
|
||||
import type { Errors } from 'io-ts';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
describe('import_rules_schema', () => {
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import type { ErrorSchema } from '../../../../schemas/response/error_schema';
|
||||
import { ImportRulesResponse } from './response_schema';
|
||||
|
||||
describe('Import rules response schema', () => {
|
||||
test('it should validate an empty import response with no errors', () => {
|
||||
const payload: ImportRulesSchema = {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: true,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -25,7 +25,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -34,7 +34,7 @@ describe('import_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate an empty import response with a single error', () => {
|
||||
const payload: ImportRulesSchema = {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -43,7 +43,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -52,7 +52,7 @@ describe('import_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate an empty import response with a single exceptions error', () => {
|
||||
const payload: ImportRulesSchema = {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -61,7 +61,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -70,7 +70,7 @@ describe('import_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate an empty import response with two errors', () => {
|
||||
const payload: ImportRulesSchema = {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -82,7 +82,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -91,7 +91,7 @@ describe('import_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate an empty import response with two exception errors', () => {
|
||||
const payload: ImportRulesSchema = {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -103,7 +103,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -112,7 +112,7 @@ describe('import_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a success_count that is a negative number', () => {
|
||||
const payload: ImportRulesSchema = {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
success_count: -1,
|
||||
rules_count: 0,
|
||||
|
@ -121,7 +121,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -132,7 +132,7 @@ describe('import_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a exceptions_success_count that is a negative number', () => {
|
||||
const payload: ImportRulesSchema = {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -141,7 +141,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: -1,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -170,7 +170,7 @@ describe('import_rules_schema', () => {
|
|||
>;
|
||||
}
|
||||
>;
|
||||
const payload: Omit<ImportRulesSchema, 'success'> & { success: string } = {
|
||||
const payload: Omit<ImportRulesResponse, 'success'> & { success: string } = {
|
||||
success: 'hello',
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -179,7 +179,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded as UnsafeCastForTest);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -207,17 +207,18 @@ describe('import_rules_schema', () => {
|
|||
>;
|
||||
}
|
||||
>;
|
||||
const payload: Omit<ImportRulesSchema, 'exceptions_success'> & { exceptions_success: string } =
|
||||
{
|
||||
success: true,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
errors: [],
|
||||
exceptions_errors: [],
|
||||
exceptions_success: 'hello',
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const payload: Omit<ImportRulesResponse, 'exceptions_success'> & {
|
||||
exceptions_success: string;
|
||||
} = {
|
||||
success: true,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
errors: [],
|
||||
exceptions_errors: [],
|
||||
exceptions_success: 'hello',
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded as UnsafeCastForTest);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -228,7 +229,7 @@ describe('import_rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a success an extra invalid field', () => {
|
||||
const payload: ImportRulesSchema & { invalid_field: string } = {
|
||||
const payload: ImportRulesResponse & { invalid_field: string } = {
|
||||
success: true,
|
||||
success_count: 0,
|
||||
rules_count: 0,
|
||||
|
@ -238,7 +239,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -248,7 +249,7 @@ describe('import_rules_schema', () => {
|
|||
|
||||
test('it should NOT validate an extra field in the second position of the errors array', () => {
|
||||
type InvalidError = ErrorSchema & { invalid_data?: string };
|
||||
const payload: Omit<ImportRulesSchema, 'errors'> & {
|
||||
const payload: Omit<ImportRulesResponse, 'errors'> & {
|
||||
errors: InvalidError[];
|
||||
} = {
|
||||
success: true,
|
||||
|
@ -262,7 +263,7 @@ describe('import_rules_schema', () => {
|
|||
exceptions_success: true,
|
||||
exceptions_success_count: 0,
|
||||
};
|
||||
const decoded = importRulesSchema.decode(payload);
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
|
@ -5,22 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { PositiveInteger } from '@kbn/securitysolution-io-ts-types';
|
||||
import * as t from 'io-ts';
|
||||
import { PositiveInteger } from '@kbn/securitysolution-io-ts-types';
|
||||
import { errorSchema } from '../../../../schemas/response/error_schema';
|
||||
|
||||
import { success, success_count } from '../common/schemas';
|
||||
import { errorSchema } from './error_schema';
|
||||
|
||||
export const importRulesSchema = t.exact(
|
||||
export type ImportRulesResponse = t.TypeOf<typeof ImportRulesResponse>;
|
||||
export const ImportRulesResponse = t.exact(
|
||||
t.type({
|
||||
exceptions_success: t.boolean,
|
||||
exceptions_success_count: PositiveInteger,
|
||||
exceptions_errors: t.array(errorSchema),
|
||||
rules_count: PositiveInteger,
|
||||
success,
|
||||
success_count,
|
||||
success: t.boolean,
|
||||
success_count: PositiveInteger,
|
||||
errors: t.array(errorSchema),
|
||||
})
|
||||
);
|
||||
|
||||
export type ImportRulesSchema = t.TypeOf<typeof importRulesSchema>;
|
|
@ -0,0 +1,6 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './api/rules/bulk_crud/bulk_create_rules/request_schema';
|
||||
export * from './api/rules/bulk_crud/bulk_delete_rules/request_schema';
|
||||
export * from './api/rules/bulk_crud/bulk_patch_rules/request_schema';
|
||||
export * from './api/rules/bulk_crud/bulk_update_rules/request_schema';
|
||||
export * from './api/rules/bulk_crud/response_schema';
|
||||
|
||||
export * from './api/rules/crud/create_rule/request_schema_validation';
|
||||
export * from './api/rules/crud/patch_rule/request_schema_validation';
|
||||
export * from './api/rules/crud/patch_rule/request_schema';
|
||||
export * from './api/rules/crud/read_rule/query_rule_by_ids_validation';
|
||||
export * from './api/rules/crud/read_rule/query_rule_by_ids';
|
||||
export * from './api/rules/crud/update_rule/request_schema_validation';
|
||||
|
||||
export * from './api/rules/export_rules/request_schema';
|
||||
export * from './api/rules/find_rules/request_schema_validation';
|
||||
export * from './api/rules/find_rules/request_schema';
|
||||
export * from './api/rules/import_rules/request_schema';
|
||||
export * from './api/rules/import_rules/response_schema';
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/pull/142950
|
||||
// export * from './api/urls';
|
||||
|
||||
export * from './model/export/export_rules_details_schema';
|
||||
export * from './model/import/rule_to_import_validation';
|
||||
export * from './model/import/rule_to_import';
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './api/rules/bulk_actions/request_schema.mock';
|
||||
export * from './api/rules/crud/patch_rule/request_schema.mock';
|
||||
|
||||
export * from './model/export/export_rules_details_schema.mock';
|
||||
export * from './model/import/rule_to_import.mock';
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ExportRulesDetails } from './export_rules_details_schema';
|
||||
import type { ExportExceptionDetailsMock } from '@kbn/lists-plugin/common/schemas/response/exception_export_details_schema.mock';
|
||||
import { getExceptionExportDetailsMock } from '@kbn/lists-plugin/common/schemas/response/exception_export_details_schema.mock';
|
||||
import type { ExportExceptionDetailsMock } from '@kbn/lists-plugin/common/schemas/response/exception_export_details_schema.mock';
|
||||
import type { ExportRulesDetails } from './export_rules_details_schema';
|
||||
|
||||
interface RuleDetailsMock {
|
||||
totalCount?: number;
|
|
@ -16,7 +16,7 @@ const createSchema = <Required extends t.Props, Optional extends t.Props>(
|
|||
return t.intersection([t.exact(t.type(requiredFields)), t.exact(t.partial(optionalFields))]);
|
||||
};
|
||||
|
||||
export const exportRulesDetails = {
|
||||
const exportRulesDetails = {
|
||||
exported_count: t.number,
|
||||
exported_rules_count: t.number,
|
||||
missing_rules: t.array(
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ImportRulesSchema } from './import_rules_schema';
|
||||
import type { RuleToImport } from './rule_to_import';
|
||||
|
||||
export const getImportRulesSchemaMock = (ruleId = 'rule-1'): ImportRulesSchema => ({
|
||||
export const getImportRulesSchemaMock = (ruleId = 'rule-1'): RuleToImport => ({
|
||||
description: 'some description',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
|
@ -18,7 +18,7 @@ export const getImportRulesSchemaMock = (ruleId = 'rule-1'): ImportRulesSchema =
|
|||
rule_id: ruleId,
|
||||
});
|
||||
|
||||
export const getImportRulesWithIdSchemaMock = (ruleId = 'rule-1'): ImportRulesSchema => ({
|
||||
export const getImportRulesWithIdSchemaMock = (ruleId = 'rule-1'): RuleToImport => ({
|
||||
id: '6afb8ce1-ea94-4790-8653-fd0b021d2113',
|
||||
description: 'some description',
|
||||
name: 'Query with a rule id',
|
||||
|
@ -35,7 +35,7 @@ export const getImportRulesWithIdSchemaMock = (ruleId = 'rule-1'): ImportRulesSc
|
|||
* as we might import/export
|
||||
* @param rules Array of rule objects with which to generate rule JSON
|
||||
*/
|
||||
export const rulesToNdJsonString = (rules: ImportRulesSchema[]) => {
|
||||
export const rulesToNdJsonString = (rules: RuleToImport[]) => {
|
||||
return rules.map((rule) => JSON.stringify(rule)).join('\r\n');
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,7 @@ export const ruleIdsToNdJsonString = (ruleIds: string[]) => {
|
|||
return rulesToNdJsonString(rules);
|
||||
};
|
||||
|
||||
export const getImportThreatMatchRulesSchemaMock = (ruleId = 'rule-1'): ImportRulesSchema => ({
|
||||
export const getImportThreatMatchRulesSchemaMock = (ruleId = 'rule-1'): RuleToImport => ({
|
||||
description: 'some description',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
File diff suppressed because it is too large
Load diff
|
@ -6,20 +6,18 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { OnlyFalseAllowed } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
import {
|
||||
rule_id,
|
||||
id,
|
||||
created_at,
|
||||
updated_at,
|
||||
created_by,
|
||||
updated_by,
|
||||
RelatedIntegrationArray,
|
||||
RequiredFieldArray,
|
||||
RuleObjectId,
|
||||
RuleSignatureId,
|
||||
SetupGuide,
|
||||
} from '../common';
|
||||
import { baseCreateParams, createTypeSpecific } from './rule_schemas';
|
||||
BaseCreateProps,
|
||||
TypeSpecificCreateProps,
|
||||
} from '../../../rule_schema';
|
||||
import { created_at, updated_at, created_by, updated_by } from '../../../schemas/common';
|
||||
|
||||
/**
|
||||
* Differences from this and the createRulesSchema are
|
||||
|
@ -31,13 +29,14 @@ import { baseCreateParams, createTypeSpecific } from './rule_schemas';
|
|||
* - created_by is optional (but ignored in the import code)
|
||||
* - updated_by is optional (but ignored in the import code)
|
||||
*/
|
||||
export const importRulesSchema = t.intersection([
|
||||
baseCreateParams,
|
||||
createTypeSpecific,
|
||||
t.exact(t.type({ rule_id })),
|
||||
export type RuleToImport = t.TypeOf<typeof RuleToImport>;
|
||||
export const RuleToImport = t.intersection([
|
||||
BaseCreateProps,
|
||||
TypeSpecificCreateProps,
|
||||
t.exact(t.type({ rule_id: RuleSignatureId })),
|
||||
t.exact(
|
||||
t.partial({
|
||||
id,
|
||||
id: RuleObjectId,
|
||||
immutable: OnlyFalseAllowed,
|
||||
updated_at,
|
||||
updated_by,
|
||||
|
@ -49,13 +48,3 @@ export const importRulesSchema = t.intersection([
|
|||
})
|
||||
),
|
||||
]);
|
||||
|
||||
export type ImportRulesSchema = t.TypeOf<typeof importRulesSchema>;
|
||||
|
||||
export const importRulesPayloadSchema = t.exact(
|
||||
t.type({
|
||||
file: t.object,
|
||||
})
|
||||
);
|
||||
|
||||
export type ImportRulesPayloadSchema = t.TypeOf<typeof importRulesPayloadSchema>;
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 { RuleToImport } from './rule_to_import';
|
||||
import { getImportRulesSchemaMock } from './rule_to_import.mock';
|
||||
import { validateRuleToImport } from './rule_to_import_validation';
|
||||
|
||||
describe('Rule to import schema, additional validation', () => {
|
||||
describe('validateRuleToImport', () => {
|
||||
test('You cannot omit timeline_title when timeline_id is present', () => {
|
||||
const schema: RuleToImport = {
|
||||
...getImportRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
};
|
||||
delete schema.timeline_title;
|
||||
const errors = validateRuleToImport(schema);
|
||||
expect(errors).toEqual(['when "timeline_id" exists, "timeline_title" must also exist']);
|
||||
});
|
||||
|
||||
test('You cannot have empty string for timeline_title when timeline_id is present', () => {
|
||||
const schema: RuleToImport = {
|
||||
...getImportRulesSchemaMock(),
|
||||
timeline_id: '123',
|
||||
timeline_title: '',
|
||||
};
|
||||
const errors = validateRuleToImport(schema);
|
||||
expect(errors).toEqual(['"timeline_title" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title with an empty timeline_id', () => {
|
||||
const schema: RuleToImport = {
|
||||
...getImportRulesSchemaMock(),
|
||||
timeline_id: '',
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
const errors = validateRuleToImport(schema);
|
||||
expect(errors).toEqual(['"timeline_id" cannot be an empty string']);
|
||||
});
|
||||
|
||||
test('You cannot have timeline_title without timeline_id', () => {
|
||||
const schema: RuleToImport = {
|
||||
...getImportRulesSchemaMock(),
|
||||
timeline_title: 'some-title',
|
||||
};
|
||||
delete schema.timeline_id;
|
||||
const errors = validateRuleToImport(schema);
|
||||
expect(errors).toEqual(['when "timeline_title" exists, "timeline_id" must also exist']);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,9 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ImportRulesSchema } from './import_rules_schema';
|
||||
import type { RuleToImport } from './rule_to_import';
|
||||
|
||||
export const validateTimelineId = (rule: ImportRulesSchema): string[] => {
|
||||
/**
|
||||
* Additional validation that is implemented outside of the schema itself.
|
||||
*/
|
||||
export const validateRuleToImport = (rule: RuleToImport): string[] => {
|
||||
return [...validateTimelineId(rule), ...validateTimelineTitle(rule), ...validateThreshold(rule)];
|
||||
};
|
||||
|
||||
const validateTimelineId = (rule: RuleToImport): string[] => {
|
||||
if (rule.timeline_id != null) {
|
||||
if (rule.timeline_title == null) {
|
||||
return ['when "timeline_id" exists, "timeline_title" must also exist'];
|
||||
|
@ -20,7 +27,7 @@ export const validateTimelineId = (rule: ImportRulesSchema): string[] => {
|
|||
return [];
|
||||
};
|
||||
|
||||
export const validateTimelineTitle = (rule: ImportRulesSchema): string[] => {
|
||||
const validateTimelineTitle = (rule: RuleToImport): string[] => {
|
||||
if (rule.timeline_title != null) {
|
||||
if (rule.timeline_id == null) {
|
||||
return ['when "timeline_title" exists, "timeline_id" must also exist'];
|
||||
|
@ -33,7 +40,7 @@ export const validateTimelineTitle = (rule: ImportRulesSchema): string[] => {
|
|||
return [];
|
||||
};
|
||||
|
||||
export const validateThreshold = (rule: ImportRulesSchema): string[] => {
|
||||
const validateThreshold = (rule: RuleToImport): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (rule.type === 'threshold') {
|
||||
if (
|
||||
|
@ -48,7 +55,3 @@ export const validateThreshold = (rule: ImportRulesSchema): string[] => {
|
|||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
export const importRuleValidateTypeDependents = (rule: ImportRulesSchema): string[] => {
|
||||
return [...validateTimelineId(rule), ...validateTimelineTitle(rule), ...validateThreshold(rule)];
|
||||
};
|
|
@ -15,7 +15,7 @@ import { TRuleExecutionEventType } from '../../model/execution_event';
|
|||
import { TLogLevel } from '../../model/log_level';
|
||||
|
||||
/**
|
||||
* Path parameters of the API route.
|
||||
* URL path parameters of the API route.
|
||||
*/
|
||||
export type GetRuleExecutionEventsRequestParams = t.TypeOf<
|
||||
typeof GetRuleExecutionEventsRequestParams
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './model/common_attributes/field_overrides';
|
||||
export * from './model/common_attributes/misc_attributes';
|
||||
export * from './model/common_attributes/related_integrations';
|
||||
export * from './model/common_attributes/required_fields';
|
||||
export * from './model/common_attributes/saved_objects';
|
||||
export * from './model/common_attributes/timeline_template';
|
||||
|
||||
export * from './model/specific_attributes/eql_attributes';
|
||||
export * from './model/specific_attributes/new_terms_attributes';
|
||||
export * from './model/specific_attributes/threshold_attributes';
|
||||
|
||||
export * from './model/rule_schemas';
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './model/rule_request_schema.mock';
|
||||
export * from './model/rule_response_schema.mock';
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
|
||||
interface RuleFields<
|
||||
Required extends t.Props,
|
||||
Optional extends t.Props,
|
||||
Defaultable extends t.Props
|
||||
> {
|
||||
required: Required;
|
||||
optional: Optional;
|
||||
defaultable: Defaultable;
|
||||
}
|
||||
|
||||
export const buildRuleSchemas = <R extends t.Props, O extends t.Props, D extends t.Props>(
|
||||
fields: RuleFields<R, O, D>
|
||||
) => {
|
||||
return {
|
||||
create: buildCreateRuleSchema(fields.required, fields.optional, fields.defaultable),
|
||||
patch: buildPatchRuleSchema(fields.required, fields.optional, fields.defaultable),
|
||||
response: buildResponseRuleSchema(fields.required, fields.optional, fields.defaultable),
|
||||
};
|
||||
};
|
||||
|
||||
const buildCreateRuleSchema = <
|
||||
Required extends t.Props,
|
||||
Optional extends t.Props,
|
||||
Defaultable extends t.Props
|
||||
>(
|
||||
requiredFields: Required,
|
||||
optionalFields: Optional,
|
||||
defaultableFields: Defaultable
|
||||
) => {
|
||||
return t.intersection([
|
||||
t.exact(t.type(requiredFields)),
|
||||
t.exact(t.partial(optionalFields)),
|
||||
t.exact(t.partial(defaultableFields)),
|
||||
]);
|
||||
};
|
||||
|
||||
const buildPatchRuleSchema = <
|
||||
Required extends t.Props,
|
||||
Optional extends t.Props,
|
||||
Defaultable extends t.Props
|
||||
>(
|
||||
requiredFields: Required,
|
||||
optionalFields: Optional,
|
||||
defaultableFields: Defaultable
|
||||
) => {
|
||||
return t.intersection([
|
||||
t.partial(requiredFields),
|
||||
t.partial(optionalFields),
|
||||
t.partial(defaultableFields),
|
||||
]);
|
||||
};
|
||||
|
||||
type OrUndefined<P extends t.Props> = {
|
||||
[K in keyof P]: P[K] | t.UndefinedC;
|
||||
};
|
||||
|
||||
export const buildResponseRuleSchema = <
|
||||
Required extends t.Props,
|
||||
Optional extends t.Props,
|
||||
Defaultable extends t.Props
|
||||
>(
|
||||
requiredFields: Required,
|
||||
optionalFields: Optional,
|
||||
defaultableFields: Defaultable
|
||||
) => {
|
||||
// This bit of logic is to force all fields to be accounted for in conversions from the internal
|
||||
// rule schema to the response schema. Rather than use `t.partial`, which makes each field optional,
|
||||
// we make each field required but possibly undefined. The result is that if a field is forgotten in
|
||||
// the conversion from internal schema to response schema TS will report an error. If we just used t.partial
|
||||
// instead, then optional fields can be accidentally omitted from the conversion - and any actual values
|
||||
// in those fields internally will be stripped in the response.
|
||||
const optionalWithUndefined = Object.keys(optionalFields).reduce<t.Props>((acc, key) => {
|
||||
acc[key] = t.union([optionalFields[key], t.undefined]);
|
||||
return acc;
|
||||
}, {}) as OrUndefined<Optional>;
|
||||
return t.intersection([
|
||||
t.exact(t.type(requiredFields)),
|
||||
t.exact(t.type(optionalWithUndefined)),
|
||||
t.exact(t.type(defaultableFields)),
|
||||
]);
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
|
||||
export type RuleNameOverride = t.TypeOf<typeof RuleNameOverride>;
|
||||
export const RuleNameOverride = t.string; // should be non-empty string?
|
||||
|
||||
export type TimestampOverride = t.TypeOf<typeof TimestampOverride>;
|
||||
export const TimestampOverride = t.string; // should be non-empty string?
|
||||
|
||||
export type TimestampOverrideFallbackDisabled = t.TypeOf<typeof TimestampOverrideFallbackDisabled>;
|
||||
export const TimestampOverrideFallbackDisabled = t.boolean;
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { listArray } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { NonEmptyString, version, UUID } from '@kbn/securitysolution-io-ts-types';
|
||||
import { max_signals, threat } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
|
||||
export type RuleObjectId = t.TypeOf<typeof RuleObjectId>;
|
||||
export const RuleObjectId = UUID;
|
||||
|
||||
/**
|
||||
* NOTE: Never make this a strict uuid, we allow the rule_id to be any string at the moment
|
||||
* in case we encounter 3rd party rule systems which might be using auto incrementing numbers
|
||||
* or other different things.
|
||||
*/
|
||||
export type RuleSignatureId = t.TypeOf<typeof RuleSignatureId>;
|
||||
export const RuleSignatureId = t.string; // should be non-empty string?
|
||||
|
||||
export type RuleName = t.TypeOf<typeof RuleName>;
|
||||
export const RuleName = NonEmptyString;
|
||||
|
||||
export type RuleDescription = t.TypeOf<typeof RuleDescription>;
|
||||
export const RuleDescription = NonEmptyString;
|
||||
|
||||
export type RuleVersion = t.TypeOf<typeof RuleVersion>;
|
||||
export const RuleVersion = version;
|
||||
|
||||
export type IsRuleImmutable = t.TypeOf<typeof IsRuleImmutable>;
|
||||
export const IsRuleImmutable = t.boolean;
|
||||
|
||||
export type IsRuleEnabled = t.TypeOf<typeof IsRuleEnabled>;
|
||||
export const IsRuleEnabled = t.boolean;
|
||||
|
||||
export type RuleTagArray = t.TypeOf<typeof RuleTagArray>;
|
||||
export const RuleTagArray = t.array(t.string); // should be non-empty strings?
|
||||
|
||||
/**
|
||||
* Note that this is a non-exact io-ts type as we allow extra meta information
|
||||
* to be added to the meta object
|
||||
*/
|
||||
export type RuleMetadata = t.TypeOf<typeof RuleMetadata>;
|
||||
export const RuleMetadata = t.object; // should be a more specific type?
|
||||
|
||||
export type RuleLicense = t.TypeOf<typeof RuleLicense>;
|
||||
export const RuleLicense = t.string; // should be non-empty string?
|
||||
|
||||
export type RuleAuthorArray = t.TypeOf<typeof RuleAuthorArray>;
|
||||
export const RuleAuthorArray = t.array(t.string); // should be non-empty strings?
|
||||
|
||||
export type RuleFalsePositiveArray = t.TypeOf<typeof RuleFalsePositiveArray>;
|
||||
export const RuleFalsePositiveArray = t.array(t.string); // should be non-empty strings?
|
||||
|
||||
export type RuleReferenceArray = t.TypeOf<typeof RuleReferenceArray>;
|
||||
export const RuleReferenceArray = t.array(t.string); // should be non-empty strings?
|
||||
|
||||
export type InvestigationGuide = t.TypeOf<typeof InvestigationGuide>;
|
||||
export const InvestigationGuide = t.string;
|
||||
|
||||
/**
|
||||
* Any instructions for the user for setting up their environment in order to start receiving
|
||||
* source events for a given rule.
|
||||
*
|
||||
* It's a multiline text. Markdown is supported.
|
||||
*/
|
||||
export type SetupGuide = t.TypeOf<typeof SetupGuide>;
|
||||
export const SetupGuide = t.string;
|
||||
|
||||
export type BuildingBlockType = t.TypeOf<typeof BuildingBlockType>;
|
||||
export const BuildingBlockType = t.string;
|
||||
|
||||
export type AlertsIndex = t.TypeOf<typeof AlertsIndex>;
|
||||
export const AlertsIndex = t.string;
|
||||
|
||||
export type AlertsIndexNamespace = t.TypeOf<typeof AlertsIndexNamespace>;
|
||||
export const AlertsIndexNamespace = t.string;
|
||||
|
||||
export type ExceptionListArray = t.TypeOf<typeof ExceptionListArray>;
|
||||
export const ExceptionListArray = listArray;
|
||||
|
||||
export type MaxSignals = t.TypeOf<typeof MaxSignals>;
|
||||
export const MaxSignals = max_signals;
|
||||
|
||||
export type ThreatArray = t.TypeOf<typeof ThreatArray>;
|
||||
export const ThreatArray = t.array(threat);
|
||||
|
||||
export type IndexPatternArray = t.TypeOf<typeof IndexPatternArray>;
|
||||
export const IndexPatternArray = t.array(t.string);
|
||||
|
||||
export type DataViewId = t.TypeOf<typeof DataViewId>;
|
||||
export const DataViewId = t.string;
|
||||
|
||||
export type RuleQuery = t.TypeOf<typeof RuleQuery>;
|
||||
export const RuleQuery = t.string;
|
||||
|
||||
/**
|
||||
* TODO: Right now the filters is an "unknown", when it could more than likely
|
||||
* become the actual ESFilter as a type.
|
||||
*/
|
||||
export type RuleFilterArray = t.TypeOf<typeof RuleFilterArray>; // Filters are not easily type-able yet
|
||||
export const RuleFilterArray = t.array(t.unknown); // Filters are not easily type-able yet
|
|
@ -8,9 +8,6 @@
|
|||
import * as t from 'io-ts';
|
||||
import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// Related integrations
|
||||
|
||||
/**
|
||||
* Related integration is a potential dependency of a rule. It's assumed that if the user installs
|
||||
* one of the related integrations of a rule, the rule might start to work properly because it will
|
||||
|
@ -74,72 +71,3 @@ export const RelatedIntegration = t.exact(
|
|||
*/
|
||||
export type RelatedIntegrationArray = t.TypeOf<typeof RelatedIntegrationArray>;
|
||||
export const RelatedIntegrationArray = t.array(RelatedIntegration);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// Required fields
|
||||
|
||||
/**
|
||||
* Almost all types of Security rules check source event documents for a match to some kind of
|
||||
* query or filter. If a document has certain field with certain values, then it's a match and
|
||||
* the rule will generate an alert.
|
||||
*
|
||||
* Required field is an event field that must be present in the source indices of a given rule.
|
||||
*
|
||||
* @example
|
||||
* const standardEcsField: RequiredField = {
|
||||
* name: 'event.action',
|
||||
* type: 'keyword',
|
||||
* ecs: true,
|
||||
* };
|
||||
*
|
||||
* @example
|
||||
* const nonEcsField: RequiredField = {
|
||||
* name: 'winlog.event_data.AttributeLDAPDisplayName',
|
||||
* type: 'keyword',
|
||||
* ecs: false,
|
||||
* };
|
||||
*/
|
||||
export const RequiredField = t.exact(
|
||||
t.type({
|
||||
name: NonEmptyString,
|
||||
type: NonEmptyString,
|
||||
ecs: t.boolean,
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of event fields that must be present in the source indices of a given rule.
|
||||
*
|
||||
* @example
|
||||
* const x: RequiredFieldArray = [
|
||||
* {
|
||||
* name: 'event.action',
|
||||
* type: 'keyword',
|
||||
* ecs: true,
|
||||
* },
|
||||
* {
|
||||
* name: 'event.code',
|
||||
* type: 'keyword',
|
||||
* ecs: true,
|
||||
* },
|
||||
* {
|
||||
* name: 'winlog.event_data.AttributeLDAPDisplayName',
|
||||
* type: 'keyword',
|
||||
* ecs: false,
|
||||
* },
|
||||
* ];
|
||||
*/
|
||||
export type RequiredFieldArray = t.TypeOf<typeof RequiredFieldArray>;
|
||||
export const RequiredFieldArray = t.array(RequiredField);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// Setup guide
|
||||
|
||||
/**
|
||||
* Any instructions for the user for setting up their environment in order to start receiving
|
||||
* source events for a given rule.
|
||||
*
|
||||
* It's a multiline text. Markdown is supported.
|
||||
*/
|
||||
export type SetupGuide = t.TypeOf<typeof SetupGuide>;
|
||||
export const SetupGuide = t.string;
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
/**
|
||||
* Almost all types of Security rules check source event documents for a match to some kind of
|
||||
* query or filter. If a document has certain field with certain values, then it's a match and
|
||||
* the rule will generate an alert.
|
||||
*
|
||||
* Required field is an event field that must be present in the source indices of a given rule.
|
||||
*
|
||||
* @example
|
||||
* const standardEcsField: RequiredField = {
|
||||
* name: 'event.action',
|
||||
* type: 'keyword',
|
||||
* ecs: true,
|
||||
* };
|
||||
*
|
||||
* @example
|
||||
* const nonEcsField: RequiredField = {
|
||||
* name: 'winlog.event_data.AttributeLDAPDisplayName',
|
||||
* type: 'keyword',
|
||||
* ecs: false,
|
||||
* };
|
||||
*/
|
||||
export type RequiredField = t.TypeOf<typeof RequiredField>;
|
||||
export const RequiredField = t.exact(
|
||||
t.type({
|
||||
name: NonEmptyString,
|
||||
type: NonEmptyString,
|
||||
ecs: t.boolean,
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of event fields that must be present in the source indices of a given rule.
|
||||
*
|
||||
* @example
|
||||
* const x: RequiredFieldArray = [
|
||||
* {
|
||||
* name: 'event.action',
|
||||
* type: 'keyword',
|
||||
* ecs: true,
|
||||
* },
|
||||
* {
|
||||
* name: 'event.code',
|
||||
* type: 'keyword',
|
||||
* ecs: true,
|
||||
* },
|
||||
* {
|
||||
* name: 'winlog.event_data.AttributeLDAPDisplayName',
|
||||
* type: 'keyword',
|
||||
* ecs: false,
|
||||
* },
|
||||
* ];
|
||||
*/
|
||||
export type RequiredFieldArray = t.TypeOf<typeof RequiredFieldArray>;
|
||||
export const RequiredFieldArray = t.array(RequiredField);
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
|
||||
/**
|
||||
* Outcome is a property of the saved object resolve api
|
||||
* will tell us info about the rule after 8.0 migrations
|
||||
*/
|
||||
export type SavedObjectResolveOutcome = t.TypeOf<typeof SavedObjectResolveOutcome>;
|
||||
export const SavedObjectResolveOutcome = t.union([
|
||||
t.literal('exactMatch'),
|
||||
t.literal('aliasMatch'),
|
||||
t.literal('conflict'),
|
||||
]);
|
||||
|
||||
export type SavedObjectResolveAliasTargetId = t.TypeOf<typeof SavedObjectResolveAliasTargetId>;
|
||||
export const SavedObjectResolveAliasTargetId = t.string;
|
||||
|
||||
export type SavedObjectResolveAliasPurpose = t.TypeOf<typeof SavedObjectResolveAliasPurpose>;
|
||||
export const SavedObjectResolveAliasPurpose = t.union([
|
||||
t.literal('savedObjectConversion'),
|
||||
t.literal('savedObjectImport'),
|
||||
]);
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 * as t from 'io-ts';
|
||||
|
||||
export type TimelineTemplateId = t.TypeOf<typeof TimelineTemplateId>;
|
||||
export const TimelineTemplateId = t.string; // should be non-empty string?
|
||||
|
||||
export type TimelineTemplateTitle = t.TypeOf<typeof TimelineTemplateTitle>;
|
||||
export const TimelineTemplateTitle = t.string; // should be non-empty string?
|
|
@ -7,18 +7,18 @@
|
|||
|
||||
import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../constants';
|
||||
import type {
|
||||
MachineLearningCreateSchema,
|
||||
MachineLearningUpdateSchema,
|
||||
QueryCreateSchema,
|
||||
QueryUpdateSchema,
|
||||
SavedQueryCreateSchema,
|
||||
ThreatMatchCreateSchema,
|
||||
ThresholdCreateSchema,
|
||||
NewTermsCreateSchema,
|
||||
NewTermsUpdateSchema,
|
||||
MachineLearningRuleCreateProps,
|
||||
MachineLearningRuleUpdateProps,
|
||||
QueryRuleCreateProps,
|
||||
QueryRuleUpdateProps,
|
||||
SavedQueryRuleCreateProps,
|
||||
ThreatMatchRuleCreateProps,
|
||||
ThresholdRuleCreateProps,
|
||||
NewTermsRuleCreateProps,
|
||||
NewTermsRuleUpdateProps,
|
||||
} from './rule_schemas';
|
||||
|
||||
export const getCreateRulesSchemaMock = (ruleId = 'rule-1'): QueryCreateSchema => ({
|
||||
export const getCreateRulesSchemaMock = (ruleId = 'rule-1'): QueryRuleCreateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
|
@ -29,7 +29,7 @@ export const getCreateRulesSchemaMock = (ruleId = 'rule-1'): QueryCreateSchema =
|
|||
rule_id: ruleId,
|
||||
});
|
||||
|
||||
export const getCreateRulesSchemaMockWithDataView = (ruleId = 'rule-1'): QueryCreateSchema => ({
|
||||
export const getCreateRulesSchemaMockWithDataView = (ruleId = 'rule-1'): QueryRuleCreateProps => ({
|
||||
data_view_id: 'logs-*',
|
||||
description: 'Detecting root and admin users',
|
||||
name: 'Query with a rule id',
|
||||
|
@ -41,7 +41,9 @@ export const getCreateRulesSchemaMockWithDataView = (ruleId = 'rule-1'): QueryCr
|
|||
rule_id: ruleId,
|
||||
});
|
||||
|
||||
export const getCreateSavedQueryRulesSchemaMock = (ruleId = 'rule-1'): SavedQueryCreateSchema => ({
|
||||
export const getCreateSavedQueryRulesSchemaMock = (
|
||||
ruleId = 'rule-1'
|
||||
): SavedQueryRuleCreateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
|
@ -56,7 +58,7 @@ export const getCreateSavedQueryRulesSchemaMock = (ruleId = 'rule-1'): SavedQuer
|
|||
export const getCreateThreatMatchRulesSchemaMock = (
|
||||
ruleId = 'rule-1',
|
||||
enabled = false
|
||||
): ThreatMatchCreateSchema => ({
|
||||
): ThreatMatchRuleCreateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
enabled,
|
||||
index: ['auditbeat-*'],
|
||||
|
@ -105,7 +107,7 @@ export const getCreateThreatMatchRulesSchemaMock = (
|
|||
|
||||
export const getCreateMachineLearningRulesSchemaMock = (
|
||||
ruleId = 'rule-1'
|
||||
): MachineLearningCreateSchema => ({
|
||||
): MachineLearningRuleCreateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
name: 'Query with a rule id',
|
||||
severity: 'high',
|
||||
|
@ -116,7 +118,7 @@ export const getCreateMachineLearningRulesSchemaMock = (
|
|||
machine_learning_job_id: 'typical-ml-job-id',
|
||||
});
|
||||
|
||||
export const getCreateThresholdRulesSchemaMock = (ruleId = 'rule-1'): ThresholdCreateSchema => ({
|
||||
export const getCreateThresholdRulesSchemaMock = (ruleId = 'rule-1'): ThresholdRuleCreateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
name: 'Query with a rule id',
|
||||
severity: 'high',
|
||||
|
@ -133,7 +135,7 @@ export const getCreateThresholdRulesSchemaMock = (ruleId = 'rule-1'): ThresholdC
|
|||
export const getCreateNewTermsRulesSchemaMock = (
|
||||
ruleId = 'rule-1',
|
||||
enabled = false
|
||||
): NewTermsCreateSchema => ({
|
||||
): NewTermsRuleCreateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
enabled,
|
||||
index: ['auditbeat-*'],
|
||||
|
@ -152,7 +154,7 @@ export const getCreateNewTermsRulesSchemaMock = (
|
|||
|
||||
export const getUpdateRulesSchemaMock = (
|
||||
id = '04128c15-0d1b-4716-a4c5-46997ac7f3bd'
|
||||
): QueryUpdateSchema => ({
|
||||
): QueryRuleUpdateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
|
@ -165,7 +167,7 @@ export const getUpdateRulesSchemaMock = (
|
|||
|
||||
export const getUpdateMachineLearningSchemaMock = (
|
||||
id = '04128c15-0d1b-4716-a4c5-46997ac7f3bd'
|
||||
): MachineLearningUpdateSchema => ({
|
||||
): MachineLearningRuleUpdateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
name: 'Query with a rule id',
|
||||
severity: 'high',
|
||||
|
@ -178,7 +180,7 @@ export const getUpdateMachineLearningSchemaMock = (
|
|||
|
||||
export const getUpdateNewTermsSchemaMock = (
|
||||
id = '04128c15-0d1b-4716-a4c5-46997ac7f3bd'
|
||||
): NewTermsUpdateSchema => ({
|
||||
): NewTermsRuleUpdateProps => ({
|
||||
description: 'Detecting root and admin users',
|
||||
index: ['auditbeat-*'],
|
||||
name: 'Query with a rule id',
|
|
@ -6,11 +6,13 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import type { CreateRulesSchema, SavedQueryCreateSchema } from './rule_schemas';
|
||||
import { createRulesSchema, responseSchema } from './rule_schemas';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import { getListArrayMock } from '../../schemas/types/lists.mock';
|
||||
import type { SavedQueryRuleCreateProps } from './rule_schemas';
|
||||
import { RuleCreateProps } from './rule_schemas';
|
||||
import {
|
||||
getCreateSavedQueryRulesSchemaMock,
|
||||
getCreateThreatMatchRulesSchemaMock,
|
||||
|
@ -18,14 +20,14 @@ import {
|
|||
getCreateThresholdRulesSchemaMock,
|
||||
getCreateRulesSchemaMockWithDataView,
|
||||
getCreateMachineLearningRulesSchemaMock,
|
||||
} from './rule_schemas.mock';
|
||||
import { getListArrayMock } from '../types/lists.mock';
|
||||
} from './rule_request_schema.mock';
|
||||
import { buildResponseRuleSchema } from './build_rule_schemas';
|
||||
|
||||
describe('rules schema', () => {
|
||||
test('empty objects do not validate', () => {
|
||||
const payload = {};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(message.errors.length).toBeGreaterThan(0);
|
||||
|
@ -33,12 +35,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('made up values do not validate', () => {
|
||||
const payload: CreateRulesSchema & { madeUp: string } = {
|
||||
const payload: RuleCreateProps & { madeUp: string } = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
madeUp: 'hi',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "madeUp"']);
|
||||
|
@ -46,11 +48,11 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(message.errors.length).toBeGreaterThan(0);
|
||||
|
@ -58,12 +60,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(message.errors.length).toBeGreaterThan(0);
|
||||
|
@ -71,13 +73,13 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(message.errors.length).toBeGreaterThan(0);
|
||||
|
@ -85,14 +87,14 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
to: 'now',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(message.errors.length).toBeGreaterThan(0);
|
||||
|
@ -100,7 +102,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -108,7 +110,7 @@ describe('rules schema', () => {
|
|||
name: 'some-name',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(message.errors.length).toBeGreaterThan(0);
|
||||
|
@ -116,7 +118,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -125,7 +127,7 @@ describe('rules schema', () => {
|
|||
severity: 'low',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(message.errors.length).toBeGreaterThan(0);
|
||||
|
@ -133,7 +135,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -143,7 +145,7 @@ describe('rules schema', () => {
|
|||
type: 'query',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -153,7 +155,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type, interval] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -164,7 +166,7 @@ describe('rules schema', () => {
|
|||
type: 'query',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -174,7 +176,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type, interval, index] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -186,7 +188,7 @@ describe('rules schema', () => {
|
|||
index: ['index-1'],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -196,7 +198,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, name, severity, type, query, index, interval] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
risk_score: 50,
|
||||
description: 'some description',
|
||||
|
@ -210,7 +212,7 @@ describe('rules schema', () => {
|
|||
interval: '5m',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -218,7 +220,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, query, language] does not validate', () => {
|
||||
const payload: Partial<CreateRulesSchema> = {
|
||||
const payload: Partial<RuleCreateProps> = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -232,7 +234,7 @@ describe('rules schema', () => {
|
|||
language: 'kuery',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -242,7 +244,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
risk_score: 50,
|
||||
description: 'some description',
|
||||
|
@ -257,7 +259,7 @@ describe('rules schema', () => {
|
|||
language: 'kuery',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -265,7 +267,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score, output_index] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
output_index: '.siem-signals',
|
||||
risk_score: 50,
|
||||
|
@ -281,7 +283,7 @@ describe('rules schema', () => {
|
|||
language: 'kuery',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -289,7 +291,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -302,7 +304,7 @@ describe('rules schema', () => {
|
|||
risk_score: 50,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -310,7 +312,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
author: [],
|
||||
severity_mapping: [],
|
||||
risk_score_mapping: [],
|
||||
|
@ -327,7 +329,7 @@ describe('rules schema', () => {
|
|||
type: 'query',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -335,12 +337,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can send in a namespace', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
namespace: 'a namespace',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -348,12 +350,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can send in an empty array to threat', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
threat: [],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -361,7 +363,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threat] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
output_index: '.siem-signals',
|
||||
risk_score: 50,
|
||||
|
@ -392,7 +394,7 @@ describe('rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -400,12 +402,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('allows references to be sent as valid', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
references: ['index-1'],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -413,12 +415,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('references cannot be numbers', () => {
|
||||
const payload: Omit<CreateRulesSchema, 'references'> & { references: number[] } = {
|
||||
const payload: Omit<RuleCreateProps, 'references'> & { references: number[] } = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
references: [5],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "references"']);
|
||||
|
@ -426,12 +428,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('indexes cannot be numbers', () => {
|
||||
const payload: Omit<CreateRulesSchema, 'index'> & { index: number[] } = {
|
||||
const payload: Omit<RuleCreateProps, 'index'> & { index: number[] } = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
index: [5],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to "index"']);
|
||||
|
@ -439,12 +441,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('saved_query type can have filters with it', () => {
|
||||
const payload: SavedQueryCreateSchema = {
|
||||
const payload: SavedQueryRuleCreateProps = {
|
||||
...getCreateSavedQueryRulesSchemaMock(),
|
||||
filters: [],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -457,7 +459,7 @@ describe('rules schema', () => {
|
|||
filters: 'some string',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -467,12 +469,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('language validates with kuery', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
language: 'kuery',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -480,12 +482,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('language validates with lucene', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
language: 'lucene',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -498,7 +500,7 @@ describe('rules schema', () => {
|
|||
language: 'something-made-up',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -508,12 +510,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('max_signals cannot be negative', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
max_signals: -1,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -523,12 +525,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('max_signals cannot be zero', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
max_signals: 0,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "0" supplied to "max_signals"']);
|
||||
|
@ -536,12 +538,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('max_signals can be 1', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
max_signals: 1,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -549,12 +551,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can optionally send in an array of tags', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
tags: ['tag_1', 'tag_2'],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -567,7 +569,7 @@ describe('rules schema', () => {
|
|||
tags: [0, 1, 2],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -599,7 +601,7 @@ describe('rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -625,7 +627,7 @@ describe('rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -649,7 +651,7 @@ describe('rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -657,12 +659,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can optionally send in an array of false positives', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
false_positives: ['false_1', 'false_2'],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -675,7 +677,7 @@ describe('rules schema', () => {
|
|||
false_positives: [5, 4],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -691,7 +693,7 @@ describe('rules schema', () => {
|
|||
immutable: 5,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "immutable"']);
|
||||
|
@ -699,12 +701,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot set the risk_score to 101', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
risk_score: 101,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -714,12 +716,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You cannot set the risk_score to -1', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
risk_score: -1,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "-1" supplied to "risk_score"']);
|
||||
|
@ -727,12 +729,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can set the risk_score to 0', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
risk_score: 0,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -740,12 +742,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can set the risk_score to 100', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
risk_score: 100,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -753,14 +755,14 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can set meta to any object you want', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
meta: {
|
||||
somethingMadeUp: { somethingElse: true },
|
||||
},
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -773,7 +775,7 @@ describe('rules schema', () => {
|
|||
meta: 'should not work',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -784,12 +786,12 @@ describe('rules schema', () => {
|
|||
|
||||
test('You can omit the query string when filters are present', () => {
|
||||
const { query, ...noQuery } = getCreateRulesSchemaMock();
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...noQuery,
|
||||
filters: [],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -797,13 +799,13 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('validates with timeline_id and timeline_title', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
timeline_id: 'timeline-id',
|
||||
timeline_title: 'timeline-title',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -816,7 +818,7 @@ describe('rules schema', () => {
|
|||
severity: 'junk',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "junk" supplied to "severity"']);
|
||||
|
@ -829,7 +831,7 @@ describe('rules schema', () => {
|
|||
actions: [{ id: 'id', action_type_id: 'action_type_id', params: {} }],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -844,7 +846,7 @@ describe('rules schema', () => {
|
|||
actions: [{ group: 'group', action_type_id: 'action_type_id', params: {} }],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -859,7 +861,7 @@ describe('rules schema', () => {
|
|||
actions: [{ group: 'group', id: 'id', params: {} }],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -874,7 +876,7 @@ describe('rules schema', () => {
|
|||
actions: [{ group: 'group', id: 'id', action_type_id: 'action_type_id' }],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -896,7 +898,7 @@ describe('rules schema', () => {
|
|||
],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -907,12 +909,12 @@ describe('rules schema', () => {
|
|||
|
||||
describe('note', () => {
|
||||
test('You can set note to a string', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
note: '# documentation markdown here',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -920,12 +922,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('You can set note to an empty string', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
note: '',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -940,7 +942,7 @@ describe('rules schema', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -950,12 +952,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('empty name is not valid', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
name: '',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "name"']);
|
||||
|
@ -963,12 +965,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('empty description is not valid', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
...getCreateRulesSchemaMock(),
|
||||
description: '',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -978,7 +980,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, note] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -992,7 +994,7 @@ describe('rules schema', () => {
|
|||
note: '# some markdown',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1001,7 +1003,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('machine_learning type does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
type: 'machine_learning',
|
||||
anomaly_threshold: 50,
|
||||
machine_learning_job_id: 'linux_anomalous_network_activity_ecs',
|
||||
|
@ -1023,7 +1025,7 @@ describe('rules schema', () => {
|
|||
rule_id: 'rule-1',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1033,7 +1035,7 @@ describe('rules schema', () => {
|
|||
test('saved_id is required when type is saved_query and will not validate without it', () => {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
const { saved_id, ...payload } = getCreateSavedQueryRulesSchemaMock();
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1044,7 +1046,7 @@ describe('rules schema', () => {
|
|||
|
||||
test('threshold is required when type is threshold and will not validate without it', () => {
|
||||
const { threshold, ...payload } = getCreateThresholdRulesSchemaMock();
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1056,7 +1058,7 @@ describe('rules schema', () => {
|
|||
test('threshold rules fail validation if threshold is not greater than 0', () => {
|
||||
const payload = getCreateThresholdRulesSchemaMock();
|
||||
payload.threshold.value = 0;
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1067,7 +1069,7 @@ describe('rules schema', () => {
|
|||
|
||||
describe('exception_list', () => {
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filters, risk_score, note, and exceptions_list] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -1083,7 +1085,7 @@ describe('rules schema', () => {
|
|||
exceptions_list: getListArrayMock(),
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1091,7 +1093,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, note, and empty exceptions_list] does validate', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -1107,7 +1109,7 @@ describe('rules schema', () => {
|
|||
exceptions_list: [],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1131,7 +1133,7 @@ describe('rules schema', () => {
|
|||
exceptions_list: [{ id: 'uuid_here', namespace_type: 'not a namespace type' }],
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1143,7 +1145,7 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('[rule_id, description, from, to, index, name, severity, interval, type, filters, risk_score, note, and non-existent exceptions_list] does validate with empty exceptions_list', () => {
|
||||
const payload: CreateRulesSchema = {
|
||||
const payload: RuleCreateProps = {
|
||||
rule_id: 'rule-1',
|
||||
description: 'some description',
|
||||
from: 'now-5m',
|
||||
|
@ -1158,7 +1160,7 @@ describe('rules schema', () => {
|
|||
note: '# some markdown',
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1169,7 +1171,7 @@ describe('rules schema', () => {
|
|||
describe('threat_match', () => {
|
||||
test('You can set a threat query, index, mapping, filters when creating a rule', () => {
|
||||
const payload = getCreateThreatMatchRulesSchemaMock();
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1180,7 +1182,7 @@ describe('rules schema', () => {
|
|||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
const { threat_index, threat_query, threat_mapping, ...payload } =
|
||||
getCreateThreatMatchRulesSchemaMock();
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1194,7 +1196,7 @@ describe('rules schema', () => {
|
|||
test('fails validation when threat_mapping is an empty array', () => {
|
||||
const payload = getCreateThreatMatchRulesSchemaMock();
|
||||
payload.threat_mapping = [];
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1207,7 +1209,7 @@ describe('rules schema', () => {
|
|||
describe('data_view_id', () => {
|
||||
test('validates when "data_view_id" and index are defined', () => {
|
||||
const payload = { ...getCreateRulesSchemaMockWithDataView(), index: ['auditbeat-*'] };
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
|
@ -1215,12 +1217,12 @@ describe('rules schema', () => {
|
|||
});
|
||||
|
||||
test('"data_view_id" cannot be a number', () => {
|
||||
const payload: Omit<CreateRulesSchema, 'data_view_id'> & { data_view_id: number } = {
|
||||
const payload: Omit<RuleCreateProps, 'data_view_id'> & { data_view_id: number } = {
|
||||
...getCreateRulesSchemaMockWithDataView(),
|
||||
data_view_id: 5,
|
||||
};
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
|
@ -1232,7 +1234,7 @@ describe('rules schema', () => {
|
|||
test('it should validate a type of "query" with "data_view_id" defined', () => {
|
||||
const payload = getCreateRulesSchemaMockWithDataView();
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = getCreateRulesSchemaMockWithDataView();
|
||||
|
@ -1244,7 +1246,7 @@ describe('rules schema', () => {
|
|||
test('it should validate a type of "saved_query" with "data_view_id" defined', () => {
|
||||
const payload = { ...getCreateSavedQueryRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = { ...getCreateSavedQueryRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
@ -1256,7 +1258,7 @@ describe('rules schema', () => {
|
|||
test('it should validate a type of "threat_match" with "data_view_id" defined', () => {
|
||||
const payload = { ...getCreateThreatMatchRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = { ...getCreateThreatMatchRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
@ -1268,7 +1270,7 @@ describe('rules schema', () => {
|
|||
test('it should validate a type of "threshold" with "data_view_id" defined', () => {
|
||||
const payload = { ...getCreateThresholdRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = { ...getCreateThresholdRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
@ -1280,7 +1282,7 @@ describe('rules schema', () => {
|
|||
test('it should NOT validate a type of "machine_learning" with "data_view_id" defined', () => {
|
||||
const payload = { ...getCreateMachineLearningRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = createRulesSchema.decode(payload);
|
||||
const decoded = RuleCreateProps.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -1301,7 +1303,11 @@ describe('rules schema', () => {
|
|||
testDefaultableString: t.string,
|
||||
},
|
||||
};
|
||||
const schema = responseSchema(testSchema.required, testSchema.optional, testSchema.defaultable);
|
||||
const schema = buildResponseRuleSchema(
|
||||
testSchema.required,
|
||||
testSchema.optional,
|
||||
testSchema.defaultable
|
||||
);
|
||||
|
||||
describe('required fields', () => {
|
||||
test('should allow required fields with the correct type', () => {
|
|
@ -7,18 +7,18 @@
|
|||
|
||||
import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../constants';
|
||||
import type {
|
||||
EqlResponseSchema,
|
||||
MachineLearningResponseSchema,
|
||||
QueryResponseSchema,
|
||||
SavedQueryResponseSchema,
|
||||
SharedResponseSchema,
|
||||
ThreatMatchResponseSchema,
|
||||
} from '../request';
|
||||
import { getListArrayMock } from '../types/lists.mock';
|
||||
EqlRule,
|
||||
MachineLearningRule,
|
||||
QueryRule,
|
||||
SavedQueryRule,
|
||||
SharedResponseProps,
|
||||
ThreatMatchRule,
|
||||
} from './rule_schemas';
|
||||
import { getListArrayMock } from '../../schemas/types/lists.mock';
|
||||
|
||||
export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z';
|
||||
|
||||
const getResponseBaseParams = (anchorDate: string = ANCHOR_DATE): SharedResponseSchema => ({
|
||||
const getResponseBaseParams = (anchorDate: string = ANCHOR_DATE): SharedResponseProps => ({
|
||||
author: [],
|
||||
id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9',
|
||||
created_at: new Date(anchorDate).toISOString(),
|
||||
|
@ -65,7 +65,7 @@ const getResponseBaseParams = (anchorDate: string = ANCHOR_DATE): SharedResponse
|
|||
namespace: undefined,
|
||||
});
|
||||
|
||||
export const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE): QueryResponseSchema => ({
|
||||
export const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE): QueryRule => ({
|
||||
...getResponseBaseParams(anchorDate),
|
||||
query: 'user.name: root or user.name: admin',
|
||||
type: 'query',
|
||||
|
@ -76,9 +76,8 @@ export const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE): QueryRespo
|
|||
saved_id: undefined,
|
||||
response_actions: undefined,
|
||||
});
|
||||
export const getSavedQuerySchemaMock = (
|
||||
anchorDate: string = ANCHOR_DATE
|
||||
): SavedQueryResponseSchema => ({
|
||||
|
||||
export const getSavedQuerySchemaMock = (anchorDate: string = ANCHOR_DATE): SavedQueryRule => ({
|
||||
...getResponseBaseParams(anchorDate),
|
||||
query: 'user.name: root or user.name: admin',
|
||||
type: 'saved_query',
|
||||
|
@ -90,9 +89,7 @@ export const getSavedQuerySchemaMock = (
|
|||
response_actions: undefined,
|
||||
});
|
||||
|
||||
export const getRulesMlSchemaMock = (
|
||||
anchorDate: string = ANCHOR_DATE
|
||||
): MachineLearningResponseSchema => {
|
||||
export const getRulesMlSchemaMock = (anchorDate: string = ANCHOR_DATE): MachineLearningRule => {
|
||||
return {
|
||||
...getResponseBaseParams(anchorDate),
|
||||
type: 'machine_learning',
|
||||
|
@ -101,9 +98,7 @@ export const getRulesMlSchemaMock = (
|
|||
};
|
||||
};
|
||||
|
||||
export const getThreatMatchingSchemaMock = (
|
||||
anchorDate: string = ANCHOR_DATE
|
||||
): ThreatMatchResponseSchema => {
|
||||
export const getThreatMatchingSchemaMock = (anchorDate: string = ANCHOR_DATE): ThreatMatchRule => {
|
||||
return {
|
||||
...getResponseBaseParams(anchorDate),
|
||||
type: 'threat_match',
|
||||
|
@ -145,9 +140,7 @@ export const getThreatMatchingSchemaMock = (
|
|||
* Useful for e2e backend tests where it doesn't have date time and other
|
||||
* server side properties attached to it.
|
||||
*/
|
||||
export const getThreatMatchingSchemaPartialMock = (
|
||||
enabled = false
|
||||
): Partial<ThreatMatchResponseSchema> => {
|
||||
export const getThreatMatchingSchemaPartialMock = (enabled = false): Partial<ThreatMatchRule> => {
|
||||
return {
|
||||
author: [],
|
||||
created_by: 'elastic',
|
||||
|
@ -216,7 +209,7 @@ export const getThreatMatchingSchemaPartialMock = (
|
|||
};
|
||||
};
|
||||
|
||||
export const getRulesEqlSchemaMock = (anchorDate: string = ANCHOR_DATE): EqlResponseSchema => {
|
||||
export const getRulesEqlSchemaMock = (anchorDate: string = ANCHOR_DATE): EqlRule => {
|
||||
return {
|
||||
...getResponseBaseParams(anchorDate),
|
||||
language: 'eql',
|
|
@ -7,23 +7,22 @@
|
|||
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import { RuleResponse } from './rule_schemas';
|
||||
import {
|
||||
getRulesSchemaMock,
|
||||
getRulesMlSchemaMock,
|
||||
getSavedQuerySchemaMock,
|
||||
getThreatMatchingSchemaMock,
|
||||
getRulesEqlSchemaMock,
|
||||
} from './rules_schema.mocks';
|
||||
import { fullResponseSchema } from '../request';
|
||||
import type { FullResponseSchema } from '../request';
|
||||
} from './rule_response_schema.mock';
|
||||
|
||||
describe('rules_schema', () => {
|
||||
describe('Rule response schema', () => {
|
||||
test('it should validate a type of "query" without anything extra', () => {
|
||||
const payload = getRulesSchemaMock();
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = getRulesSchemaMock();
|
||||
|
@ -33,10 +32,10 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a type of "query" when it has extra data', () => {
|
||||
const payload: FullResponseSchema & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
const payload: RuleResponse & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
payload.invalid_extra_data = 'invalid_extra_data';
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -45,10 +44,10 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate invalid_data for the type', () => {
|
||||
const payload: Omit<FullResponseSchema, 'type'> & { type: string } = getRulesSchemaMock();
|
||||
const payload: Omit<RuleResponse, 'type'> & { type: string } = getRulesSchemaMock();
|
||||
payload.type = 'invalid_data';
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -57,11 +56,11 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate a type of "query" with a saved_id together', () => {
|
||||
const payload: FullResponseSchema & { saved_id?: string } = getRulesSchemaMock();
|
||||
const payload: RuleResponse & { saved_id?: string } = getRulesSchemaMock();
|
||||
payload.type = 'query';
|
||||
payload.saved_id = 'save id 123';
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -72,7 +71,7 @@ describe('rules_schema', () => {
|
|||
test('it should validate a type of "saved_query" with a "saved_id" dependent', () => {
|
||||
const payload = getSavedQuerySchemaMock();
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = getSavedQuerySchemaMock();
|
||||
|
@ -82,11 +81,11 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a type of "saved_query" without a "saved_id" dependent', () => {
|
||||
const payload: FullResponseSchema & { saved_id?: string } = getSavedQuerySchemaMock();
|
||||
const payload: RuleResponse & { saved_id?: string } = getSavedQuerySchemaMock();
|
||||
// @ts-expect-error
|
||||
delete payload.saved_id;
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -97,11 +96,11 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a type of "saved_query" when it has extra data', () => {
|
||||
const payload: FullResponseSchema & { saved_id?: string; invalid_extra_data?: string } =
|
||||
const payload: RuleResponse & { saved_id?: string; invalid_extra_data?: string } =
|
||||
getSavedQuerySchemaMock();
|
||||
payload.invalid_extra_data = 'invalid_extra_data';
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -114,7 +113,7 @@ describe('rules_schema', () => {
|
|||
payload.timeline_id = 'some timeline id';
|
||||
payload.timeline_title = 'some timeline title';
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = getRulesSchemaMock();
|
||||
|
@ -126,12 +125,12 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate a type of "timeline_id" if there is "timeline_title" dependent when it has extra invalid data', () => {
|
||||
const payload: FullResponseSchema & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
const payload: RuleResponse & { invalid_extra_data?: string } = getRulesSchemaMock();
|
||||
payload.timeline_id = 'some timeline id';
|
||||
payload.timeline_title = 'some timeline title';
|
||||
payload.invalid_extra_data = 'invalid_extra_data';
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -143,7 +142,7 @@ describe('rules_schema', () => {
|
|||
test('it should validate an empty array for "exceptions_list"', () => {
|
||||
const payload = getRulesSchemaMock();
|
||||
payload.exceptions_list = [];
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = getRulesSchemaMock();
|
||||
|
@ -153,11 +152,11 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should NOT validate when "exceptions_list" is not expected type', () => {
|
||||
const payload: Omit<FullResponseSchema, 'exceptions_list'> & {
|
||||
const payload: Omit<RuleResponse, 'exceptions_list'> & {
|
||||
exceptions_list?: string;
|
||||
} = { ...getRulesSchemaMock(), exceptions_list: 'invalid_data' };
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -172,7 +171,7 @@ describe('rules_schema', () => {
|
|||
test('it should validate a type of "query" with "data_view_id" defined', () => {
|
||||
const payload = { ...getRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = { ...getRulesSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
@ -182,14 +181,14 @@ describe('rules_schema', () => {
|
|||
});
|
||||
|
||||
test('it should validate a type of "saved_query" with "data_view_id" defined', () => {
|
||||
const payload: FullResponseSchema & { saved_id?: string; data_view_id?: string } =
|
||||
const payload: RuleResponse & { saved_id?: string; data_view_id?: string } =
|
||||
getSavedQuerySchemaMock();
|
||||
payload.data_view_id = 'logs-*';
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected: FullResponseSchema & { saved_id?: string; data_view_id?: string } =
|
||||
const expected: RuleResponse & { saved_id?: string; data_view_id?: string } =
|
||||
getSavedQuerySchemaMock();
|
||||
|
||||
expected.data_view_id = 'logs-*';
|
||||
|
@ -201,7 +200,7 @@ describe('rules_schema', () => {
|
|||
test('it should validate a type of "eql" with "data_view_id" defined', () => {
|
||||
const payload = { ...getRulesEqlSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = { ...getRulesEqlSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
@ -213,7 +212,7 @@ describe('rules_schema', () => {
|
|||
test('it should validate a type of "threat_match" with "data_view_id" defined', () => {
|
||||
const payload = { ...getThreatMatchingSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
const expected = { ...getThreatMatchingSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
@ -225,7 +224,7 @@ describe('rules_schema', () => {
|
|||
test('it should NOT validate a type of "machine_learning" with "data_view_id" defined', () => {
|
||||
const payload = { ...getRulesMlSchemaMock(), data_view_id: 'logs-*' };
|
||||
|
||||
const decoded = fullResponseSchema.decode(payload);
|
||||
const decoded = RuleResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue