mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Migrate rules management endpoints to OpenAPI and code generation (#165091)
**Part of: https://github.com/elastic/security-team/issues/7491** ## Summary Migrated Detection Engine APIs to OpenAPI schema and code generation: - [x] `PUT /api/detection_engine/rules/prepackaged` - [x] `POST /api/detection_engine/rules/_export` - [x] `POST /api/detection_engine/rules/_import` - [x] `GET /api/detection_engine/tags` - [x] `GET /internal/detection_engine/rules/{ruleId}/execution/results`
This commit is contained in:
parent
b05397366e
commit
06502b9cdc
80 changed files with 2100 additions and 992 deletions
|
@ -43,9 +43,17 @@
|
|||
{{~/if~}}
|
||||
|
||||
{{~#if (eq type "integer")}}
|
||||
{{> zod_schema_item}}
|
||||
z.coerce.number().int()
|
||||
{{~#if minimum includeZero=true}}.min({{minimum}}){{/if~}}
|
||||
{{~#if maximum includeZero=true}}.max({{maximum}}){{/if~}}
|
||||
{{~#if (eq requiredBool false)}}.optional(){{/if~}}
|
||||
{{~#if (defined default)}}.default({{{toJSON default}}}){{/if~}}
|
||||
{{~/if~}}
|
||||
|
||||
{{~#if (eq type "number")}}
|
||||
{{> zod_schema_item}}
|
||||
z.coerce.number()
|
||||
{{~#if minimum includeZero=true}}.min({{minimum}}){{/if~}}
|
||||
{{~#if maximum includeZero=true}}.max({{maximum}}){{/if~}}
|
||||
{{~#if (eq requiredBool false)}}.optional(){{/if~}}
|
||||
{{~#if (defined default)}}.default({{{toJSON default}}}){{/if~}}
|
||||
{{~/if~}}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
import { RuleSignatureId } from './rule_schema/common_attributes.gen';
|
||||
|
||||
export type ErrorSchema = z.infer<typeof ErrorSchema>;
|
||||
export const ErrorSchema = z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
rule_id: RuleSignatureId.optional(),
|
||||
list_id: z.string().min(1).optional(),
|
||||
item_id: z.string().min(1).optional(),
|
||||
error: z.object({
|
||||
status_code: z.number().int().min(400),
|
||||
message: z.string(),
|
||||
}),
|
||||
})
|
||||
.strict();
|
|
@ -4,12 +4,13 @@ info:
|
|||
version: 'not applicable'
|
||||
paths: {}
|
||||
components:
|
||||
x-codegen-enabled: false
|
||||
x-codegen-enabled: true
|
||||
schemas:
|
||||
ErrorSchema:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
additionalProperties: false
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
|
|
|
@ -8,14 +8,13 @@
|
|||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import type { ErrorSchema } from './error_schema';
|
||||
import { errorSchema } from './error_schema';
|
||||
import { ErrorSchema } from './error_schema';
|
||||
import { getErrorSchemaMock } from './error_schema.mock';
|
||||
|
||||
describe('error_schema', () => {
|
||||
test('it should validate an error with a UUID given for id', () => {
|
||||
const error = getErrorSchemaMock();
|
||||
const decoded = errorSchema.decode(getErrorSchemaMock());
|
||||
const decoded = ErrorSchema.decode(getErrorSchemaMock());
|
||||
const checked = exactCheck(error, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -25,7 +24,7 @@ describe('error_schema', () => {
|
|||
|
||||
test('it should validate an error with a plain string given for id since sometimes we echo the user id which might not be a UUID back out to them', () => {
|
||||
const error = getErrorSchemaMock('fake id');
|
||||
const decoded = errorSchema.decode(error);
|
||||
const decoded = ErrorSchema.decode(error);
|
||||
const checked = exactCheck(error, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -37,7 +36,7 @@ describe('error_schema', () => {
|
|||
type InvalidError = ErrorSchema & { invalid_extra_data?: string };
|
||||
const error: InvalidError = getErrorSchemaMock();
|
||||
error.invalid_extra_data = 'invalid_extra_data';
|
||||
const decoded = errorSchema.decode(error);
|
||||
const decoded = ErrorSchema.decode(error);
|
||||
const checked = exactCheck(error, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
@ -49,7 +48,7 @@ describe('error_schema', () => {
|
|||
const error = getErrorSchemaMock();
|
||||
// @ts-expect-error
|
||||
delete error.error;
|
||||
const decoded = errorSchema.decode(error);
|
||||
const decoded = ErrorSchema.decode(error);
|
||||
const checked = exactCheck(error, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
|
|
|
@ -31,5 +31,5 @@ const required = t.exact(
|
|||
})
|
||||
);
|
||||
|
||||
export const errorSchema = t.intersection([partial, required]);
|
||||
export type ErrorSchema = t.TypeOf<typeof errorSchema>;
|
||||
export const ErrorSchema = t.intersection([partial, required]);
|
||||
export type ErrorSchema = t.TypeOf<typeof ErrorSchema>;
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A universally unique identifier
|
||||
*/
|
||||
export type UUID = z.infer<typeof UUID>;
|
||||
export const UUID = z.string();
|
||||
|
||||
export type RuleObjectId = z.infer<typeof RuleObjectId>;
|
||||
export const RuleObjectId = z.string();
|
||||
|
||||
/**
|
||||
* Could be any string, not necessarily a UUID
|
||||
*/
|
||||
export type RuleSignatureId = z.infer<typeof RuleSignatureId>;
|
||||
export const RuleSignatureId = z.string();
|
||||
|
||||
export type RuleName = z.infer<typeof RuleName>;
|
||||
export const RuleName = z.string().min(1);
|
||||
|
||||
export type RuleDescription = z.infer<typeof RuleDescription>;
|
||||
export const RuleDescription = z.string().min(1);
|
||||
|
||||
export type RuleVersion = z.infer<typeof RuleVersion>;
|
||||
export const RuleVersion = z.string();
|
||||
|
||||
export type IsRuleImmutable = z.infer<typeof IsRuleImmutable>;
|
||||
export const IsRuleImmutable = z.boolean();
|
||||
|
||||
export type IsRuleEnabled = z.infer<typeof IsRuleEnabled>;
|
||||
export const IsRuleEnabled = z.boolean();
|
||||
|
||||
export type RuleTagArray = z.infer<typeof RuleTagArray>;
|
||||
export const RuleTagArray = z.array(z.string());
|
||||
|
||||
export type RuleMetadata = z.infer<typeof RuleMetadata>;
|
||||
export const RuleMetadata = z.object({});
|
||||
|
||||
export type RuleLicense = z.infer<typeof RuleLicense>;
|
||||
export const RuleLicense = z.string();
|
||||
|
||||
export type RuleAuthorArray = z.infer<typeof RuleAuthorArray>;
|
||||
export const RuleAuthorArray = z.array(z.string());
|
||||
|
||||
export type RuleFalsePositiveArray = z.infer<typeof RuleFalsePositiveArray>;
|
||||
export const RuleFalsePositiveArray = z.array(z.string());
|
||||
|
||||
export type RuleReferenceArray = z.infer<typeof RuleReferenceArray>;
|
||||
export const RuleReferenceArray = z.array(z.string());
|
||||
|
||||
export type InvestigationGuide = z.infer<typeof InvestigationGuide>;
|
||||
export const InvestigationGuide = z.string();
|
||||
|
||||
export type SetupGuide = z.infer<typeof SetupGuide>;
|
||||
export const SetupGuide = z.string();
|
||||
|
||||
export type BuildingBlockType = z.infer<typeof BuildingBlockType>;
|
||||
export const BuildingBlockType = z.string();
|
||||
|
||||
export type AlertsIndex = z.infer<typeof AlertsIndex>;
|
||||
export const AlertsIndex = z.string();
|
||||
|
||||
export type AlertsIndexNamespace = z.infer<typeof AlertsIndexNamespace>;
|
||||
export const AlertsIndexNamespace = z.string();
|
||||
|
||||
export type MaxSignals = z.infer<typeof MaxSignals>;
|
||||
export const MaxSignals = z.number().int().min(1);
|
||||
|
||||
export type Subtechnique = z.infer<typeof Subtechnique>;
|
||||
export const Subtechnique = z.object({
|
||||
/**
|
||||
* Subtechnique ID
|
||||
*/
|
||||
id: z.string(),
|
||||
/**
|
||||
* Subtechnique name
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* Subtechnique reference
|
||||
*/
|
||||
reference: z.string(),
|
||||
});
|
||||
|
||||
export type Technique = z.infer<typeof Technique>;
|
||||
export const Technique = z.object({
|
||||
/**
|
||||
* Technique ID
|
||||
*/
|
||||
id: z.string(),
|
||||
/**
|
||||
* Technique name
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* Technique reference
|
||||
*/
|
||||
reference: z.string(),
|
||||
/**
|
||||
* Array containing more specific information on the attack technique
|
||||
*/
|
||||
subtechnique: z.array(Subtechnique).optional(),
|
||||
});
|
||||
|
||||
export type Threat = z.infer<typeof Threat>;
|
||||
export const Threat = z.object({
|
||||
/**
|
||||
* Relevant attack framework
|
||||
*/
|
||||
framework: z.string(),
|
||||
tactic: z.object({
|
||||
/**
|
||||
* Tactic ID
|
||||
*/
|
||||
id: z.string(),
|
||||
/**
|
||||
* Tactic name
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* Tactic reference
|
||||
*/
|
||||
reference: z.string(),
|
||||
}),
|
||||
/**
|
||||
* Array containing information on the attack techniques (optional)
|
||||
*/
|
||||
technique: z.array(Technique).optional(),
|
||||
});
|
||||
|
||||
export type ThreatArray = z.infer<typeof ThreatArray>;
|
||||
export const ThreatArray = z.array(Threat);
|
||||
|
||||
export type IndexPatternArray = z.infer<typeof IndexPatternArray>;
|
||||
export const IndexPatternArray = z.array(z.string());
|
||||
|
||||
export type DataViewId = z.infer<typeof DataViewId>;
|
||||
export const DataViewId = z.string();
|
||||
|
||||
export type RuleQuery = z.infer<typeof RuleQuery>;
|
||||
export const RuleQuery = z.string();
|
||||
|
||||
export type RuleFilterArray = z.infer<typeof RuleFilterArray>;
|
||||
export const RuleFilterArray = z.array(z.object({}));
|
||||
|
||||
export type RuleNameOverride = z.infer<typeof RuleNameOverride>;
|
||||
export const RuleNameOverride = z.string();
|
||||
|
||||
export type TimestampOverride = z.infer<typeof TimestampOverride>;
|
||||
export const TimestampOverride = z.string();
|
||||
|
||||
export type TimestampOverrideFallbackDisabled = z.infer<typeof TimestampOverrideFallbackDisabled>;
|
||||
export const TimestampOverrideFallbackDisabled = z.boolean();
|
||||
|
||||
export type RequiredField = z.infer<typeof RequiredField>;
|
||||
export const RequiredField = z.object({
|
||||
name: z.string().min(1).optional(),
|
||||
type: z.string().min(1).optional(),
|
||||
ecs: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type RequiredFieldArray = z.infer<typeof RequiredFieldArray>;
|
||||
export const RequiredFieldArray = z.array(RequiredField);
|
||||
|
||||
export type TimelineTemplateId = z.infer<typeof TimelineTemplateId>;
|
||||
export const TimelineTemplateId = z.string();
|
||||
|
||||
export type TimelineTemplateTitle = z.infer<typeof TimelineTemplateTitle>;
|
||||
export const TimelineTemplateTitle = z.string();
|
||||
|
||||
export type SavedObjectResolveOutcome = z.infer<typeof SavedObjectResolveOutcome>;
|
||||
export const SavedObjectResolveOutcome = z.enum(['exactMatch', 'aliasMatch', 'conflict']);
|
||||
export const SavedObjectResolveOutcomeEnum = SavedObjectResolveOutcome.enum;
|
||||
export type SavedObjectResolveOutcomeEnum = typeof SavedObjectResolveOutcome.enum;
|
||||
|
||||
export type SavedObjectResolveAliasTargetId = z.infer<typeof SavedObjectResolveAliasTargetId>;
|
||||
export const SavedObjectResolveAliasTargetId = z.string();
|
||||
|
||||
export type SavedObjectResolveAliasPurpose = z.infer<typeof SavedObjectResolveAliasPurpose>;
|
||||
export const SavedObjectResolveAliasPurpose = z.enum([
|
||||
'savedObjectConversion',
|
||||
'savedObjectImport',
|
||||
]);
|
||||
export const SavedObjectResolveAliasPurposeEnum = SavedObjectResolveAliasPurpose.enum;
|
||||
export type SavedObjectResolveAliasPurposeEnum = typeof SavedObjectResolveAliasPurpose.enum;
|
||||
|
||||
export type RelatedIntegration = z.infer<typeof RelatedIntegration>;
|
||||
export const RelatedIntegration = z.object({
|
||||
package: z.string().min(1),
|
||||
version: z.string().min(1),
|
||||
integration: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
export type RelatedIntegrationArray = z.infer<typeof RelatedIntegrationArray>;
|
||||
export const RelatedIntegrationArray = z.array(RelatedIntegration);
|
|
@ -4,7 +4,7 @@ info:
|
|||
version: 'not applicable'
|
||||
paths: {}
|
||||
components:
|
||||
x-codegen-enabled: false
|
||||
x-codegen-enabled: true
|
||||
schemas:
|
||||
UUID:
|
||||
type: string
|
||||
|
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
export type Action = z.infer<typeof Action>;
|
||||
export const Action = z.object({
|
||||
/**
|
||||
* The action type used for sending notifications.
|
||||
*/
|
||||
action_type_id: z.string(),
|
||||
/**
|
||||
* Optionally groups actions by use cases. Use `default` for alert notifications.
|
||||
*/
|
||||
group: z.string(),
|
||||
/**
|
||||
* The connector ID.
|
||||
*/
|
||||
id: z.string(),
|
||||
/**
|
||||
* Object containing the allowed connector fields, which varies according to the connector type.
|
||||
*/
|
||||
params: z.object({}),
|
||||
uuid: z.string().optional(),
|
||||
/**
|
||||
* TODO implement the schema type
|
||||
*/
|
||||
alerts_filter: z.object({}).optional(),
|
||||
/**
|
||||
* TODO implement the schema type
|
||||
*/
|
||||
frequency: z.object({}).optional(),
|
||||
});
|
||||
|
||||
export type AlertSuppression = z.infer<typeof AlertSuppression>;
|
||||
export const AlertSuppression = z.object({
|
||||
group_by: z.array(z.string()).min(1).max(3),
|
||||
duration: z
|
||||
.object({
|
||||
value: z.number().int().min(1),
|
||||
unit: z.enum(['s', 'm', 'h']),
|
||||
})
|
||||
.optional(),
|
||||
missing_fields_strategy: z.enum(['doNotSuppress', 'suppress']).optional(),
|
||||
});
|
||||
|
||||
export type BaseRule = z.infer<typeof BaseRule>;
|
||||
export const BaseRule = z.object({
|
||||
/**
|
||||
* Rule name
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* Rule description
|
||||
*/
|
||||
description: z.string(),
|
||||
/**
|
||||
* Risk score (0 to 100)
|
||||
*/
|
||||
risk_score: z.number().int().min(0).max(100),
|
||||
/**
|
||||
* Severity of the rule
|
||||
*/
|
||||
severity: z.enum(['low', 'medium', 'high', 'critical']),
|
||||
/**
|
||||
* Sets the source field for the alert's signal.rule.name value
|
||||
*/
|
||||
rule_name_override: z.string().optional(),
|
||||
/**
|
||||
* Sets the time field used to query indices (optional)
|
||||
*/
|
||||
timestamp_override: z.string().optional(),
|
||||
/**
|
||||
* Timeline template ID
|
||||
*/
|
||||
timeline_id: z.string().optional(),
|
||||
/**
|
||||
* Timeline template title
|
||||
*/
|
||||
timeline_title: z.string().optional(),
|
||||
outcome: z.enum(['exactMatch', 'aliasMatch', 'conflict']).optional(),
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
alias_target_id: z.string().optional(),
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
alias_purpose: z.enum(['savedObjectConversion', 'savedObjectImport']).optional(),
|
||||
/**
|
||||
* The rule’s license.
|
||||
*/
|
||||
license: z.string().optional(),
|
||||
/**
|
||||
* Notes to help investigate alerts produced by the rule.
|
||||
*/
|
||||
note: z.string().optional(),
|
||||
/**
|
||||
* Determines if the rule acts as a building block. By default, building-block alerts are not displayed in the UI. These rules are used as a foundation for other rules that do generate alerts. Its value must be default.
|
||||
*/
|
||||
building_block_type: z.string().optional(),
|
||||
/**
|
||||
* (deprecated) Has no effect.
|
||||
*/
|
||||
output_index: z.string().optional(),
|
||||
/**
|
||||
* Has no effect.
|
||||
*/
|
||||
namespace: z.string().optional(),
|
||||
/**
|
||||
* Stores rule metadata.
|
||||
*/
|
||||
meta: z.object({}).optional(),
|
||||
/**
|
||||
* Defines the interval on which a rule's actions are executed.
|
||||
*/
|
||||
throttle: z.string().optional(),
|
||||
/**
|
||||
* The rule’s version number. Defaults to 1.
|
||||
*/
|
||||
version: z.number().int().min(1).optional().default(1),
|
||||
/**
|
||||
* String array containing words and phrases to help categorize, filter, and search rules. Defaults to an empty array.
|
||||
*/
|
||||
tags: z.array(z.string()).optional().default([]),
|
||||
/**
|
||||
* Determines whether the rule is enabled. Defaults to true.
|
||||
*/
|
||||
enabled: z.boolean().optional().default(true),
|
||||
/**
|
||||
* Overrides generated alerts' risk_score with a value from the source event
|
||||
*/
|
||||
risk_score_mapping: z
|
||||
.array(
|
||||
z.object({
|
||||
field: z.string(),
|
||||
operator: z.enum(['equals']),
|
||||
value: z.string(),
|
||||
risk_score: z.number().int().min(0).max(100).optional(),
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
.default([]),
|
||||
/**
|
||||
* Overrides generated alerts' severity with values from the source event
|
||||
*/
|
||||
severity_mapping: z
|
||||
.array(
|
||||
z.object({
|
||||
field: z.string(),
|
||||
operator: z.enum(['equals']),
|
||||
severity: z.enum(['low', 'medium', 'high', 'critical']),
|
||||
value: z.string(),
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
.default([]),
|
||||
/**
|
||||
* Frequency of rule execution, using a date math range. For example, "1h" means the rule runs every hour. Defaults to 5m (5 minutes).
|
||||
*/
|
||||
interval: z.string().optional().default('5m'),
|
||||
/**
|
||||
* Time from which data is analyzed each time the rule executes, using a date math range. For example, now-4200s means the rule analyzes data from 70 minutes before its start time. Defaults to now-6m (analyzes data from 6 minutes before the start time).
|
||||
*/
|
||||
from: z.string().optional().default('now-6m'),
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
to: z.string().optional().default('now'),
|
||||
actions: z.array(Action).optional().default([]),
|
||||
exceptions_list: z
|
||||
.array(
|
||||
z.object({
|
||||
/**
|
||||
* ID of the exception container
|
||||
*/
|
||||
id: z.string().min(1),
|
||||
/**
|
||||
* List ID of the exception container
|
||||
*/
|
||||
list_id: z.string().min(1),
|
||||
/**
|
||||
* The exception type
|
||||
*/
|
||||
type: z.enum([
|
||||
'detection',
|
||||
'rule_default',
|
||||
'endpoint',
|
||||
'endpoint_trusted_apps',
|
||||
'endpoint_events',
|
||||
'endpoint_host_isolation_exceptions',
|
||||
'endpoint_blocklists',
|
||||
]),
|
||||
/**
|
||||
* Determines the exceptions validity in rule's Kibana space
|
||||
*/
|
||||
namespace_type: z.enum(['agnostic', 'single']),
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
.default([]),
|
||||
author: z.array(z.string()).optional().default([]),
|
||||
false_positives: z.array(z.string()).optional().default([]),
|
||||
references: z.array(z.string()).optional().default([]),
|
||||
max_signals: z.number().int().min(1).optional().default(100),
|
||||
threat: z
|
||||
.array(
|
||||
z.object({
|
||||
/**
|
||||
* Relevant attack framework
|
||||
*/
|
||||
framework: z.string(),
|
||||
tactic: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
reference: z.string(),
|
||||
}),
|
||||
technique: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
reference: z.string(),
|
||||
subtechnique: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
reference: z.string(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type QueryRule = z.infer<typeof QueryRule>;
|
||||
export const QueryRule = BaseRule.and(
|
||||
z.object({
|
||||
/**
|
||||
* Rule type
|
||||
*/
|
||||
type: z.enum(['query']),
|
||||
index: z.array(z.string()).optional(),
|
||||
data_view_id: z.string().optional(),
|
||||
filters: z.array(z.unknown()).optional(),
|
||||
saved_id: z.string().optional(),
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
response_actions: z.array(z.object({})).optional(),
|
||||
alert_suppression: AlertSuppression.optional(),
|
||||
/**
|
||||
* Query to execute
|
||||
*/
|
||||
query: z.string().optional().default(''),
|
||||
/**
|
||||
* Query language to use.
|
||||
*/
|
||||
language: z.enum(['kuery', 'lucene']).optional().default('kuery'),
|
||||
})
|
||||
);
|
||||
|
||||
export type SavedQueryRule = z.infer<typeof SavedQueryRule>;
|
||||
export const SavedQueryRule = BaseRule.and(
|
||||
z.object({
|
||||
/**
|
||||
* Rule type
|
||||
*/
|
||||
type: z.enum(['saved_query']),
|
||||
index: z.array(z.string()).optional(),
|
||||
data_view_id: z.string().optional(),
|
||||
filters: z.array(z.unknown()).optional(),
|
||||
saved_id: z.string(),
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
response_actions: z.array(z.object({})).optional(),
|
||||
alert_suppression: AlertSuppression.optional(),
|
||||
/**
|
||||
* Query to execute
|
||||
*/
|
||||
query: z.string().optional(),
|
||||
/**
|
||||
* Query language to use.
|
||||
*/
|
||||
language: z.enum(['kuery', 'lucene']).optional().default('kuery'),
|
||||
})
|
||||
);
|
||||
|
||||
export type ThresholdRule = z.infer<typeof ThresholdRule>;
|
||||
export const ThresholdRule = BaseRule.and(
|
||||
z.object({
|
||||
/**
|
||||
* Rule type
|
||||
*/
|
||||
type: z.enum(['threshold']),
|
||||
query: z.string(),
|
||||
threshold: z.object({
|
||||
/**
|
||||
* Field to aggregate on
|
||||
*/
|
||||
field: z.union([z.string(), z.array(z.string())]),
|
||||
/**
|
||||
* Threshold value
|
||||
*/
|
||||
value: z.number().int().min(1).optional(),
|
||||
cardinality: z
|
||||
.array(
|
||||
z.object({
|
||||
field: z.string().optional(),
|
||||
value: z.number().int().min(0).optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
}),
|
||||
index: z.array(z.string()).optional(),
|
||||
data_view_id: z.string().optional(),
|
||||
filters: z.array(z.unknown()).optional(),
|
||||
saved_id: z.string().optional(),
|
||||
/**
|
||||
* Query language to use.
|
||||
*/
|
||||
language: z.enum(['kuery', 'lucene']).optional().default('kuery'),
|
||||
})
|
||||
);
|
||||
|
||||
export type ThreatMatchRule = z.infer<typeof ThreatMatchRule>;
|
||||
export const ThreatMatchRule = BaseRule.and(
|
||||
z.object({
|
||||
/**
|
||||
* Rule type
|
||||
*/
|
||||
type: z.enum(['threat_match']),
|
||||
query: z.string(),
|
||||
/**
|
||||
* Query to execute
|
||||
*/
|
||||
threat_query: z.string(),
|
||||
threat_mapping: z
|
||||
.array(
|
||||
z.object({
|
||||
entries: z
|
||||
.array(
|
||||
z.object({
|
||||
field: z.string().min(1).optional(),
|
||||
type: z.enum(['mapping']).optional(),
|
||||
value: z.string().min(1).optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
)
|
||||
.min(1),
|
||||
threat_index: z.array(z.string()),
|
||||
index: z.array(z.string()).optional(),
|
||||
data_view_id: z.string().optional(),
|
||||
filters: z.array(z.unknown()).optional(),
|
||||
saved_id: z.string().optional(),
|
||||
threat_filters: z.array(z.unknown()).optional(),
|
||||
/**
|
||||
* Defines the path to the threat indicator in the indicator documents (optional)
|
||||
*/
|
||||
threat_indicator_path: z.string().optional(),
|
||||
/**
|
||||
* Query language to use.
|
||||
*/
|
||||
threat_language: z.enum(['kuery', 'lucene']).optional(),
|
||||
concurrent_searches: z.number().int().min(1).optional(),
|
||||
items_per_search: z.number().int().min(1).optional(),
|
||||
/**
|
||||
* Query language to use.
|
||||
*/
|
||||
language: z.enum(['kuery', 'lucene']).optional().default('kuery'),
|
||||
})
|
||||
);
|
||||
|
||||
export type MlRule = z.infer<typeof MlRule>;
|
||||
export const MlRule = BaseRule.and(
|
||||
z.object({
|
||||
/**
|
||||
* Rule type
|
||||
*/
|
||||
type: z.enum(['machine_learning']),
|
||||
/**
|
||||
* Anomaly threshold
|
||||
*/
|
||||
anomaly_threshold: z.number().int().min(0),
|
||||
/**
|
||||
* Machine learning job ID
|
||||
*/
|
||||
machine_learning_job_id: z.union([z.string(), z.array(z.string()).min(1)]),
|
||||
})
|
||||
);
|
||||
|
||||
export type EqlRule = z.infer<typeof EqlRule>;
|
||||
export const EqlRule = BaseRule.and(
|
||||
z.object({
|
||||
/**
|
||||
* Rule type
|
||||
*/
|
||||
type: z.enum(['eql']),
|
||||
language: z.enum(['eql']),
|
||||
/**
|
||||
* EQL query to execute
|
||||
*/
|
||||
query: z.string(),
|
||||
index: z.array(z.string()).optional(),
|
||||
data_view_id: z.string().optional(),
|
||||
filters: z.array(z.unknown()).optional(),
|
||||
/**
|
||||
* Contains the event classification
|
||||
*/
|
||||
event_category_field: z.string().optional(),
|
||||
/**
|
||||
* Sets a secondary field for sorting events
|
||||
*/
|
||||
tiebreaker_field: z.string().optional(),
|
||||
/**
|
||||
* Contains the event timestamp used for sorting a sequence of events
|
||||
*/
|
||||
timestamp_field: z.string().optional(),
|
||||
})
|
||||
);
|
||||
|
||||
export type NewTermsRule = z.infer<typeof NewTermsRule>;
|
||||
export const NewTermsRule = BaseRule.and(
|
||||
z.object({
|
||||
/**
|
||||
* Rule type
|
||||
*/
|
||||
type: z.enum(['new_terms']),
|
||||
query: z.string(),
|
||||
new_terms_fields: z.array(z.string()).min(1).max(3),
|
||||
history_window_size: z.string().min(1).optional(),
|
||||
index: z.array(z.string()).optional(),
|
||||
data_view_id: z.string().optional(),
|
||||
filters: z.array(z.unknown()).optional(),
|
||||
language: z.enum(['kuery', 'lucene']).optional().default('kuery'),
|
||||
})
|
||||
);
|
||||
|
||||
export type Rule = z.infer<typeof Rule>;
|
||||
export const Rule = z.union([
|
||||
QueryRule,
|
||||
SavedQueryRule,
|
||||
ThresholdRule,
|
||||
ThreatMatchRule,
|
||||
MlRule,
|
||||
EqlRule,
|
||||
NewTermsRule,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Defines the maximum interval in which a rule's actions are executed.
|
||||
*/
|
||||
export type Throttle = z.infer<typeof Throttle>;
|
||||
export const Throttle = z.enum(['rule', '1h', '1d', '7d']);
|
||||
export const ThrottleEnum = Throttle.enum;
|
||||
export type ThrottleEnum = typeof Throttle.enum;
|
||||
|
||||
export type Subtechnique = z.infer<typeof Subtechnique>;
|
||||
export const Subtechnique = z.object({
|
||||
/**
|
||||
* Subtechnique ID
|
||||
*/
|
||||
id: z.string(),
|
||||
/**
|
||||
* Subtechnique name
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* Subtechnique reference
|
||||
*/
|
||||
reference: z.string(),
|
||||
});
|
||||
|
||||
export type Technique = z.infer<typeof Technique>;
|
||||
export const Technique = z.object({
|
||||
/**
|
||||
* Technique ID
|
||||
*/
|
||||
id: z.string(),
|
||||
/**
|
||||
* Technique name
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* Technique reference
|
||||
*/
|
||||
reference: z.string(),
|
||||
/**
|
||||
* Array containing more specific information on the attack technique
|
||||
*/
|
||||
subtechnique: z.array(Subtechnique).optional(),
|
||||
});
|
||||
|
||||
export type Threat = z.infer<typeof Threat>;
|
||||
export const Threat = z.object({
|
||||
/**
|
||||
* Relevant attack framework
|
||||
*/
|
||||
framework: z.string(),
|
||||
tactic: z.object({
|
||||
/**
|
||||
* Tactic ID
|
||||
*/
|
||||
id: z.string(),
|
||||
/**
|
||||
* Tactic name
|
||||
*/
|
||||
name: z.string(),
|
||||
/**
|
||||
* Tactic reference
|
||||
*/
|
||||
reference: z.string(),
|
||||
}),
|
||||
/**
|
||||
* Array containing information on the attack techniques (optional)
|
||||
*/
|
||||
technique: z.array(Technique).optional(),
|
||||
});
|
||||
|
||||
export type RuleResponse = z.infer<typeof RuleResponse>;
|
||||
export const RuleResponse = z.object({});
|
||||
|
||||
export type RuleCreateProps = z.infer<typeof RuleCreateProps>;
|
||||
export const RuleCreateProps = z.object({});
|
||||
|
||||
export type RuleUpdateProps = z.infer<typeof RuleUpdateProps>;
|
||||
export const RuleUpdateProps = z.object({});
|
||||
|
||||
export type RulePatchProps = z.infer<typeof RulePatchProps>;
|
||||
export const RulePatchProps = z.object({});
|
|
@ -4,98 +4,8 @@ info:
|
|||
version: 'not applicable'
|
||||
paths: {}
|
||||
components:
|
||||
x-codegen-enabled: false
|
||||
x-codegen-enabled: true
|
||||
schemas:
|
||||
SortOrder:
|
||||
type: string
|
||||
enum:
|
||||
- asc
|
||||
- desc
|
||||
RuleExecutionStatus:
|
||||
type: string
|
||||
description: |-
|
||||
Custom execution status of Security rules that is different from the status used in the Alerting Framework. We merge our custom status with the Framework's status to determine the resulting status of a rule.
|
||||
- going to run - @deprecated Replaced by the 'running' status but left for backwards compatibility with rule execution events already written to Event Log in the prior versions of Kibana. Don't use when writing rule status changes.
|
||||
- running - Rule execution started but not reached any intermediate or final status.
|
||||
- partial failure - Rule can partially fail for various reasons either in the middle of an execution (in this case we update its status right away) or in the end of it. So currently this status can be both intermediate and final at the same time. A typical reason for a partial failure: not all the indices that the rule searches over actually exist.
|
||||
- failed - Rule failed to execute due to unhandled exception or a reason defined in the business logic of its executor function.
|
||||
- succeeded - Rule executed successfully without any issues. Note: this status is just an indication of a rule's "health". The rule might or might not generate any alerts despite of it.
|
||||
|
||||
enum:
|
||||
- going to run
|
||||
- running
|
||||
- partial failure
|
||||
- failed
|
||||
- succeeded
|
||||
|
||||
RuleExecutionResult:
|
||||
type: object
|
||||
description: |-
|
||||
Rule execution result is an aggregate that groups plain rule execution events by execution UUID.
|
||||
properties:
|
||||
execution_uuid:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
duration_ms:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
num_active_alerts:
|
||||
type: integer
|
||||
num_new_alerts:
|
||||
type: integer
|
||||
num_recovered_alerts:
|
||||
type: integer
|
||||
num_triggered_actions:
|
||||
type: integer
|
||||
num_succeeded_actions:
|
||||
type: integer
|
||||
num_errored_actions:
|
||||
type: integer
|
||||
total_search_duration_ms:
|
||||
type: integer
|
||||
es_search_duration_ms:
|
||||
type: integer
|
||||
schedule_delay_ms:
|
||||
type: integer
|
||||
timed_out:
|
||||
type: boolean
|
||||
indexing_duration_ms:
|
||||
type: integer
|
||||
search_duration_ms:
|
||||
type: integer
|
||||
gap_duration_s:
|
||||
type: integer
|
||||
security_status:
|
||||
type: string
|
||||
security_message:
|
||||
type: string
|
||||
required:
|
||||
- execution_uuid
|
||||
- timestamp
|
||||
- duration_ms
|
||||
- status
|
||||
- message
|
||||
- num_active_alerts
|
||||
- num_new_alerts
|
||||
- num_recovered_alerts
|
||||
- num_triggered_actions
|
||||
- num_succeeded_actions
|
||||
- num_errored_actions
|
||||
- total_search_duration_ms
|
||||
- es_search_duration_ms
|
||||
- schedule_delay_ms
|
||||
- timed_out
|
||||
- indexing_duration_ms
|
||||
- search_duration_ms
|
||||
- gap_duration_s
|
||||
- security_status
|
||||
- security_message
|
||||
|
||||
Action:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -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 { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
export type SortOrder = z.infer<typeof SortOrder>;
|
||||
export const SortOrder = z.enum(['asc', 'desc']);
|
||||
export const SortOrderEnum = SortOrder.enum;
|
||||
export type SortOrderEnum = typeof SortOrder.enum;
|
|
@ -0,0 +1,13 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Sorting Schema
|
||||
version: 'not applicable'
|
||||
paths: {}
|
||||
components:
|
||||
x-codegen-enabled: true
|
||||
schemas:
|
||||
SortOrder:
|
||||
type: string
|
||||
enum:
|
||||
- asc
|
||||
- desc
|
|
@ -20,5 +20,5 @@ const required = t.exact(
|
|||
})
|
||||
);
|
||||
|
||||
export const warningSchema = t.intersection([partial, required]);
|
||||
export type WarningSchema = t.TypeOf<typeof warningSchema>;
|
||||
export const WarningSchema = t.intersection([partial, required]);
|
||||
export type WarningSchema = t.TypeOf<typeof WarningSchema>;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
export * from './get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.gen';
|
||||
export * from './get_prebuilt_rules_status/get_prebuilt_rules_status_route';
|
||||
export * from './install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route';
|
||||
export * from './install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.gen';
|
||||
export * from './perform_rule_installation/perform_rule_installation_route';
|
||||
export * from './perform_rule_upgrade/perform_rule_upgrade_route';
|
||||
export * from './review_rule_installation/review_rule_installation_route';
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
export type InstallPrebuiltRulesAndTimelinesResponse = z.infer<
|
||||
typeof InstallPrebuiltRulesAndTimelinesResponse
|
||||
>;
|
||||
export const InstallPrebuiltRulesAndTimelinesResponse = z
|
||||
.object({
|
||||
/**
|
||||
* The number of rules installed
|
||||
*/
|
||||
rules_installed: z.number().int().min(0),
|
||||
/**
|
||||
* The number of rules updated
|
||||
*/
|
||||
rules_updated: z.number().int().min(0),
|
||||
/**
|
||||
* The number of timelines installed
|
||||
*/
|
||||
timelines_installed: z.number().int().min(0),
|
||||
/**
|
||||
* The number of timelines updated
|
||||
*/
|
||||
timelines_updated: z.number().int().min(0),
|
||||
})
|
||||
.strict();
|
|
@ -5,8 +5,8 @@ info:
|
|||
paths:
|
||||
/api/detection_engine/rules/prepackaged:
|
||||
put:
|
||||
operationId: InstallPrebuiltRules
|
||||
x-codegen-enabled: false
|
||||
operationId: InstallPrebuiltRulesAndTimelines
|
||||
x-codegen-enabled: true
|
||||
summary: Installs all Elastic prebuilt rules and timelines
|
||||
tags:
|
||||
- Prebuilt Rules API
|
||||
|
@ -17,6 +17,7 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
rules_installed:
|
||||
type: integer
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
* 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 { InstallPrebuiltRulesAndTimelinesResponse } from './install_prebuilt_rules_and_timelines_route';
|
||||
import { stringifyZodError } from '@kbn/securitysolution-es-utils';
|
||||
import { expectParseError, expectParseSuccess } from '../../../../test/zod_helpers';
|
||||
import { InstallPrebuiltRulesAndTimelinesResponse } from './install_prebuilt_rules_and_timelines_route.gen';
|
||||
|
||||
describe('Install prebuilt rules and timelines response schema', () => {
|
||||
test('it should validate an empty prepackaged response with defaults', () => {
|
||||
|
@ -19,12 +17,9 @@ describe('Install prebuilt rules and timelines response schema', () => {
|
|||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = InstallPrebuiltRulesAndTimelinesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should not validate an extra invalid field added', () => {
|
||||
|
@ -35,12 +30,11 @@ describe('Install prebuilt rules and timelines response schema', () => {
|
|||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_field"']);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = InstallPrebuiltRulesAndTimelinesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
"Unrecognized key(s) in object: 'invalid_field'"
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_installed" number', () => {
|
||||
|
@ -50,14 +44,11 @@ describe('Install prebuilt rules and timelines response schema', () => {
|
|||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "-1" supplied to "rules_installed"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = InstallPrebuiltRulesAndTimelinesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'rules_installed: Number must be greater than or equal to 0'
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response with a negative "rules_updated"', () => {
|
||||
|
@ -67,14 +58,11 @@ describe('Install prebuilt rules and timelines response schema', () => {
|
|||
timelines_installed: 0,
|
||||
timelines_updated: 0,
|
||||
};
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "-1" supplied to "rules_updated"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = InstallPrebuiltRulesAndTimelinesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'rules_updated: Number must be greater than or equal to 0'
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response if "rules_installed" is not there', () => {
|
||||
|
@ -86,14 +74,9 @@ describe('Install prebuilt rules and timelines response schema', () => {
|
|||
};
|
||||
// @ts-expect-error
|
||||
delete payload.rules_installed;
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "undefined" supplied to "rules_installed"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = InstallPrebuiltRulesAndTimelinesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual('rules_installed: Required');
|
||||
});
|
||||
|
||||
test('it should NOT validate an empty prepackaged response if "rules_updated" is not there', () => {
|
||||
|
@ -105,13 +88,8 @@ describe('Install prebuilt rules and timelines response schema', () => {
|
|||
};
|
||||
// @ts-expect-error
|
||||
delete payload.rules_updated;
|
||||
const decoded = InstallPrebuiltRulesAndTimelinesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "undefined" supplied to "rules_updated"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = InstallPrebuiltRulesAndTimelinesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual('rules_updated: Required');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* 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,
|
||||
})
|
||||
);
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
import { RuleResponse, errorSchema } from '../../model';
|
||||
import { RuleResponse, ErrorSchema } from '../../model';
|
||||
|
||||
export type BulkCrudRulesResponse = t.TypeOf<typeof BulkCrudRulesResponse>;
|
||||
export const BulkCrudRulesResponse = t.array(t.union([RuleResponse, errorSchema]));
|
||||
export const BulkCrudRulesResponse = t.array(t.union([RuleResponse, ErrorSchema]));
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
import { RuleSignatureId } from '../../model/rule_schema/common_attributes.gen';
|
||||
|
||||
export type ExportRulesRequestQuery = z.infer<typeof ExportRulesRequestQuery>;
|
||||
export const ExportRulesRequestQuery = z.object({
|
||||
/**
|
||||
* Determines whether a summary of the exported rules is returned.
|
||||
*/
|
||||
exclude_export_details: z.preprocess(
|
||||
(value: unknown) => (typeof value === 'boolean' ? String(value) : value),
|
||||
z
|
||||
.enum(['true', 'false'])
|
||||
.default('false')
|
||||
.transform((value) => value === 'true')
|
||||
),
|
||||
/**
|
||||
* File name for saving the exported rules.
|
||||
*/
|
||||
file_name: z.string().optional().default('export.ndjson'),
|
||||
});
|
||||
export type ExportRulesRequestQueryInput = z.input<typeof ExportRulesRequestQuery>;
|
||||
|
||||
export type ExportRulesRequestBody = z.infer<typeof ExportRulesRequestBody>;
|
||||
export const ExportRulesRequestBody = z
|
||||
.object({
|
||||
/**
|
||||
* Array of `rule_id` fields. Exports all rules when unspecified.
|
||||
*/
|
||||
objects: z.array(
|
||||
z.object({
|
||||
rule_id: RuleSignatureId,
|
||||
})
|
||||
),
|
||||
})
|
||||
.nullable();
|
||||
export type ExportRulesRequestBodyInput = z.input<typeof ExportRulesRequestBody>;
|
|
@ -7,7 +7,7 @@ paths:
|
|||
summary: Exports rules to an `.ndjson` file
|
||||
post:
|
||||
operationId: ExportRules
|
||||
x-codegen-enabled: false
|
||||
x-codegen-enabled: true
|
||||
summary: Export rules
|
||||
description: Exports rules to an `.ndjson` file. The following configuration items are also included in the `.ndjson` file - Actions, Exception lists. Prebuilt rules cannot be exported.
|
||||
tags:
|
||||
|
@ -35,6 +35,7 @@ paths:
|
|||
type: object
|
||||
required:
|
||||
- objects
|
||||
nullable: true
|
||||
properties:
|
||||
objects:
|
||||
type: array
|
||||
|
@ -44,13 +45,13 @@ paths:
|
|||
- rule_id
|
||||
properties:
|
||||
rule_id:
|
||||
type: string
|
||||
$ref: '../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleSignatureId'
|
||||
description: Array of `rule_id` fields. Exports all rules when unspecified.
|
||||
responses:
|
||||
200:
|
||||
description: Indicates a successful call.
|
||||
content:
|
||||
application/json:
|
||||
application/ndjson:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
|
|
|
@ -5,55 +5,43 @@
|
|||
* 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 { ExportRulesRequestBody, ExportRulesRequestQuery } from './export_rules_route';
|
||||
import { stringifyZodError } from '@kbn/securitysolution-es-utils';
|
||||
import { expectParseError, expectParseSuccess } from '../../../../test/zod_helpers';
|
||||
import type { ExportRulesRequestQueryInput } from './export_rules_route.gen';
|
||||
import { ExportRulesRequestBody, ExportRulesRequestQuery } from './export_rules_route.gen';
|
||||
|
||||
describe('Export rules request schema', () => {
|
||||
describe('ExportRulesRequestBody', () => {
|
||||
test('null value or absent values validate', () => {
|
||||
const payload: Partial<ExportRulesRequestBody> = null;
|
||||
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ExportRulesRequestBody.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('empty object does not validate', () => {
|
||||
const payload = {};
|
||||
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "undefined" supplied to "objects"',
|
||||
'Invalid value "{}" supplied to "({| objects: Array<{| rule_id: string |}> |} | null)"',
|
||||
]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ExportRulesRequestBody.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual('objects: Required');
|
||||
});
|
||||
|
||||
test('empty object array does validate', () => {
|
||||
const payload: ExportRulesRequestBody = { objects: [] };
|
||||
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ExportRulesRequestBody.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('array with rule_id validates', () => {
|
||||
const payload: ExportRulesRequestBody = { objects: [{ rule_id: 'test-1' }] };
|
||||
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ExportRulesRequestBody.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('array with id does not validate as we do not allow that on purpose since we export rule_id', () => {
|
||||
|
@ -61,94 +49,80 @@ describe('Export rules request schema', () => {
|
|||
objects: [{ id: '4a7ff83d-3055-4bb2-ba68-587b9c6c15a4' }],
|
||||
};
|
||||
|
||||
const decoded = ExportRulesRequestBody.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "undefined" supplied to "objects,rule_id"',
|
||||
'Invalid value "{"objects":[{"id":"4a7ff83d-3055-4bb2-ba68-587b9c6c15a4"}]}" supplied to "({| objects: Array<{| rule_id: string |}> |} | null)"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ExportRulesRequestBody.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual('objects.0.rule_id: Required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ExportRulesRequestQuery', () => {
|
||||
test('default value for file_name is export.ndjson and default for exclude_export_details is false', () => {
|
||||
const payload: Partial<ExportRulesRequestQuery> = {};
|
||||
const payload: ExportRulesRequestQueryInput = {};
|
||||
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
const expected: ExportRulesRequestQuery = {
|
||||
file_name: 'export.ndjson',
|
||||
exclude_export_details: false,
|
||||
};
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(expected);
|
||||
|
||||
const result = ExportRulesRequestQuery.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(expected);
|
||||
});
|
||||
|
||||
test('file_name validates', () => {
|
||||
const payload: ExportRulesRequestQuery = {
|
||||
const payload: ExportRulesRequestQueryInput = {
|
||||
file_name: 'test.ndjson',
|
||||
};
|
||||
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
const expected: ExportRulesRequestQuery = {
|
||||
file_name: 'test.ndjson',
|
||||
exclude_export_details: false,
|
||||
};
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(expected);
|
||||
|
||||
const result = ExportRulesRequestQuery.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(expected);
|
||||
});
|
||||
|
||||
test('file_name does not validate with a number', () => {
|
||||
const payload: Omit<ExportRulesRequestQuery, 'file_name'> & { file_name: number } = {
|
||||
const payload: Omit<ExportRulesRequestQueryInput, 'file_name'> & { file_name: number } = {
|
||||
file_name: 10,
|
||||
};
|
||||
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "10" supplied to "file_name"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ExportRulesRequestQuery.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'file_name: Expected string, received number'
|
||||
);
|
||||
});
|
||||
|
||||
test('exclude_export_details validates with a boolean true', () => {
|
||||
const payload: ExportRulesRequestQuery = {
|
||||
const payload: ExportRulesRequestQueryInput = {
|
||||
exclude_export_details: true,
|
||||
};
|
||||
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
const expected: ExportRulesRequestQuery = {
|
||||
exclude_export_details: true,
|
||||
file_name: 'export.ndjson',
|
||||
};
|
||||
expect(message.schema).toEqual(expected);
|
||||
|
||||
const result = ExportRulesRequestQuery.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(expected);
|
||||
});
|
||||
|
||||
test('exclude_export_details does not validate with a string', () => {
|
||||
const payload: Omit<ExportRulesRequestQuery, 'exclude_export_details'> & {
|
||||
const payload: Omit<ExportRulesRequestQueryInput, 'exclude_export_details'> & {
|
||||
exclude_export_details: string;
|
||||
} = {
|
||||
exclude_export_details: 'invalid string',
|
||||
};
|
||||
|
||||
const decoded = ExportRulesRequestQuery.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "invalid string" supplied to "exclude_export_details"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ExportRulesRequestQuery.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
`exclude_export_details: Invalid enum value. Expected 'true' | 'false', received 'invalid string'`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* 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 { RuleSignatureId } from '../../model';
|
||||
|
||||
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 })
|
||||
);
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
import { ErrorSchema } from '../../model/error_schema.gen';
|
||||
import { WarningSchema } from '../../model/warning_schema.gen';
|
||||
|
||||
export type ImportRulesRequestQuery = z.infer<typeof ImportRulesRequestQuery>;
|
||||
export const ImportRulesRequestQuery = z.object({
|
||||
/**
|
||||
* Determines whether existing rules with the same `rule_id` are overwritten.
|
||||
*/
|
||||
overwrite: z.preprocess(
|
||||
(value: unknown) => (typeof value === 'boolean' ? String(value) : value),
|
||||
z
|
||||
.enum(['true', 'false'])
|
||||
.default('false')
|
||||
.transform((value) => value === 'true')
|
||||
),
|
||||
/**
|
||||
* Determines whether existing exception lists with the same `list_id` are overwritten.
|
||||
*/
|
||||
overwrite_exceptions: z.preprocess(
|
||||
(value: unknown) => (typeof value === 'boolean' ? String(value) : value),
|
||||
z
|
||||
.enum(['true', 'false'])
|
||||
.default('false')
|
||||
.transform((value) => value === 'true')
|
||||
),
|
||||
/**
|
||||
* Determines whether existing actions with the same `kibana.alert.rule.actions.id` are overwritten.
|
||||
*/
|
||||
overwrite_action_connectors: z.preprocess(
|
||||
(value: unknown) => (typeof value === 'boolean' ? String(value) : value),
|
||||
z
|
||||
.enum(['true', 'false'])
|
||||
.default('false')
|
||||
.transform((value) => value === 'true')
|
||||
),
|
||||
/**
|
||||
* Generates a new list ID for each imported exception list.
|
||||
*/
|
||||
as_new_list: z.preprocess(
|
||||
(value: unknown) => (typeof value === 'boolean' ? String(value) : value),
|
||||
z
|
||||
.enum(['true', 'false'])
|
||||
.default('false')
|
||||
.transform((value) => value === 'true')
|
||||
),
|
||||
});
|
||||
export type ImportRulesRequestQueryInput = z.input<typeof ImportRulesRequestQuery>;
|
||||
|
||||
export type ImportRulesResponse = z.infer<typeof ImportRulesResponse>;
|
||||
export const ImportRulesResponse = z
|
||||
.object({
|
||||
exceptions_success: z.boolean(),
|
||||
exceptions_success_count: z.number().int().min(0),
|
||||
exceptions_errors: z.array(ErrorSchema),
|
||||
rules_count: z.number().int().min(0),
|
||||
success: z.boolean(),
|
||||
success_count: z.number().int().min(0),
|
||||
errors: z.array(ErrorSchema),
|
||||
action_connectors_errors: z.array(ErrorSchema),
|
||||
action_connectors_warnings: z.array(WarningSchema),
|
||||
action_connectors_success: z.boolean(),
|
||||
action_connectors_success_count: z.number().int().min(0),
|
||||
})
|
||||
.strict();
|
|
@ -7,7 +7,7 @@ paths:
|
|||
summary: Imports rules from an `.ndjson` file
|
||||
post:
|
||||
operationId: ImportRules
|
||||
x-codegen-enabled: false
|
||||
x-codegen-enabled: true
|
||||
summary: Import rules
|
||||
description: Imports rules from an `.ndjson` file, including actions and exception lists.
|
||||
tags:
|
||||
|
@ -45,6 +45,13 @@ paths:
|
|||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
- name: as_new_list
|
||||
in: query
|
||||
required: false
|
||||
description: Generates a new list ID for each imported exception list.
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
responses:
|
||||
200:
|
||||
description: Indicates a successful call.
|
||||
|
@ -52,3 +59,51 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- exceptions_success
|
||||
- exceptions_success_count
|
||||
- exceptions_errors
|
||||
- rules_count
|
||||
- success
|
||||
- success_count
|
||||
- errors
|
||||
- action_connectors_errors
|
||||
- action_connectors_warnings
|
||||
- action_connectors_success
|
||||
- action_connectors_success_count
|
||||
properties:
|
||||
exceptions_success:
|
||||
type: boolean
|
||||
exceptions_success_count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exceptions_errors:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/error_schema.schema.yaml#/components/schemas/ErrorSchema'
|
||||
rules_count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
success:
|
||||
type: boolean
|
||||
success_count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/error_schema.schema.yaml#/components/schemas/ErrorSchema'
|
||||
action_connectors_errors:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/error_schema.schema.yaml#/components/schemas/ErrorSchema'
|
||||
action_connectors_warnings:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/warning_schema.schema.yaml#/components/schemas/WarningSchema'
|
||||
action_connectors_success:
|
||||
type: boolean
|
||||
action_connectors_success_count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
|
|
|
@ -5,15 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import type { Either } from 'fp-ts/lib/Either';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import type { Errors } from 'io-ts';
|
||||
|
||||
import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import type { ErrorSchema } from '../../model/error_schema';
|
||||
|
||||
import { ImportRulesResponse } from './import_rules_route';
|
||||
import { stringifyZodError } from '@kbn/securitysolution-es-utils';
|
||||
import { expectParseError, expectParseSuccess } from '../../../../test/zod_helpers';
|
||||
import type { ErrorSchema } from '../../model/error_schema.gen';
|
||||
import { ImportRulesResponse } from './import_rules_route.gen';
|
||||
|
||||
describe('Import rules schema', () => {
|
||||
describe('response schema', () => {
|
||||
|
@ -31,12 +26,9 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should validate an empty import response with a single error', () => {
|
||||
|
@ -53,12 +45,9 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should validate an empty import response with a single exceptions error', () => {
|
||||
|
@ -75,12 +64,9 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should validate an empty import response with two errors', () => {
|
||||
|
@ -100,12 +86,9 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should validate an empty import response with two exception errors', () => {
|
||||
|
@ -125,12 +108,9 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should NOT validate a success_count that is a negative number', () => {
|
||||
|
@ -147,14 +127,11 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "-1" supplied to "success_count"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'success_count: Number must be greater than or equal to 0'
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate a exceptions_success_count that is a negative number', () => {
|
||||
|
@ -171,35 +148,14 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "-1" supplied to "exceptions_success_count"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'exceptions_success_count: Number must be greater than or equal to 0'
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate a success that is not a boolean', () => {
|
||||
type UnsafeCastForTest = Either<
|
||||
Errors,
|
||||
{
|
||||
success: string;
|
||||
success_count: number;
|
||||
errors: Array<
|
||||
{
|
||||
id?: string | undefined;
|
||||
rule_id?: string | undefined;
|
||||
} & {
|
||||
error: {
|
||||
status_code: number;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
const payload: Omit<ImportRulesResponse, 'success'> & { success: string } = {
|
||||
success: 'hello',
|
||||
success_count: 0,
|
||||
|
@ -213,36 +169,12 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded as UnsafeCastForTest);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "hello" supplied to "success"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual('success: Expected boolean, received string');
|
||||
});
|
||||
|
||||
test('it should NOT validate a exceptions_success that is not a boolean', () => {
|
||||
type UnsafeCastForTest = Either<
|
||||
Errors,
|
||||
{
|
||||
success: boolean;
|
||||
exceptions_success: string;
|
||||
success_count: number;
|
||||
errors: Array<
|
||||
{
|
||||
id?: string | undefined;
|
||||
rule_id?: string | undefined;
|
||||
} & {
|
||||
error: {
|
||||
status_code: number;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
const payload: Omit<ImportRulesResponse, 'exceptions_success'> & {
|
||||
exceptions_success: string;
|
||||
} = {
|
||||
|
@ -258,14 +190,11 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded as UnsafeCastForTest);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "hello" supplied to "exceptions_success"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'exceptions_success: Expected boolean, received string'
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate a success an extra invalid field', () => {
|
||||
|
@ -283,12 +212,11 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_field"']);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
"Unrecognized key(s) in object: 'invalid_field'"
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate an extra field in the second position of the errors array', () => {
|
||||
|
@ -311,12 +239,11 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual(['invalid keys "invalid_data"']);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
"errors.1: Unrecognized key(s) in object: 'invalid_data'"
|
||||
);
|
||||
});
|
||||
|
||||
test('it should validate an empty import response with a single connectors error', () => {
|
||||
|
@ -333,13 +260,11 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [{ error: { status_code: 400, message: 'some message' } }],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should validate an empty import response with multiple errors', () => {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
|
@ -357,33 +282,12 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [{ error: { status_code: 400, message: 'some message' } }],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should NOT validate action_connectors_success that is not boolean', () => {
|
||||
type UnsafeCastForTest = Either<
|
||||
Errors,
|
||||
{
|
||||
success: boolean;
|
||||
action_connectors_success: string;
|
||||
success_count: number;
|
||||
errors: Array<
|
||||
{
|
||||
id?: string | undefined;
|
||||
rule_id?: string | undefined;
|
||||
} & {
|
||||
error: {
|
||||
status_code: number;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
const payload: Omit<ImportRulesResponse, 'action_connectors_success'> & {
|
||||
action_connectors_success: string;
|
||||
} = {
|
||||
|
@ -399,15 +303,13 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded as UnsafeCastForTest);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "invalid" supplied to "action_connectors_success"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'action_connectors_success: Expected boolean, received string'
|
||||
);
|
||||
});
|
||||
|
||||
test('it should NOT validate a action_connectors_success_count that is a negative number', () => {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
|
@ -422,16 +324,13 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "-1" supplied to "action_connectors_success_count"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'action_connectors_success_count: Number must be greater than or equal to 0'
|
||||
);
|
||||
});
|
||||
test('it should validate a action_connectors_warnings after importing successfully', () => {
|
||||
test('it should validate a action_connectors_warnings after importing successfully', () => {
|
||||
const payload: ImportRulesResponse = {
|
||||
success: false,
|
||||
success_count: 0,
|
||||
|
@ -447,33 +346,12 @@ describe('Import rules schema', () => {
|
|||
{ type: 'type', message: 'message', actionPath: 'actionPath' },
|
||||
],
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(payload);
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(payload);
|
||||
});
|
||||
|
||||
test('it should NOT validate a action_connectors_warnings that is not WarningSchema', () => {
|
||||
type UnsafeCastForTest = Either<
|
||||
Errors,
|
||||
{
|
||||
success: boolean;
|
||||
action_connectors_warnings: string;
|
||||
success_count: number;
|
||||
errors: Array<
|
||||
{
|
||||
id?: string | undefined;
|
||||
rule_id?: string | undefined;
|
||||
} & {
|
||||
error: {
|
||||
status_code: number;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
const payload: Omit<ImportRulesResponse, 'action_connectors_warnings'> & {
|
||||
action_connectors_warnings: string;
|
||||
} = {
|
||||
|
@ -489,14 +367,11 @@ describe('Import rules schema', () => {
|
|||
action_connectors_errors: [],
|
||||
action_connectors_warnings: 'invalid',
|
||||
};
|
||||
const decoded = ImportRulesResponse.decode(payload);
|
||||
const checked = exactCheck(payload, decoded as UnsafeCastForTest);
|
||||
const message = pipe(checked, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([
|
||||
'Invalid value "invalid" supplied to "action_connectors_warnings"',
|
||||
]);
|
||||
expect(message.schema).toEqual({});
|
||||
const result = ImportRulesResponse.safeParse(payload);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(
|
||||
'action_connectors_warnings: Expected array, received string'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* 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 { DefaultStringBooleanFalse, PositiveInteger } from '@kbn/securitysolution-io-ts-types';
|
||||
import { errorSchema, warningSchema } from '../../model';
|
||||
|
||||
export const ImportRulesRequestQuery = t.exact(
|
||||
t.partial({
|
||||
overwrite: DefaultStringBooleanFalse,
|
||||
overwrite_exceptions: DefaultStringBooleanFalse,
|
||||
overwrite_action_connectors: DefaultStringBooleanFalse,
|
||||
as_new_list: DefaultStringBooleanFalse,
|
||||
})
|
||||
);
|
||||
|
||||
export type ImportRulesRequestQuery = t.TypeOf<typeof ImportRulesRequestQuery>;
|
||||
export interface ImportRulesRequestQueryDecoded {
|
||||
overwrite: boolean;
|
||||
overwrite_exceptions: boolean;
|
||||
overwrite_action_connectors: boolean;
|
||||
as_new_list: boolean;
|
||||
}
|
||||
|
||||
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: t.boolean,
|
||||
success_count: PositiveInteger,
|
||||
errors: t.array(errorSchema),
|
||||
action_connectors_errors: t.array(errorSchema),
|
||||
action_connectors_warnings: t.array(warningSchema),
|
||||
action_connectors_success: t.boolean,
|
||||
action_connectors_success_count: PositiveInteger,
|
||||
})
|
||||
);
|
|
@ -21,14 +21,14 @@ export * from './crud/read_rule/read_rule_route';
|
|||
export * from './crud/update_rule/request_schema_validation';
|
||||
export * from './crud/update_rule/update_rule_route';
|
||||
export * from './export_rules/export_rules_details_schema';
|
||||
export * from './export_rules/export_rules_route';
|
||||
export * from './export_rules/export_rules_route.gen';
|
||||
export * from './find_rules/find_rules_route';
|
||||
export * from './find_rules/request_schema_validation';
|
||||
export * from './get_rule_management_filters/get_rule_management_filters_route';
|
||||
export * from './import_rules/import_rules_route';
|
||||
export * from './import_rules/import_rules_route.gen';
|
||||
export * from './import_rules/rule_to_import_validation';
|
||||
export * from './import_rules/rule_to_import';
|
||||
export * from './model/query_rule_by_ids_validation';
|
||||
export * from './model/query_rule_by_ids';
|
||||
export * from './urls';
|
||||
export * from './read_tags/read_tags_route';
|
||||
export * from './read_tags/read_tags_route.gen';
|
||||
|
|
|
@ -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 type { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
import { RuleTagArray } from '../../model/rule_schema/common_attributes.gen';
|
||||
|
||||
export type ReadTagsResponse = z.infer<typeof ReadTagsResponse>;
|
||||
export const ReadTagsResponse = RuleTagArray;
|
|
@ -6,8 +6,8 @@ paths:
|
|||
/api/detection_engine/tags:
|
||||
summary: Aggregates and returns rule tags
|
||||
get:
|
||||
operationId: GetTags
|
||||
x-codegen-enabled: false
|
||||
operationId: ReadTags
|
||||
x-codegen-enabled: true
|
||||
summary: Aggregates and returns all unique tags from all rules
|
||||
tags:
|
||||
- Tags API
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* 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 const ReadTagsResponse = t.array(t.string);
|
||||
export type ReadTagsResponse = t.TypeOf<typeof ReadTagsResponse>;
|
|
@ -11,13 +11,14 @@ export * from './detection_engine_health/get_space_health/get_space_health_route
|
|||
export * from './detection_engine_health/setup_health/setup_health_route';
|
||||
export * from './detection_engine_health/model';
|
||||
export * from './rule_execution_logs/get_rule_execution_events/get_rule_execution_events_route';
|
||||
export * from './rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route';
|
||||
export * from './rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen';
|
||||
export * from './urls';
|
||||
|
||||
export * from './model/execution_event';
|
||||
export * from './model/execution_metrics';
|
||||
export * from './model/execution_result';
|
||||
export * from './model/execution_result.gen';
|
||||
export * from './model/execution_settings';
|
||||
export * from './model/execution_status.gen';
|
||||
export * from './model/execution_status';
|
||||
export * from './model/execution_summary';
|
||||
export * from './model/log_level';
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rule execution result is an aggregate that groups plain rule execution events by execution UUID. It contains such information as execution UUID, date, status and metrics.
|
||||
*/
|
||||
export type RuleExecutionResult = z.infer<typeof RuleExecutionResult>;
|
||||
export const RuleExecutionResult = z.object({
|
||||
execution_uuid: z.string(),
|
||||
timestamp: z.string().datetime(),
|
||||
duration_ms: z.number().int(),
|
||||
status: z.string(),
|
||||
message: z.string(),
|
||||
num_active_alerts: z.number().int(),
|
||||
num_new_alerts: z.number().int(),
|
||||
num_recovered_alerts: z.number().int(),
|
||||
num_triggered_actions: z.number().int(),
|
||||
num_succeeded_actions: z.number().int(),
|
||||
num_errored_actions: z.number().int(),
|
||||
total_search_duration_ms: z.number().int(),
|
||||
es_search_duration_ms: z.number().int(),
|
||||
schedule_delay_ms: z.number().int(),
|
||||
timed_out: z.boolean(),
|
||||
indexing_duration_ms: z.number().int(),
|
||||
search_duration_ms: z.number().int(),
|
||||
gap_duration_s: z.number().int(),
|
||||
security_status: z.string(),
|
||||
security_message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* We support sorting rule execution results by these fields.
|
||||
*/
|
||||
export type SortFieldOfRuleExecutionResult = z.infer<typeof SortFieldOfRuleExecutionResult>;
|
||||
export const SortFieldOfRuleExecutionResult = z.enum([
|
||||
'timestamp',
|
||||
'duration_ms',
|
||||
'gap_duration_s',
|
||||
'indexing_duration_ms',
|
||||
'search_duration_ms',
|
||||
'schedule_delay_ms',
|
||||
]);
|
||||
export const SortFieldOfRuleExecutionResultEnum = SortFieldOfRuleExecutionResult.enum;
|
||||
export type SortFieldOfRuleExecutionResultEnum = typeof SortFieldOfRuleExecutionResult.enum;
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { RuleExecutionResult } from './execution_result';
|
||||
import type { RuleExecutionResult } from './execution_result.gen';
|
||||
|
||||
const getSomeResults = (): RuleExecutionResult[] => [
|
||||
{
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Execution Result Schema
|
||||
version: not applicable
|
||||
paths: {}
|
||||
components:
|
||||
x-codegen-enabled: true
|
||||
schemas:
|
||||
RuleExecutionResult:
|
||||
type: object
|
||||
description: |-
|
||||
Rule execution result is an aggregate that groups plain rule execution events by execution UUID. It contains such information as execution UUID, date, status and metrics.
|
||||
properties:
|
||||
execution_uuid:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
duration_ms:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
num_active_alerts:
|
||||
type: integer
|
||||
num_new_alerts:
|
||||
type: integer
|
||||
num_recovered_alerts:
|
||||
type: integer
|
||||
num_triggered_actions:
|
||||
type: integer
|
||||
num_succeeded_actions:
|
||||
type: integer
|
||||
num_errored_actions:
|
||||
type: integer
|
||||
total_search_duration_ms:
|
||||
type: integer
|
||||
es_search_duration_ms:
|
||||
type: integer
|
||||
schedule_delay_ms:
|
||||
type: integer
|
||||
timed_out:
|
||||
type: boolean
|
||||
indexing_duration_ms:
|
||||
type: integer
|
||||
search_duration_ms:
|
||||
type: integer
|
||||
gap_duration_s:
|
||||
type: integer
|
||||
security_status:
|
||||
type: string
|
||||
security_message:
|
||||
type: string
|
||||
required:
|
||||
- execution_uuid
|
||||
- timestamp
|
||||
- duration_ms
|
||||
- status
|
||||
- message
|
||||
- num_active_alerts
|
||||
- num_new_alerts
|
||||
- num_recovered_alerts
|
||||
- num_triggered_actions
|
||||
- num_succeeded_actions
|
||||
- num_errored_actions
|
||||
- total_search_duration_ms
|
||||
- es_search_duration_ms
|
||||
- schedule_delay_ms
|
||||
- timed_out
|
||||
- indexing_duration_ms
|
||||
- search_duration_ms
|
||||
- gap_duration_s
|
||||
- security_status
|
||||
- security_message
|
||||
|
||||
SortFieldOfRuleExecutionResult:
|
||||
type: string
|
||||
description: We support sorting rule execution results by these fields.
|
||||
enum:
|
||||
- timestamp
|
||||
- duration_ms
|
||||
- gap_duration_s
|
||||
- indexing_duration_ms
|
||||
- search_duration_ms
|
||||
- schedule_delay_ms
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Custom execution status of Security rules that is different from the status used in the Alerting Framework. We merge our custom status with the Framework's status to determine the resulting status of a rule.
|
||||
- going to run - @deprecated Replaced by the 'running' status but left for backwards compatibility with rule execution events already written to Event Log in the prior versions of Kibana. Don't use when writing rule status changes.
|
||||
- running - Rule execution started but not reached any intermediate or final status.
|
||||
- partial failure - Rule can partially fail for various reasons either in the middle of an execution (in this case we update its status right away) or in the end of it. So currently this status can be both intermediate and final at the same time. A typical reason for a partial failure: not all the indices that the rule searches over actually exist.
|
||||
- failed - Rule failed to execute due to unhandled exception or a reason defined in the business logic of its executor function.
|
||||
- succeeded - Rule executed successfully without any issues. Note: this status is just an indication of a rule's "health". The rule might or might not generate any alerts despite of it.
|
||||
*/
|
||||
export type RuleExecutionStatus = z.infer<typeof RuleExecutionStatus>;
|
||||
export const RuleExecutionStatus = z.enum([
|
||||
'going to run',
|
||||
'running',
|
||||
'partial failure',
|
||||
'failed',
|
||||
'succeeded',
|
||||
]);
|
||||
export const RuleExecutionStatusEnum = RuleExecutionStatus.enum;
|
||||
export type RuleExecutionStatusEnum = typeof RuleExecutionStatus.enum;
|
|
@ -0,0 +1,24 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Execution Status Schema
|
||||
version: not applicable
|
||||
paths: {}
|
||||
components:
|
||||
x-codegen-enabled: true
|
||||
schemas:
|
||||
RuleExecutionStatus:
|
||||
type: string
|
||||
description: |-
|
||||
Custom execution status of Security rules that is different from the status used in the Alerting Framework. We merge our custom status with the Framework's status to determine the resulting status of a rule.
|
||||
- going to run - @deprecated Replaced by the 'running' status but left for backwards compatibility with rule execution events already written to Event Log in the prior versions of Kibana. Don't use when writing rule status changes.
|
||||
- running - Rule execution started but not reached any intermediate or final status.
|
||||
- partial failure - Rule can partially fail for various reasons either in the middle of an execution (in this case we update its status right away) or in the end of it. So currently this status can be both intermediate and final at the same time. A typical reason for a partial failure: not all the indices that the rule searches over actually exist.
|
||||
- failed - Rule failed to execute due to unhandled exception or a reason defined in the business logic of its executor function.
|
||||
- succeeded - Rule executed successfully without any issues. Note: this status is just an indication of a rule's "health". The rule might or might not generate any alerts despite of it.
|
||||
|
||||
enum:
|
||||
- going to run
|
||||
- running
|
||||
- partial failure
|
||||
- failed
|
||||
- succeeded
|
|
@ -5,57 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type * as t from 'io-ts';
|
||||
import { enumeration, PositiveInteger } from '@kbn/securitysolution-io-ts-types';
|
||||
import type { RuleLastRunOutcomes } from '@kbn/alerting-plugin/common';
|
||||
import { enumeration, PositiveInteger } from '@kbn/securitysolution-io-ts-types';
|
||||
import type * as t from 'io-ts';
|
||||
import { assertUnreachable } from '../../../../utility_types';
|
||||
import type { RuleExecutionStatus } from './execution_status.gen';
|
||||
import { RuleExecutionStatusEnum } from './execution_status.gen';
|
||||
|
||||
/**
|
||||
* Custom execution status of Security rules that is different from the status
|
||||
* used in the Alerting Framework. We merge our custom status with the
|
||||
* Framework's status to determine the resulting status of a rule.
|
||||
*/
|
||||
export enum RuleExecutionStatus {
|
||||
/**
|
||||
* @deprecated Replaced by the 'running' status but left for backwards compatibility
|
||||
* with rule execution events already written to Event Log in the prior versions of Kibana.
|
||||
* Don't use when writing rule status changes.
|
||||
*/
|
||||
'going to run' = 'going to run',
|
||||
|
||||
/**
|
||||
* Rule execution started but not reached any intermediate or final status.
|
||||
*/
|
||||
'running' = 'running',
|
||||
|
||||
/**
|
||||
* Rule can partially fail for various reasons either in the middle of an execution
|
||||
* (in this case we update its status right away) or in the end of it. So currently
|
||||
* this status can be both intermediate and final at the same time.
|
||||
* A typical reason for a partial failure: not all the indices that the rule searches
|
||||
* over actually exist.
|
||||
*/
|
||||
'partial failure' = 'partial failure',
|
||||
|
||||
/**
|
||||
* Rule failed to execute due to unhandled exception or a reason defined in the
|
||||
* business logic of its executor function.
|
||||
*/
|
||||
'failed' = 'failed',
|
||||
|
||||
/**
|
||||
* Rule executed successfully without any issues. Note: this status is just an indication
|
||||
* of a rule's "health". The rule might or might not generate any alerts despite of it.
|
||||
*/
|
||||
'succeeded' = 'succeeded',
|
||||
}
|
||||
|
||||
export const TRuleExecutionStatus = enumeration('RuleExecutionStatus', RuleExecutionStatus);
|
||||
|
||||
/**
|
||||
* An array of supported rule execution statuses.
|
||||
*/
|
||||
export const RULE_EXECUTION_STATUSES = Object.values(RuleExecutionStatus);
|
||||
// TODO remove after the migration to Zod is done
|
||||
export const TRuleExecutionStatus = enumeration('RuleExecutionStatus', RuleExecutionStatusEnum);
|
||||
|
||||
export type RuleExecutionStatusOrder = t.TypeOf<typeof RuleExecutionStatusOrder>;
|
||||
export const RuleExecutionStatusOrder = PositiveInteger;
|
||||
|
@ -64,15 +22,15 @@ export const ruleExecutionStatusToNumber = (
|
|||
status: RuleExecutionStatus
|
||||
): RuleExecutionStatusOrder => {
|
||||
switch (status) {
|
||||
case RuleExecutionStatus.succeeded:
|
||||
case RuleExecutionStatusEnum.succeeded:
|
||||
return 0;
|
||||
case RuleExecutionStatus['going to run']:
|
||||
case RuleExecutionStatusEnum['going to run']:
|
||||
return 10;
|
||||
case RuleExecutionStatus.running:
|
||||
case RuleExecutionStatusEnum.running:
|
||||
return 15;
|
||||
case RuleExecutionStatus['partial failure']:
|
||||
case RuleExecutionStatusEnum['partial failure']:
|
||||
return 20;
|
||||
case RuleExecutionStatus.failed:
|
||||
case RuleExecutionStatusEnum.failed:
|
||||
return 30;
|
||||
default:
|
||||
assertUnreachable(status);
|
||||
|
@ -85,13 +43,13 @@ export const ruleLastRunOutcomeToExecutionStatus = (
|
|||
): RuleExecutionStatus => {
|
||||
switch (outcome) {
|
||||
case 'succeeded':
|
||||
return RuleExecutionStatus.succeeded;
|
||||
return RuleExecutionStatusEnum.succeeded;
|
||||
case 'warning':
|
||||
return RuleExecutionStatus['partial failure'];
|
||||
return RuleExecutionStatusEnum['partial failure'];
|
||||
case 'failed':
|
||||
return RuleExecutionStatus.failed;
|
||||
return RuleExecutionStatusEnum.failed;
|
||||
default:
|
||||
assertUnreachable(outcome);
|
||||
return RuleExecutionStatus.failed;
|
||||
return RuleExecutionStatusEnum.failed;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RuleExecutionStatus } from './execution_status';
|
||||
import { RuleExecutionStatusEnum } from './execution_status.gen';
|
||||
import type { RuleExecutionSummary } from './execution_summary';
|
||||
|
||||
const getSummarySucceeded = (): RuleExecutionSummary => ({
|
||||
last_execution: {
|
||||
date: '2020-02-18T15:26:49.783Z',
|
||||
status: RuleExecutionStatus.succeeded,
|
||||
status: RuleExecutionStatusEnum.succeeded,
|
||||
status_order: 0,
|
||||
message: 'succeeded',
|
||||
metrics: {
|
||||
|
@ -25,7 +25,7 @@ const getSummarySucceeded = (): RuleExecutionSummary => ({
|
|||
const getSummaryFailed = (): RuleExecutionSummary => ({
|
||||
last_execution: {
|
||||
date: '2020-02-18T15:15:58.806Z',
|
||||
status: RuleExecutionStatus.failed,
|
||||
status: RuleExecutionStatusEnum.failed,
|
||||
status_order: 30,
|
||||
message:
|
||||
'Signal rule name: "Query with a rule id Number 1", id: "1ea5a820-4da1-4e82-92a1-2b43a7bece08", rule_id: "query-rule-id-1" has a time gap of 5 days (412682928ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.',
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
export * from './execution_event';
|
||||
export * from './execution_metrics';
|
||||
export * from './execution_result';
|
||||
export * from './execution_result.gen';
|
||||
export * from './execution_settings';
|
||||
export * from './execution_status';
|
||||
export * from './execution_status.gen';
|
||||
export * from './execution_summary';
|
||||
export * from './log_level';
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import { enumeration } from '@kbn/securitysolution-io-ts-types';
|
||||
import { enumFromString } from '../../../../utils/enum_from_string';
|
||||
import { assertUnreachable } from '../../../../utility_types';
|
||||
import { RuleExecutionStatus } from './execution_status';
|
||||
import type { RuleExecutionStatus } from './execution_status.gen';
|
||||
import { RuleExecutionStatusEnum } from './execution_status.gen';
|
||||
|
||||
export enum LogLevel {
|
||||
'trace' = 'trace',
|
||||
|
@ -67,13 +68,13 @@ export const logLevelFromString = enumFromString(LogLevel);
|
|||
|
||||
export const logLevelFromExecutionStatus = (status: RuleExecutionStatus): LogLevel => {
|
||||
switch (status) {
|
||||
case RuleExecutionStatus['going to run']:
|
||||
case RuleExecutionStatus.running:
|
||||
case RuleExecutionStatus.succeeded:
|
||||
case RuleExecutionStatusEnum['going to run']:
|
||||
case RuleExecutionStatusEnum.running:
|
||||
case RuleExecutionStatusEnum.succeeded:
|
||||
return LogLevel.info;
|
||||
case RuleExecutionStatus['partial failure']:
|
||||
case RuleExecutionStatusEnum['partial failure']:
|
||||
return LogLevel.warn;
|
||||
case RuleExecutionStatus.failed:
|
||||
case RuleExecutionStatusEnum.failed:
|
||||
return LogLevel.error;
|
||||
default:
|
||||
assertUnreachable(status);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
import { RuleExecutionStatus } from '../../model/execution_status.gen';
|
||||
import {
|
||||
SortFieldOfRuleExecutionResult,
|
||||
RuleExecutionResult,
|
||||
} from '../../model/execution_result.gen';
|
||||
import { SortOrder } from '../../../model/sorting.gen';
|
||||
|
||||
export type GetRuleExecutionEventsRequestQuery = z.infer<typeof GetRuleExecutionEventsRequestQuery>;
|
||||
export const GetRuleExecutionEventsRequestQuery = z.object({
|
||||
/**
|
||||
* Start date of the time range to query
|
||||
*/
|
||||
start: z.string().datetime(),
|
||||
/**
|
||||
* End date of the time range to query
|
||||
*/
|
||||
end: z.string().datetime(),
|
||||
/**
|
||||
* Query text to filter results by
|
||||
*/
|
||||
query_text: z.string().optional().default(''),
|
||||
/**
|
||||
* Comma-separated list of rule execution statuses to filter results by
|
||||
*/
|
||||
status_filters: z
|
||||
.preprocess(
|
||||
(value: unknown) =>
|
||||
typeof value === 'string' ? (value === '' ? [] : value.split(',')) : value,
|
||||
z.array(RuleExecutionStatus)
|
||||
)
|
||||
.optional()
|
||||
.default([]),
|
||||
/**
|
||||
* Field to sort results by
|
||||
*/
|
||||
sort_field: SortFieldOfRuleExecutionResult.optional().default('timestamp'),
|
||||
/**
|
||||
* Sort order to sort results by
|
||||
*/
|
||||
sort_order: SortOrder.optional().default('desc'),
|
||||
/**
|
||||
* Page number to return
|
||||
*/
|
||||
page: z.coerce.number().int().optional().default(1),
|
||||
/**
|
||||
* Number of results per page
|
||||
*/
|
||||
per_page: z.coerce.number().int().optional().default(20),
|
||||
});
|
||||
export type GetRuleExecutionEventsRequestQueryInput = z.input<
|
||||
typeof GetRuleExecutionEventsRequestQuery
|
||||
>;
|
||||
|
||||
export type GetRuleExecutionEventsRequestParams = z.infer<
|
||||
typeof GetRuleExecutionEventsRequestParams
|
||||
>;
|
||||
export const GetRuleExecutionEventsRequestParams = z.object({
|
||||
/**
|
||||
* Saved object ID of the rule to get execution results for
|
||||
*/
|
||||
ruleId: z.string().min(1),
|
||||
});
|
||||
export type GetRuleExecutionEventsRequestParamsInput = z.input<
|
||||
typeof GetRuleExecutionEventsRequestParams
|
||||
>;
|
||||
|
||||
export type GetRuleExecutionEventsResponse = z.infer<typeof GetRuleExecutionEventsResponse>;
|
||||
export const GetRuleExecutionEventsResponse = z.object({
|
||||
events: z.array(RuleExecutionResult).optional(),
|
||||
total: z.number().int().optional(),
|
||||
});
|
|
@ -0,0 +1,92 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Get Rule Execution Events API endpoint
|
||||
version: '1'
|
||||
paths:
|
||||
/internal/detection_engine/rules/{ruleId}/execution/events:
|
||||
put:
|
||||
operationId: GetRuleExecutionEvents
|
||||
x-codegen-enabled: true
|
||||
summary: Returns execution events of a given rule (aggregated by execution UUID) from Event Log.
|
||||
tags:
|
||||
- Rule Execution Log API
|
||||
parameters:
|
||||
- name: ruleId
|
||||
in: path
|
||||
required: true
|
||||
description: Saved object ID of the rule to get execution results for
|
||||
schema:
|
||||
type: string
|
||||
minLength: 1
|
||||
- name: start
|
||||
in: query
|
||||
required: true
|
||||
description: Start date of the time range to query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: end
|
||||
in: query
|
||||
required: true
|
||||
description: End date of the time range to query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: query_text
|
||||
in: query
|
||||
required: false
|
||||
description: Query text to filter results by
|
||||
schema:
|
||||
type: string
|
||||
default: ''
|
||||
- name: status_filters
|
||||
in: query
|
||||
required: false
|
||||
description: Comma-separated list of rule execution statuses to filter results by
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/execution_status.schema.yaml#/components/schemas/RuleExecutionStatus'
|
||||
default: []
|
||||
- name: sort_field
|
||||
in: query
|
||||
required: false
|
||||
description: Field to sort results by
|
||||
schema:
|
||||
$ref: '../../model/execution_result.schema.yaml#/components/schemas/SortFieldOfRuleExecutionResult'
|
||||
default: timestamp
|
||||
- name: sort_order
|
||||
in: query
|
||||
required: false
|
||||
description: Sort order to sort results by
|
||||
schema:
|
||||
$ref: '../../../model/sorting.schema.yaml#/components/schemas/SortOrder'
|
||||
default: desc
|
||||
- name: page
|
||||
in: query
|
||||
required: false
|
||||
description: Page number to return
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
- name: per_page
|
||||
in: query
|
||||
required: false
|
||||
description: Number of results per page
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
responses:
|
||||
200:
|
||||
description: Indicates a successful call
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
events:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/execution_result.schema.yaml#/components/schemas/RuleExecutionResult'
|
||||
total:
|
||||
type: integer
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/*
|
||||
* NOTICE: Do not edit this file manually.
|
||||
* This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator.
|
||||
*/
|
||||
|
||||
import { RuleExecutionStatus } from '../../model/execution_status.gen';
|
||||
import {
|
||||
SortFieldOfRuleExecutionResult,
|
||||
RuleExecutionResult,
|
||||
} from '../../model/execution_result.gen';
|
||||
import { SortOrder } from '../../../model/sorting.gen';
|
||||
|
||||
export type GetRuleExecutionResultsRequestQuery = z.infer<
|
||||
typeof GetRuleExecutionResultsRequestQuery
|
||||
>;
|
||||
export const GetRuleExecutionResultsRequestQuery = z.object({
|
||||
/**
|
||||
* Start date of the time range to query
|
||||
*/
|
||||
start: z.string().datetime(),
|
||||
/**
|
||||
* End date of the time range to query
|
||||
*/
|
||||
end: z.string().datetime(),
|
||||
/**
|
||||
* Query text to filter results by
|
||||
*/
|
||||
query_text: z.string().optional().default(''),
|
||||
/**
|
||||
* Comma-separated list of rule execution statuses to filter results by
|
||||
*/
|
||||
status_filters: z
|
||||
.preprocess(
|
||||
(value: unknown) =>
|
||||
typeof value === 'string' ? (value === '' ? [] : value.split(',')) : value,
|
||||
z.array(RuleExecutionStatus)
|
||||
)
|
||||
.optional()
|
||||
.default([]),
|
||||
/**
|
||||
* Field to sort results by
|
||||
*/
|
||||
sort_field: SortFieldOfRuleExecutionResult.optional().default('timestamp'),
|
||||
/**
|
||||
* Sort order to sort results by
|
||||
*/
|
||||
sort_order: SortOrder.optional().default('desc'),
|
||||
/**
|
||||
* Page number to return
|
||||
*/
|
||||
page: z.coerce.number().int().optional().default(1),
|
||||
/**
|
||||
* Number of results per page
|
||||
*/
|
||||
per_page: z.coerce.number().int().optional().default(20),
|
||||
});
|
||||
export type GetRuleExecutionResultsRequestQueryInput = z.input<
|
||||
typeof GetRuleExecutionResultsRequestQuery
|
||||
>;
|
||||
|
||||
export type GetRuleExecutionResultsRequestParams = z.infer<
|
||||
typeof GetRuleExecutionResultsRequestParams
|
||||
>;
|
||||
export const GetRuleExecutionResultsRequestParams = z.object({
|
||||
/**
|
||||
* Saved object ID of the rule to get execution results for
|
||||
*/
|
||||
ruleId: z.string().min(1),
|
||||
});
|
||||
export type GetRuleExecutionResultsRequestParamsInput = z.input<
|
||||
typeof GetRuleExecutionResultsRequestParams
|
||||
>;
|
||||
|
||||
export type GetRuleExecutionResultsResponse = z.infer<typeof GetRuleExecutionResultsResponse>;
|
||||
export const GetRuleExecutionResultsResponse = z.object({
|
||||
events: z.array(RuleExecutionResult).optional(),
|
||||
total: z.number().int().optional(),
|
||||
});
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { ruleExecutionResultMock } from '../../model/execution_result.mock';
|
||||
import type { GetRuleExecutionResultsResponse } from './get_rule_execution_results_route';
|
||||
import type { GetRuleExecutionResultsResponse } from './get_rule_execution_results_route.gen';
|
||||
|
||||
const getSomeResponse = (): GetRuleExecutionResultsResponse => {
|
||||
const results = ruleExecutionResultMock.getSomeResults();
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
title: Get Rule Execution Results API endpoint
|
||||
version: '1'
|
||||
paths:
|
||||
/internal/detection_engine/rules/{ruleId}/execution/results:
|
||||
put:
|
||||
operationId: GetRuleExecutionResults
|
||||
x-codegen-enabled: true
|
||||
summary: Returns execution results of a given rule (aggregated by execution UUID) from Event Log.
|
||||
tags:
|
||||
- Rule Execution Log API
|
||||
parameters:
|
||||
- name: ruleId
|
||||
in: path
|
||||
required: true
|
||||
description: Saved object ID of the rule to get execution results for
|
||||
schema:
|
||||
type: string
|
||||
minLength: 1
|
||||
- name: start
|
||||
in: query
|
||||
required: true
|
||||
description: Start date of the time range to query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: end
|
||||
in: query
|
||||
required: true
|
||||
description: End date of the time range to query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: query_text
|
||||
in: query
|
||||
required: false
|
||||
description: Query text to filter results by
|
||||
schema:
|
||||
type: string
|
||||
default: ''
|
||||
- name: status_filters
|
||||
in: query
|
||||
required: false
|
||||
description: Comma-separated list of rule execution statuses to filter results by
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/execution_status.schema.yaml#/components/schemas/RuleExecutionStatus'
|
||||
default: []
|
||||
- name: sort_field
|
||||
in: query
|
||||
required: false
|
||||
description: Field to sort results by
|
||||
schema:
|
||||
$ref: '../../model/execution_result.schema.yaml#/components/schemas/SortFieldOfRuleExecutionResult'
|
||||
default: timestamp
|
||||
- name: sort_order
|
||||
in: query
|
||||
required: false
|
||||
description: Sort order to sort results by
|
||||
schema:
|
||||
$ref: '../../../model/sorting.schema.yaml#/components/schemas/SortOrder'
|
||||
default: desc
|
||||
- name: page
|
||||
in: query
|
||||
required: false
|
||||
description: Page number to return
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
- name: per_page
|
||||
in: query
|
||||
required: false
|
||||
description: Number of results per page
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
responses:
|
||||
200:
|
||||
description: Indicates a successful call
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
events:
|
||||
type: array
|
||||
items:
|
||||
$ref: '../../model/execution_result.schema.yaml#/components/schemas/RuleExecutionResult'
|
||||
total:
|
||||
type: integer
|
|
@ -5,32 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { left } from 'fp-ts/lib/Either';
|
||||
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { stringifyZodError } from '@kbn/securitysolution-es-utils';
|
||||
import { expectParseError, expectParseSuccess } from '../../../../../test/zod_helpers';
|
||||
import { RuleExecutionStatus } from '../../model';
|
||||
import { GetRuleExecutionResultsRequestQuery } from './get_rule_execution_results_route.gen';
|
||||
|
||||
import { RULE_EXECUTION_STATUSES } from '../../model/execution_status';
|
||||
import {
|
||||
DefaultSortField,
|
||||
DefaultRuleExecutionStatusCsvArray,
|
||||
} from './get_rule_execution_results_route';
|
||||
const StatusFiltersSchema = GetRuleExecutionResultsRequestQuery.shape.status_filters;
|
||||
const SortFieldSchema = GetRuleExecutionResultsRequestQuery.shape.sort_field;
|
||||
|
||||
describe('Request schema of Get rule execution results', () => {
|
||||
describe('DefaultRuleExecutionStatusCsvArray', () => {
|
||||
describe('Validation succeeds', () => {
|
||||
describe('when input is a single rule execution status', () => {
|
||||
const cases = RULE_EXECUTION_STATUSES.map((supportedStatus) => {
|
||||
const cases = RuleExecutionStatus.options.map((supportedStatus) => {
|
||||
return { input: supportedStatus };
|
||||
});
|
||||
|
||||
cases.forEach(({ input }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultRuleExecutionStatusCsvArray.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const expectedOutput = [input]; // note that it's an array after decode
|
||||
const result = StatusFiltersSchema.safeParse(input);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(expectedOutput);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -43,12 +40,11 @@ describe('Request schema of Get rule execution results', () => {
|
|||
|
||||
cases.forEach(({ input }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultRuleExecutionStatusCsvArray.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const expectedOutput = input;
|
||||
const result = StatusFiltersSchema.safeParse(input);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(expectedOutput);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -67,11 +63,10 @@ describe('Request schema of Get rule execution results', () => {
|
|||
|
||||
cases.forEach(({ input, expectedOutput }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultRuleExecutionStatusCsvArray.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const result = StatusFiltersSchema.safeParse(input);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(expectedOutput);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -82,37 +77,30 @@ describe('Request schema of Get rule execution results', () => {
|
|||
const cases = [
|
||||
{
|
||||
input: 'val',
|
||||
expectedErrors: [
|
||||
'Invalid value "val" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received 'val'",
|
||||
},
|
||||
{
|
||||
input: '5',
|
||||
expectedErrors: [
|
||||
'Invalid value "5" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received '5'",
|
||||
},
|
||||
{
|
||||
input: 5,
|
||||
expectedErrors: [
|
||||
'Invalid value "5" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors: 'Expected array, received number',
|
||||
},
|
||||
{
|
||||
input: {},
|
||||
expectedErrors: [
|
||||
'Invalid value "{}" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors: 'Expected array, received object',
|
||||
},
|
||||
];
|
||||
|
||||
cases.forEach(({ input, expectedErrors }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultRuleExecutionStatusCsvArray.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const result = StatusFiltersSchema.safeParse(input);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual(expectedErrors);
|
||||
expect(message.schema).toEqual({});
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(expectedErrors);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -121,34 +109,27 @@ describe('Request schema of Get rule execution results', () => {
|
|||
const cases = [
|
||||
{
|
||||
input: ['value 1', 5],
|
||||
expectedErrors: [
|
||||
'Invalid value "value 1" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
'Invalid value "5" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received 'value 1', 1: Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received number",
|
||||
},
|
||||
{
|
||||
input: ['value 1', 'succeeded'],
|
||||
expectedErrors: [
|
||||
'Invalid value "value 1" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received 'value 1'",
|
||||
},
|
||||
{
|
||||
input: ['', 5, {}],
|
||||
expectedErrors: [
|
||||
'Invalid value "" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
'Invalid value "5" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
'Invalid value "{}" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received '', 1: Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received number, 2: Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received object",
|
||||
},
|
||||
];
|
||||
|
||||
cases.forEach(({ input, expectedErrors }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultRuleExecutionStatusCsvArray.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const result = StatusFiltersSchema.safeParse(input);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual(expectedErrors);
|
||||
expect(message.schema).toEqual({});
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(expectedErrors);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -157,34 +138,27 @@ describe('Request schema of Get rule execution results', () => {
|
|||
const cases = [
|
||||
{
|
||||
input: 'value 1,5',
|
||||
expectedErrors: [
|
||||
'Invalid value "value 1" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
'Invalid value "5" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received 'value 1', 1: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received '5'",
|
||||
},
|
||||
{
|
||||
input: 'value 1,succeeded',
|
||||
expectedErrors: [
|
||||
'Invalid value "value 1" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received 'value 1'",
|
||||
},
|
||||
{
|
||||
input: ',5,{}',
|
||||
expectedErrors: [
|
||||
'Invalid value "" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
'Invalid value "5" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
'Invalid value "{}" supplied to "DefaultCsvArray<RuleExecutionStatus>"',
|
||||
],
|
||||
expectedErrors:
|
||||
"0: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received '', 1: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received '5', 2: Invalid enum value. Expected 'going to run' | 'running' | 'partial failure' | 'failed' | 'succeeded', received '{}'",
|
||||
},
|
||||
];
|
||||
|
||||
cases.forEach(({ input, expectedErrors }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultRuleExecutionStatusCsvArray.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const result = StatusFiltersSchema.safeParse(input);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual(expectedErrors);
|
||||
expect(message.schema).toEqual({});
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(expectedErrors);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -192,16 +166,14 @@ describe('Request schema of Get rule execution results', () => {
|
|||
|
||||
describe('Validation returns default value (an empty array)', () => {
|
||||
describe('when input is', () => {
|
||||
const cases = [{ input: null }, { input: undefined }, { input: '' }, { input: [] }];
|
||||
const cases = [{ input: undefined }, { input: '' }, { input: [] }];
|
||||
|
||||
cases.forEach(({ input }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultRuleExecutionStatusCsvArray.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const expectedOutput: string[] = [];
|
||||
const result = StatusFiltersSchema.safeParse(input);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(expectedOutput);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -222,11 +194,9 @@ describe('Request schema of Get rule execution results', () => {
|
|||
|
||||
cases.forEach(({ input }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultSortField.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual(input);
|
||||
const result = SortFieldSchema.safeParse(input);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual(input);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -244,12 +214,10 @@ describe('Request schema of Get rule execution results', () => {
|
|||
|
||||
cases.forEach(({ input }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultSortField.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
const expectedErrors = [`Invalid value "${input}" supplied to "DefaultSortField"`];
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual(expectedErrors);
|
||||
expect(message.schema).toEqual({});
|
||||
const expectedErrors = `Invalid enum value. Expected 'timestamp' | 'duration_ms' | 'gap_duration_s' | 'indexing_duration_ms' | 'search_duration_ms' | 'schedule_delay_ms', received '${input}'`;
|
||||
const result = SortFieldSchema.safeParse(input);
|
||||
expectParseError(result);
|
||||
expect(stringifyZodError(result.error)).toEqual(expectedErrors);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -257,18 +225,38 @@ describe('Request schema of Get rule execution results', () => {
|
|||
|
||||
describe('Validation returns the default sort field "timestamp"', () => {
|
||||
describe('when input is', () => {
|
||||
const cases = [{ input: null }, { input: undefined }];
|
||||
const cases = [{ input: undefined }];
|
||||
|
||||
cases.forEach(({ input }) => {
|
||||
it(`${input}`, () => {
|
||||
const decoded = DefaultSortField.decode(input);
|
||||
const message = pipe(decoded, foldLeftRight);
|
||||
|
||||
expect(getPaths(left(message.errors))).toEqual([]);
|
||||
expect(message.schema).toEqual('timestamp');
|
||||
const result = SortFieldSchema.safeParse(input);
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual('timestamp');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetRuleExecutionResultsRequestQuery', () => {
|
||||
it('should convert string values to numbers', () => {
|
||||
const result = GetRuleExecutionResultsRequestQuery.safeParse({
|
||||
start: '2021-08-01T00:00:00.000Z',
|
||||
end: '2021-08-02T00:00:00.000Z',
|
||||
page: '1',
|
||||
per_page: '10',
|
||||
});
|
||||
expectParseSuccess(result);
|
||||
expect(result.data).toEqual({
|
||||
end: '2021-08-02T00:00:00.000Z',
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
query_text: '',
|
||||
sort_field: 'timestamp',
|
||||
sort_order: 'desc',
|
||||
start: '2021-08-01T00:00:00.000Z',
|
||||
status_filters: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* 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 { DefaultPage, DefaultPerPage } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
import {
|
||||
defaultCsvArray,
|
||||
DefaultEmptyString,
|
||||
defaultValue,
|
||||
IsoDateString,
|
||||
NonEmptyString,
|
||||
} from '@kbn/securitysolution-io-ts-types';
|
||||
|
||||
import { DefaultSortOrderDesc } from '../../../model';
|
||||
import {
|
||||
RuleExecutionResult,
|
||||
SortFieldOfRuleExecutionResult,
|
||||
TRuleExecutionStatus,
|
||||
} from '../../model';
|
||||
|
||||
/**
|
||||
* Types the DefaultRuleExecutionStatusCsvArray as:
|
||||
* - If not specified, then a default empty array will be set
|
||||
* - If an array is sent in, then the array will be validated to ensure all elements are a RuleExecutionStatus
|
||||
* (or that the array is empty)
|
||||
* - If a CSV string is sent in, then it will be parsed to an array which will be validated
|
||||
*/
|
||||
export const DefaultRuleExecutionStatusCsvArray = defaultCsvArray(TRuleExecutionStatus);
|
||||
|
||||
/**
|
||||
* Types the DefaultSortField as:
|
||||
* - If undefined, then a default sort field of 'timestamp' will be set
|
||||
* - If a string is sent in, then the string will be validated to ensure it is as valid sortFields
|
||||
*/
|
||||
export const DefaultSortField = defaultValue(
|
||||
SortFieldOfRuleExecutionResult,
|
||||
'timestamp',
|
||||
'DefaultSortField'
|
||||
);
|
||||
|
||||
/**
|
||||
* Path parameters of the API route.
|
||||
*/
|
||||
export type GetRuleExecutionResultsRequestParams = t.TypeOf<
|
||||
typeof GetRuleExecutionResultsRequestParams
|
||||
>;
|
||||
export const GetRuleExecutionResultsRequestParams = t.exact(
|
||||
t.type({
|
||||
ruleId: NonEmptyString,
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Query string parameters of the API route.
|
||||
*/
|
||||
export type GetRuleExecutionResultsRequestQuery = t.TypeOf<
|
||||
typeof GetRuleExecutionResultsRequestQuery
|
||||
>;
|
||||
export const GetRuleExecutionResultsRequestQuery = t.exact(
|
||||
t.type({
|
||||
start: IsoDateString,
|
||||
end: IsoDateString,
|
||||
query_text: DefaultEmptyString, // defaults to ''
|
||||
status_filters: DefaultRuleExecutionStatusCsvArray, // defaults to []
|
||||
sort_field: DefaultSortField, // defaults to 'timestamp'
|
||||
sort_order: DefaultSortOrderDesc, // defaults to 'desc'
|
||||
page: DefaultPage, // defaults to 1
|
||||
per_page: DefaultPerPage, // defaults to 20
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Response body of the API route.
|
||||
*/
|
||||
export type GetRuleExecutionResultsResponse = t.TypeOf<typeof GetRuleExecutionResultsResponse>;
|
||||
export const GetRuleExecutionResultsResponse = t.exact(
|
||||
t.type({
|
||||
events: t.array(RuleExecutionResult),
|
||||
total: t.number,
|
||||
})
|
||||
);
|
|
@ -19,7 +19,7 @@ import {
|
|||
SavedObjectResolveAliasTargetId,
|
||||
SavedObjectResolveOutcome,
|
||||
} from '../../detection_engine/model/rule_schema';
|
||||
import { errorSchema, success, success_count as successCount } from '../../detection_engine';
|
||||
import { ErrorSchema, success, success_count as successCount } from '../../detection_engine';
|
||||
|
||||
export const BareNoteSchema = runtimeTypes.intersection([
|
||||
runtimeTypes.type({
|
||||
|
@ -499,7 +499,7 @@ export const importTimelineResultSchema = runtimeTypes.exact(
|
|||
success_count: successCount,
|
||||
timelines_installed: PositiveInteger,
|
||||
timelines_updated: PositiveInteger,
|
||||
errors: runtimeTypes.array(errorSchema),
|
||||
errors: runtimeTypes.array(ErrorSchema),
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
import { RuleExecutionStatus } from '../../api/detection_engine';
|
||||
import type { RuleExecutionStatus } from '../../api/detection_engine';
|
||||
import { RuleExecutionStatusEnum } from '../../api/detection_engine';
|
||||
import { prepareKQLStringParam } from '../../utils/kql';
|
||||
import {
|
||||
ENABLED_FIELD,
|
||||
|
@ -75,11 +76,11 @@ export function convertRulesFilterToKQL({
|
|||
kql.push(`NOT ${convertRuleTypesToKQL(excludeRuleTypes)}`);
|
||||
}
|
||||
|
||||
if (ruleExecutionStatus === RuleExecutionStatus.succeeded) {
|
||||
if (ruleExecutionStatus === RuleExecutionStatusEnum.succeeded) {
|
||||
kql.push(`${LAST_RUN_OUTCOME_FIELD}: "succeeded"`);
|
||||
} else if (ruleExecutionStatus === RuleExecutionStatus['partial failure']) {
|
||||
} else if (ruleExecutionStatus === RuleExecutionStatusEnum['partial failure']) {
|
||||
kql.push(`${LAST_RUN_OUTCOME_FIELD}: "warning"`);
|
||||
} else if (ruleExecutionStatus === RuleExecutionStatus.failed) {
|
||||
} else if (ruleExecutionStatus === RuleExecutionStatusEnum.failed) {
|
||||
kql.push(`${LAST_RUN_OUTCOME_FIELD}: "failed"`);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ import React, { useCallback } from 'react';
|
|||
import { replace } from 'lodash';
|
||||
import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
import { RuleExecutionStatus } from '../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type { RuleExecutionStatus } from '../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { ExecutionStatusFilter } from '../../../../rule_monitoring';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
@ -36,10 +37,10 @@ export const replaceQueryTextAliases = (queryText: string): string => {
|
|||
};
|
||||
|
||||
// This only includes statuses which are or can be final
|
||||
const STATUS_FILTERS = [
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatus.failed,
|
||||
RuleExecutionStatus['partial failure'],
|
||||
const STATUS_FILTERS: RuleExecutionStatus[] = [
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
RuleExecutionStatusEnum.failed,
|
||||
RuleExecutionStatusEnum['partial failure'],
|
||||
];
|
||||
|
||||
interface ExecutionLogTableSearchProps {
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
EuiButtonIcon,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type { SecurityJob } from '../../../../common/components/ml_popover/types';
|
||||
import * as i18n from '../rules_table/translations';
|
||||
|
||||
|
@ -64,7 +64,7 @@ const MlRuleWarningPopoverComponent: React.FC<MlRuleWarningPopoverComponentProps
|
|||
onClick={togglePopover}
|
||||
/>
|
||||
);
|
||||
const popoverTitle = getCapitalizedStatusText(RuleExecutionStatus['partial failure']);
|
||||
const popoverTitle = getCapitalizedStatusText(RuleExecutionStatusEnum['partial failure']);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
EuiButtonIcon,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type { SecurityJob } from '../../../../common/components/ml_popover/types';
|
||||
import * as i18n from './translations';
|
||||
|
||||
|
@ -64,7 +64,7 @@ const MlRuleWarningPopoverComponent: React.FC<MlRuleWarningPopoverComponentProps
|
|||
onClick={togglePopover}
|
||||
/>
|
||||
);
|
||||
const popoverTitle = getCapitalizedStatusText(RuleExecutionStatus['partial failure']);
|
||||
const popoverTitle = getCapitalizedStatusText(RuleExecutionStatusEnum['partial failure']);
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
|
|
|
@ -9,7 +9,8 @@ import React, { useState } from 'react';
|
|||
import type { EuiSelectableOption } from '@elastic/eui';
|
||||
import { EuiFilterButton, EuiPopover, EuiSelectable } from '@elastic/eui';
|
||||
import * as i18n from '../../../../../detections/pages/detection_engine/rules/translations';
|
||||
import { RuleExecutionStatus } from '../../../../../../common/api/detection_engine/rule_monitoring/model/execution_status';
|
||||
import type { RuleExecutionStatus } from '../../../../../../common/api/detection_engine';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../../common/api/detection_engine';
|
||||
import { getCapitalizedStatusText } from '../../../../../detections/components/rules/rule_execution_status/utils';
|
||||
import { RuleStatusBadge } from '../../../../../detections/components/rules/rule_execution_status/rule_status_badge';
|
||||
|
||||
|
@ -36,19 +37,19 @@ const RuleExecutionStatusSelectorComponent = ({
|
|||
|
||||
const selectableOptions: EuiSelectableOption[] = [
|
||||
{
|
||||
label: getCapitalizedStatusText(RuleExecutionStatus.succeeded) || '',
|
||||
data: { status: RuleExecutionStatus.succeeded },
|
||||
checked: selectedStatus === RuleExecutionStatus.succeeded ? 'on' : undefined,
|
||||
label: getCapitalizedStatusText(RuleExecutionStatusEnum.succeeded) || '',
|
||||
data: { status: RuleExecutionStatusEnum.succeeded },
|
||||
checked: selectedStatus === RuleExecutionStatusEnum.succeeded ? 'on' : undefined,
|
||||
},
|
||||
{
|
||||
label: getCapitalizedStatusText(RuleExecutionStatus['partial failure']) || '',
|
||||
data: { status: RuleExecutionStatus['partial failure'] },
|
||||
checked: selectedStatus === RuleExecutionStatus['partial failure'] ? 'on' : undefined,
|
||||
label: getCapitalizedStatusText(RuleExecutionStatusEnum['partial failure']) || '',
|
||||
data: { status: RuleExecutionStatusEnum['partial failure'] },
|
||||
checked: selectedStatus === RuleExecutionStatusEnum['partial failure'] ? 'on' : undefined,
|
||||
},
|
||||
{
|
||||
label: getCapitalizedStatusText(RuleExecutionStatus.failed) || '',
|
||||
data: { status: RuleExecutionStatus.failed },
|
||||
checked: selectedStatus === RuleExecutionStatus.failed ? 'on' : undefined,
|
||||
label: getCapitalizedStatusText(RuleExecutionStatusEnum.failed) || '',
|
||||
data: { status: RuleExecutionStatusEnum.failed },
|
||||
checked: selectedStatus === RuleExecutionStatusEnum.failed ? 'on' : undefined,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -13,11 +13,11 @@ import { useRuleManagementFilters } from '../../../../rule_management/logic/use_
|
|||
import { RULES_TABLE_ACTIONS } from '../../../../../common/lib/apm/user_actions';
|
||||
import { useStartTransaction } from '../../../../../common/lib/apm/use_start_transaction';
|
||||
import * as i18n from '../../../../../detections/pages/detection_engine/rules/translations';
|
||||
import type { RuleExecutionStatus } from '../../../../../../common/api/detection_engine/rule_monitoring/model/execution_status';
|
||||
import { useRulesTableContext } from '../rules_table/rules_table_context';
|
||||
import { TagsFilterPopover } from './tags_filter_popover';
|
||||
import { RuleExecutionStatusSelector } from './rule_execution_status_selector';
|
||||
import { RuleSearchField } from './rule_search_field';
|
||||
import type { RuleExecutionStatus } from '../../../../../../common/api/detection_engine';
|
||||
|
||||
const FilterWrapper = styled(EuiFlexGroup)`
|
||||
margin-bottom: ${({ theme }) => theme.eui.euiSizeXS};
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleStatusBadge } from './rule_status_badge';
|
||||
|
||||
describe('RuleStatusBadge', () => {
|
||||
it('renders capitalized status text', () => {
|
||||
render(<RuleStatusBadge status={RuleExecutionStatus.succeeded} />);
|
||||
render(<RuleStatusBadge status={RuleExecutionStatusEnum.succeeded} />);
|
||||
|
||||
expect(screen.getByText('Succeeded')).toBeInTheDocument();
|
||||
});
|
||||
|
|
|
@ -11,7 +11,8 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value';
|
|||
import { HealthTruncateText } from '../../../../common/components/health_truncate_text';
|
||||
import { getCapitalizedStatusText, getStatusColor } from './utils';
|
||||
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
interface RuleStatusBadgeProps {
|
||||
status: RuleExecutionStatus | null | undefined;
|
||||
|
@ -29,7 +30,8 @@ const RuleStatusBadgeComponent = ({
|
|||
showTooltip = true,
|
||||
}: RuleStatusBadgeProps) => {
|
||||
const isFailedStatus =
|
||||
status === RuleExecutionStatus.failed || status === RuleExecutionStatus['partial failure'];
|
||||
status === RuleExecutionStatusEnum.failed ||
|
||||
status === RuleExecutionStatusEnum['partial failure'];
|
||||
const statusText = getCapitalizedStatusText(status);
|
||||
|
||||
const statusTooltip = isFailedStatus && message ? message : statusText;
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleStatusFailedCallOut } from './rule_status_failed_callout';
|
||||
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
|
@ -32,22 +33,22 @@ describe('RuleStatusFailedCallOut', () => {
|
|||
});
|
||||
|
||||
it('is hidden if status is "going to run"', () => {
|
||||
const result = renderWith(RuleExecutionStatus['going to run']);
|
||||
const result = renderWith(RuleExecutionStatusEnum['going to run']);
|
||||
expect(result.queryByTestId(TEST_ID)).toBe(null);
|
||||
});
|
||||
|
||||
it('is hidden if status is "running"', () => {
|
||||
const result = renderWith(RuleExecutionStatus.running);
|
||||
const result = renderWith(RuleExecutionStatusEnum.running);
|
||||
expect(result.queryByTestId(TEST_ID)).toBe(null);
|
||||
});
|
||||
|
||||
it('is hidden if status is "succeeded"', () => {
|
||||
const result = renderWith(RuleExecutionStatus.succeeded);
|
||||
const result = renderWith(RuleExecutionStatusEnum.succeeded);
|
||||
expect(result.queryByTestId(TEST_ID)).toBe(null);
|
||||
});
|
||||
|
||||
it('is visible if status is "partial failure"', () => {
|
||||
const result = renderWith(RuleExecutionStatus['partial failure']);
|
||||
const result = renderWith(RuleExecutionStatusEnum['partial failure']);
|
||||
result.getByTestId(TEST_ID);
|
||||
result.getByText('Warning at');
|
||||
result.getByText('Jan 27, 2022 @ 15:03:31.176');
|
||||
|
@ -55,7 +56,7 @@ describe('RuleStatusFailedCallOut', () => {
|
|||
});
|
||||
|
||||
it('is visible if status is "failed"', () => {
|
||||
const result = renderWith(RuleExecutionStatus.failed);
|
||||
const result = renderWith(RuleExecutionStatusEnum.failed);
|
||||
result.getByTestId(TEST_ID);
|
||||
result.getByText('Rule failure at');
|
||||
result.getByText('Jan 27, 2022 @ 15:03:31.176');
|
||||
|
|
|
@ -10,7 +10,8 @@ import React from 'react';
|
|||
import { EuiCallOut, EuiCodeBlock } from '@elastic/eui';
|
||||
|
||||
import { FormattedDate } from '../../../../common/components/formatted_date';
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
|
@ -75,13 +76,13 @@ interface HelperProps {
|
|||
|
||||
const getPropsByStatus = (status: RuleExecutionStatus | null | undefined): HelperProps => {
|
||||
switch (status) {
|
||||
case RuleExecutionStatus.failed:
|
||||
case RuleExecutionStatusEnum.failed:
|
||||
return {
|
||||
shouldBeDisplayed: true,
|
||||
color: 'danger',
|
||||
title: i18n.ERROR_CALLOUT_TITLE,
|
||||
};
|
||||
case RuleExecutionStatus['partial failure']:
|
||||
case RuleExecutionStatusEnum['partial failure']:
|
||||
return {
|
||||
shouldBeDisplayed: true,
|
||||
color: 'warning',
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
import type { IconColor } from '@elastic/eui';
|
||||
import { capitalize } from 'lodash';
|
||||
import { assertUnreachable } from '../../../../../common/utility_types';
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
export const getStatusText = (value: RuleExecutionStatus | null | undefined): string | null => {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value === RuleExecutionStatus['partial failure']) {
|
||||
if (value === RuleExecutionStatusEnum['partial failure']) {
|
||||
return 'warning';
|
||||
}
|
||||
return value;
|
||||
|
@ -31,16 +32,16 @@ export const getStatusColor = (status: RuleExecutionStatus | null | undefined):
|
|||
if (status == null) {
|
||||
return 'subdued';
|
||||
}
|
||||
if (status === RuleExecutionStatus.succeeded) {
|
||||
if (status === RuleExecutionStatusEnum.succeeded) {
|
||||
return 'success';
|
||||
}
|
||||
if (status === RuleExecutionStatus.failed) {
|
||||
if (status === RuleExecutionStatusEnum.failed) {
|
||||
return 'danger';
|
||||
}
|
||||
if (
|
||||
status === RuleExecutionStatus.running ||
|
||||
status === RuleExecutionStatus['partial failure'] ||
|
||||
status === RuleExecutionStatus['going to run']
|
||||
status === RuleExecutionStatusEnum.running ||
|
||||
status === RuleExecutionStatusEnum['partial failure'] ||
|
||||
status === RuleExecutionStatusEnum['going to run']
|
||||
) {
|
||||
return 'warning';
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { validate } from '@kbn/securitysolution-io-ts-utils';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
InstallPrebuiltRulesAndTimelinesResponse,
|
||||
|
@ -116,9 +115,7 @@ export const createPrepackagedRules = async (
|
|||
throw new AggregateError(result.errors, 'Error installing new prebuilt rules');
|
||||
}
|
||||
|
||||
const { result: timelinesResult, error: timelinesError } = await performTimelinesInstallation(
|
||||
context
|
||||
);
|
||||
const { result: timelinesResult } = await performTimelinesInstallation(context);
|
||||
|
||||
await upgradePrebuiltRules(rulesClient, rulesToUpdate);
|
||||
|
||||
|
@ -129,17 +126,5 @@ export const createPrepackagedRules = async (
|
|||
timelines_updated: timelinesResult?.timelines_updated ?? 0,
|
||||
};
|
||||
|
||||
const [validated, genericErrors] = validate(
|
||||
prebuiltRulesOutput,
|
||||
InstallPrebuiltRulesAndTimelinesResponse
|
||||
);
|
||||
|
||||
if (genericErrors != null && timelinesError != null) {
|
||||
throw new PrepackagedRulesError(
|
||||
[genericErrors, timelinesError].filter((msg) => msg != null).join(', '),
|
||||
500
|
||||
);
|
||||
}
|
||||
|
||||
return validated;
|
||||
return InstallPrebuiltRulesAndTimelinesResponse.parse(prebuiltRulesOutput);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
ExportRulesRequestQuery,
|
||||
} from '../../../../../../../common/api/detection_engine/rule_management';
|
||||
|
||||
import { buildRouteValidation } from '../../../../../../utils/build_validation/route_validation';
|
||||
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
|
||||
import type { ConfigType } from '../../../../../../config';
|
||||
import { getNonPackagedRulesCount } from '../../../logic/search/get_existing_prepackaged_rules';
|
||||
|
@ -40,8 +40,8 @@ export const exportRulesRoute = (
|
|||
version: '2023-10-31',
|
||||
validate: {
|
||||
request: {
|
||||
query: buildRouteValidation(ExportRulesRequestQuery),
|
||||
body: buildRouteValidation(ExportRulesRequestBody),
|
||||
query: buildRouteValidationWithZod(ExportRulesRequestQuery),
|
||||
body: buildRouteValidationWithZod(ExportRulesRequestBody),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,40 +5,34 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import type { IKibanaResponse } from '@kbn/core/server';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { createPromiseFromStreams } from '@kbn/utils';
|
||||
import { chunk } from 'lodash/fp';
|
||||
import { extname } from 'path';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { createPromiseFromStreams } from '@kbn/utils';
|
||||
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import { validate } from '@kbn/securitysolution-io-ts-utils';
|
||||
|
||||
import type { IKibanaResponse } from '@kbn/core/server';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants';
|
||||
import type { ImportRulesRequestQueryDecoded } from '../../../../../../../common/api/detection_engine/rule_management';
|
||||
import {
|
||||
ImportRulesRequestQuery,
|
||||
ImportRulesResponse,
|
||||
} from '../../../../../../../common/api/detection_engine/rule_management';
|
||||
|
||||
import type { HapiReadableStream, SecuritySolutionPluginRouter } from '../../../../../../types';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants';
|
||||
import type { ConfigType } from '../../../../../../config';
|
||||
import type { SetupPlugins } from '../../../../../../plugin';
|
||||
import type { HapiReadableStream, SecuritySolutionPluginRouter } from '../../../../../../types';
|
||||
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
|
||||
import { buildMlAuthz } from '../../../../../machine_learning/authz';
|
||||
import type { ImportRuleResponse, BulkError } from '../../../../routes/utils';
|
||||
import { isBulkError, isImportRegular, buildSiemResponse } from '../../../../routes/utils';
|
||||
|
||||
import type { BulkError, ImportRuleResponse } from '../../../../routes/utils';
|
||||
import { buildSiemResponse, isBulkError, isImportRegular } from '../../../../routes/utils';
|
||||
import { importRuleActionConnectors } from '../../../logic/import/action_connectors/import_rule_action_connectors';
|
||||
import { createRulesAndExceptionsStreamFromNdJson } from '../../../logic/import/create_rules_stream_from_ndjson';
|
||||
import { getReferencedExceptionLists } from '../../../logic/import/gather_referenced_exceptions';
|
||||
import type { RuleExceptionsPromiseFromStreams } from '../../../logic/import/import_rules_utils';
|
||||
import { importRules as importRulesHelper } from '../../../logic/import/import_rules_utils';
|
||||
import { importRuleExceptions } from '../../../logic/import/import_rule_exceptions';
|
||||
import {
|
||||
getTupleDuplicateErrorsAndUniqueRules,
|
||||
migrateLegacyActionsIds,
|
||||
} from '../../../utils/utils';
|
||||
import { createRulesAndExceptionsStreamFromNdJson } from '../../../logic/import/create_rules_stream_from_ndjson';
|
||||
import { buildRouteValidation } from '../../../../../../utils/build_validation/route_validation';
|
||||
import type { RuleExceptionsPromiseFromStreams } from '../../../logic/import/import_rules_utils';
|
||||
import { importRules as importRulesHelper } from '../../../logic/import/import_rules_utils';
|
||||
import { getReferencedExceptionLists } from '../../../logic/import/gather_referenced_exceptions';
|
||||
import { importRuleExceptions } from '../../../logic/import/import_rule_exceptions';
|
||||
import { importRuleActionConnectors } from '../../../logic/import/action_connectors/import_rule_action_connectors';
|
||||
|
||||
const CHUNK_PARSED_OBJECT_SIZE = 50;
|
||||
|
||||
|
@ -64,10 +58,7 @@ export const importRulesRoute = (
|
|||
version: '2023-10-31',
|
||||
validate: {
|
||||
request: {
|
||||
query: buildRouteValidation<
|
||||
typeof ImportRulesRequestQuery,
|
||||
ImportRulesRequestQueryDecoded
|
||||
>(ImportRulesRequestQuery),
|
||||
query: buildRouteValidationWithZod(ImportRulesRequestQuery),
|
||||
body: schema.any(), // validation on file object is accomplished later in the handler.
|
||||
},
|
||||
},
|
||||
|
@ -202,12 +193,7 @@ export const importRulesRoute = (
|
|||
action_connectors_warnings: actionConnectorWarnings,
|
||||
};
|
||||
|
||||
const [validated, errors] = validate(importRules, ImportRulesResponse);
|
||||
if (errors != null) {
|
||||
return siemResponse.error({ statusCode: 500, body: errors });
|
||||
} else {
|
||||
return response.ok({ body: validated ?? {} });
|
||||
}
|
||||
return response.ok({ body: ImportRulesResponse.parse(importRules) });
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
return siemResponse.error({
|
||||
|
|
|
@ -29,11 +29,8 @@ export const readTagsRoute = (router: SecuritySolutionPluginRouter) => {
|
|||
},
|
||||
async (context, request, response): Promise<IKibanaResponse<ReadTagsResponse>> => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
const rulesClient = (await context.alerting)?.getRulesClient();
|
||||
|
||||
if (!rulesClient) {
|
||||
return siemResponse.error({ statusCode: 404 });
|
||||
}
|
||||
const ctx = await context.resolve(['alerting']);
|
||||
const rulesClient = ctx.alerting.getRulesClient();
|
||||
|
||||
try {
|
||||
const tags = await readTags({
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import type { IKibanaResponse } from '@kbn/core/server';
|
||||
import { buildRouteValidation } from '../../../../../../utils/build_validation/route_validation';
|
||||
import { buildSiemResponse } from '../../../../routes/utils';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
|
||||
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
|
||||
import { buildSiemResponse } from '../../../../routes/utils';
|
||||
|
||||
import type { GetRuleExecutionResultsResponse } from '../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import {
|
||||
GET_RULE_EXECUTION_RESULTS_URL,
|
||||
GetRuleExecutionResultsRequestParams,
|
||||
GetRuleExecutionResultsRequestQuery,
|
||||
GET_RULE_EXECUTION_RESULTS_URL,
|
||||
} from '../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
/**
|
||||
|
@ -36,8 +36,8 @@ export const getRuleExecutionResultsRoute = (router: SecuritySolutionPluginRoute
|
|||
version: '1',
|
||||
validate: {
|
||||
request: {
|
||||
params: buildRouteValidation(GetRuleExecutionResultsRequestParams),
|
||||
query: buildRouteValidation(GetRuleExecutionResultsRequestQuery),
|
||||
params: buildRouteValidationWithZod(GetRuleExecutionResultsRequestParams),
|
||||
query: buildRouteValidationWithZod(GetRuleExecutionResultsRequestQuery),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -15,10 +15,11 @@ import type {
|
|||
NumberOfLoggedMessages,
|
||||
RuleExecutionStats,
|
||||
TopMessages,
|
||||
RuleExecutionStatus,
|
||||
} from '../../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import {
|
||||
RuleExecutionEventType,
|
||||
RuleExecutionStatus,
|
||||
RuleExecutionStatusEnum,
|
||||
LogLevel,
|
||||
} from '../../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
|
@ -72,8 +73,8 @@ export const getRuleExecutionStatsAggregation = (
|
|||
{
|
||||
terms: {
|
||||
[f.RULE_EXECUTION_STATUS]: [
|
||||
RuleExecutionStatus.running,
|
||||
RuleExecutionStatus['going to run'],
|
||||
RuleExecutionStatusEnum.running,
|
||||
RuleExecutionStatusEnum['going to run'],
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -223,9 +224,9 @@ const normalizeNumberOfExecutions = (
|
|||
return {
|
||||
total: Number(totalExecutions.value || 0),
|
||||
by_outcome: {
|
||||
succeeded: getStatusCount(RuleExecutionStatus.succeeded),
|
||||
warning: getStatusCount(RuleExecutionStatus['partial failure']),
|
||||
failed: getStatusCount(RuleExecutionStatus.failed),
|
||||
succeeded: getStatusCount(RuleExecutionStatusEnum.succeeded),
|
||||
warning: getStatusCount(RuleExecutionStatusEnum['partial failure']),
|
||||
failed: getStatusCount(RuleExecutionStatusEnum.failed),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,30 +5,31 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import { sum } from 'lodash';
|
||||
import type { Duration } from 'moment';
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
|
||||
import type {
|
||||
PublicRuleResultService,
|
||||
PublicRuleMonitoringService,
|
||||
PublicRuleResultService,
|
||||
} from '@kbn/alerting-plugin/server/types';
|
||||
import type {
|
||||
RuleExecutionMetrics,
|
||||
RuleExecutionSettings,
|
||||
RuleExecutionStatus,
|
||||
} from '../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import {
|
||||
LogLevel,
|
||||
logLevelFromExecutionStatus,
|
||||
LogLevelSetting,
|
||||
logLevelToNumber,
|
||||
RuleExecutionStatus,
|
||||
RuleExecutionStatusEnum,
|
||||
} from '../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
import { assertUnreachable } from '../../../../../../../common/utility_types';
|
||||
import { withSecuritySpan } from '../../../../../../utils/with_security_span';
|
||||
import { truncateValue } from '../../utils/normalization';
|
||||
import type { ExtMeta } from '../../utils/console_logging';
|
||||
import { truncateValue } from '../../utils/normalization';
|
||||
import { getCorrelationIds } from './correlation_ids';
|
||||
|
||||
import type { IEventLogWriter } from '../event_log/event_log_writer';
|
||||
|
@ -164,7 +165,7 @@ export const createRuleExecutionLogClientForExecutors = (
|
|||
const writeStatusChangeToRuleObject = async (args: NormalizedStatusChangeArgs): Promise<void> => {
|
||||
const { newStatus, message, metrics } = args;
|
||||
|
||||
if (newStatus === RuleExecutionStatus.running) {
|
||||
if (newStatus === RuleExecutionStatusEnum.running) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -186,9 +187,9 @@ export const createRuleExecutionLogClientForExecutors = (
|
|||
ruleMonitoringService.setLastRunMetricsGapDurationS(executionGapDurationS);
|
||||
}
|
||||
|
||||
if (newStatus === RuleExecutionStatus.failed) {
|
||||
if (newStatus === RuleExecutionStatusEnum.failed) {
|
||||
ruleResultService.addLastRunError(message);
|
||||
} else if (newStatus === RuleExecutionStatus['partial failure']) {
|
||||
} else if (newStatus === RuleExecutionStatusEnum['partial failure']) {
|
||||
ruleResultService.addLastRunWarning(message);
|
||||
}
|
||||
|
||||
|
@ -234,7 +235,7 @@ interface NormalizedStatusChangeArgs {
|
|||
}
|
||||
|
||||
const normalizeStatusChangeArgs = (args: StatusChangeArgs): NormalizedStatusChangeArgs => {
|
||||
if (args.newStatus === RuleExecutionStatus.running) {
|
||||
if (args.newStatus === RuleExecutionStatusEnum.running) {
|
||||
return {
|
||||
newStatus: args.newStatus,
|
||||
message: '',
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
|
||||
import type { Duration } from 'moment';
|
||||
import type { RuleExecutionStatus } from '../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type {
|
||||
RuleExecutionStatus,
|
||||
RuleExecutionStatusEnum,
|
||||
} from '../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
/**
|
||||
* Used from rule executors to log various information about the rule execution:
|
||||
|
@ -109,7 +112,7 @@ export interface RuleExecutionContext {
|
|||
}
|
||||
|
||||
export interface RunningStatusChangeArgs {
|
||||
newStatus: RuleExecutionStatus.running;
|
||||
newStatus: RuleExecutionStatusEnum['running'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,9 +9,9 @@ import type { ResolvedSanitizedRule, SanitizedRule } from '@kbn/alerting-plugin/
|
|||
|
||||
import type { RuleExecutionSummary } from '../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import {
|
||||
ruleLastRunOutcomeToExecutionStatus,
|
||||
RuleExecutionStatusEnum,
|
||||
ruleExecutionStatusToNumber,
|
||||
RuleExecutionStatus,
|
||||
ruleLastRunOutcomeToExecutionStatus,
|
||||
} from '../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
import type { RuleParams } from '../../../rule_schema';
|
||||
|
@ -39,8 +39,8 @@ export const createRuleExecutionSummary = (
|
|||
return {
|
||||
last_execution: {
|
||||
date: lastRunInternal.timestamp,
|
||||
status: RuleExecutionStatus.running,
|
||||
status_order: ruleExecutionStatusToNumber(RuleExecutionStatus.running),
|
||||
status: RuleExecutionStatusEnum.running,
|
||||
status_order: ruleExecutionStatusToNumber(RuleExecutionStatusEnum.running),
|
||||
message: '',
|
||||
metrics: {},
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*/
|
||||
|
||||
import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules';
|
||||
import { RuleExecutionStatus } from '../../../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
|
||||
import {
|
||||
formatExecutionEventResponse,
|
||||
|
@ -1328,30 +1328,30 @@ describe('mapRuleStatusToPlatformStatus', () => {
|
|||
expect(mapRuleExecutionStatusToPlatformStatus([])).toEqual([]);
|
||||
});
|
||||
|
||||
test('should correctly translate RuleExecutionStatus.failed to `failure` platform status', () => {
|
||||
expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatus.failed])).toEqual([
|
||||
test('should correctly translate RuleExecutionStatusEnum.failed to `failure` platform status', () => {
|
||||
expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatusEnum.failed])).toEqual([
|
||||
'failure',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should correctly translate RuleExecutionStatus.succeeded to `success` platform status', () => {
|
||||
expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatus.succeeded])).toEqual([
|
||||
test('should correctly translate RuleExecutionStatusEnum.succeeded to `success` platform status', () => {
|
||||
expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatusEnum.succeeded])).toEqual([
|
||||
'success',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should correctly translate RuleExecutionStatus.["going to run"] to empty array platform status', () => {
|
||||
expect(mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatus['going to run']])).toEqual(
|
||||
[]
|
||||
);
|
||||
test('should correctly translate RuleExecutionStatusEnum.["going to run"] to empty array platform status', () => {
|
||||
expect(
|
||||
mapRuleExecutionStatusToPlatformStatus([RuleExecutionStatusEnum['going to run']])
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test("should correctly translate multiple RuleExecutionStatus's to platform statuses", () => {
|
||||
expect(
|
||||
mapRuleExecutionStatusToPlatformStatus([
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatus.failed,
|
||||
RuleExecutionStatus['going to run'],
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
RuleExecutionStatusEnum.failed,
|
||||
RuleExecutionStatusEnum['going to run'],
|
||||
]).sort()
|
||||
).toEqual(['failure', 'success']);
|
||||
});
|
||||
|
@ -1362,13 +1362,15 @@ describe('mapPlatformStatusToRuleExecutionStatus', () => {
|
|||
expect(mapPlatformStatusToRuleExecutionStatus('')).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('should correctly translate `failure` platform status to `RuleExecutionStatus.failed`', () => {
|
||||
expect(mapPlatformStatusToRuleExecutionStatus('failure')).toEqual(RuleExecutionStatus.failed);
|
||||
test('should correctly translate `failure` platform status to `RuleExecutionStatusEnum.failed`', () => {
|
||||
expect(mapPlatformStatusToRuleExecutionStatus('failure')).toEqual(
|
||||
RuleExecutionStatusEnum.failed
|
||||
);
|
||||
});
|
||||
|
||||
test('should correctly translate `success` platform status to `RuleExecutionStatus.succeeded`', () => {
|
||||
test('should correctly translate `success` platform status to `RuleExecutionStatusEnum.succeeded`', () => {
|
||||
expect(mapPlatformStatusToRuleExecutionStatus('success')).toEqual(
|
||||
RuleExecutionStatus.succeeded
|
||||
RuleExecutionStatusEnum.succeeded
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,23 +5,24 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { flatMap, get } from 'lodash';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import type { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/server';
|
||||
import { BadRequestError } from '@kbn/securitysolution-es-utils';
|
||||
import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules';
|
||||
import type { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/server';
|
||||
import { flatMap, get } from 'lodash';
|
||||
|
||||
import type {
|
||||
RuleExecutionResult,
|
||||
GetRuleExecutionResultsResponse,
|
||||
RuleExecutionResult,
|
||||
RuleExecutionStatus,
|
||||
} from '../../../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatus } from '../../../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import * as f from '../../../../event_log/event_log_fields';
|
||||
import type {
|
||||
ExecutionEventAggregationOptions,
|
||||
ExecutionUuidAggResult,
|
||||
ExecutionUuidAggBucket,
|
||||
ExecutionUuidAggResult,
|
||||
} from './types';
|
||||
import * as f from '../../../../event_log/event_log_fields';
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/issues/125642 Move the fields from this file to `event_log_fields.ts`
|
||||
|
||||
|
@ -378,9 +379,9 @@ export const mapRuleExecutionStatusToPlatformStatus = (
|
|||
): string[] => {
|
||||
return flatMap(ruleStatuses, (rs) => {
|
||||
switch (rs) {
|
||||
case RuleExecutionStatus.failed:
|
||||
case RuleExecutionStatusEnum.failed:
|
||||
return 'failure';
|
||||
case RuleExecutionStatus.succeeded:
|
||||
case RuleExecutionStatusEnum.succeeded:
|
||||
return 'success';
|
||||
default:
|
||||
return [];
|
||||
|
@ -397,9 +398,9 @@ export const mapPlatformStatusToRuleExecutionStatus = (
|
|||
): RuleExecutionStatus | undefined => {
|
||||
switch (platformStatus) {
|
||||
case 'failure':
|
||||
return RuleExecutionStatus.failed;
|
||||
return RuleExecutionStatusEnum.failed;
|
||||
case 'success':
|
||||
return RuleExecutionStatus.succeeded;
|
||||
return RuleExecutionStatusEnum.succeeded;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
DETECTION_ENGINE_RULES_PREVIEW,
|
||||
} from '../../../../../../common/constants';
|
||||
import { validateCreateRuleProps } from '../../../../../../common/api/detection_engine/rule_management';
|
||||
import { RuleExecutionStatus } from '../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type {
|
||||
PreviewResponse,
|
||||
RulePreviewLogs,
|
||||
|
@ -287,11 +287,11 @@ export const previewRulesRoute = async (
|
|||
})) as { state: TState });
|
||||
|
||||
const errors = loggedStatusChanges
|
||||
.filter((item) => item.newStatus === RuleExecutionStatus.failed)
|
||||
.filter((item) => item.newStatus === RuleExecutionStatusEnum.failed)
|
||||
.map((item) => item.message ?? 'Unknown Error');
|
||||
|
||||
const warnings = loggedStatusChanges
|
||||
.filter((item) => item.newStatus === RuleExecutionStatus['partial failure'])
|
||||
.filter((item) => item.newStatus === RuleExecutionStatusEnum['partial failure'])
|
||||
.map((item) => item.message ?? 'Unknown Warning');
|
||||
|
||||
logs.push({
|
||||
|
|
|
@ -36,7 +36,7 @@ import { getNotificationResultsLink } from '../rule_actions_legacy';
|
|||
import { formatAlertForNotificationActions } from '../rule_actions_legacy/logic/notifications/schedule_notification_actions';
|
||||
import { createResultObject } from './utils';
|
||||
import { bulkCreateFactory, wrapHitsFactory, wrapSequencesFactory } from './factories';
|
||||
import { RuleExecutionStatus } from '../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { truncateList } from '../rule_monitoring';
|
||||
import aadFieldConversion from '../routes/index/signal_aad_mapping.json';
|
||||
import { extractReferences, injectReferences } from './saved_object_references';
|
||||
|
@ -179,7 +179,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
ruleExecutionLogger.debug(`Starting Security Rule execution (interval: ${interval})`);
|
||||
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus.running,
|
||||
newStatus: RuleExecutionStatusEnum.running,
|
||||
});
|
||||
|
||||
let result = createResultObject(state);
|
||||
|
@ -240,7 +240,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
: `Check for indices to search failed ${exc}`;
|
||||
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus.failed,
|
||||
newStatus: RuleExecutionStatusEnum.failed,
|
||||
message: errorMessage,
|
||||
});
|
||||
|
||||
|
@ -299,7 +299,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
}
|
||||
} catch (exc) {
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message: `Check privileges failed to execute ${exc}`,
|
||||
});
|
||||
wroteWarningStatus = true;
|
||||
|
@ -321,7 +321,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
const gapDuration = `${remainingGap.humanize()} (${remainingGap.asMilliseconds()}ms)`;
|
||||
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus.failed,
|
||||
newStatus: RuleExecutionStatusEnum.failed,
|
||||
message: `${gapDuration} were not queried between this rule execution and the last execution, so signals may have been missed. Consider increasing your look behind time or adding more Kibana instances`,
|
||||
metrics: { executionGap: remainingGap },
|
||||
});
|
||||
|
@ -459,7 +459,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
|
||||
if (result.warningMessages.length) {
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message: truncateList(result.warningMessages).join(),
|
||||
metrics: {
|
||||
searchDurations: result.searchAfterTimes,
|
||||
|
@ -485,7 +485,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
|
||||
if (!hasError && !wroteWarningStatus && !result.warning) {
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus.succeeded,
|
||||
newStatus: RuleExecutionStatusEnum.succeeded,
|
||||
message: 'Rule execution completed successfully',
|
||||
metrics: {
|
||||
searchDurations: result.searchAfterTimes,
|
||||
|
@ -495,7 +495,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
});
|
||||
} else if (wroteWarningStatus && !hasError && !result.warning) {
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message: warningMessage,
|
||||
metrics: {
|
||||
searchDurations: result.searchAfterTimes,
|
||||
|
@ -506,7 +506,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
}
|
||||
} else {
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus.failed,
|
||||
newStatus: RuleExecutionStatusEnum.failed,
|
||||
message: `Bulk Indexing of alerts failed: ${truncateList(result.errors).join()}`,
|
||||
metrics: {
|
||||
searchDurations: result.searchAfterTimes,
|
||||
|
@ -519,7 +519,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
|
|||
const errorMessage = error.message ?? '(no error message given)';
|
||||
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus.failed,
|
||||
newStatus: RuleExecutionStatusEnum.failed,
|
||||
message: `An error occurred during rule execution: message: "${errorMessage}"`,
|
||||
metrics: {
|
||||
searchDurations: result.searchAfterTimes,
|
||||
|
|
|
@ -14,7 +14,7 @@ import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks
|
|||
import { alertsMock } from '@kbn/alerting-plugin/server/mocks';
|
||||
import { listMock } from '@kbn/lists-plugin/server/mocks';
|
||||
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock';
|
||||
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
|
||||
|
||||
|
@ -655,7 +655,7 @@ describe('utils', () => {
|
|||
expect(wroteWarningStatus).toBeTruthy();
|
||||
expect(foundNoIndices).toBeFalsy();
|
||||
expect(ruleExecutionLogger.logStatusChange).toHaveBeenCalledWith({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message:
|
||||
'The following indices are missing the timestamp override field "event.ingested": ["myfakeindex-1","myfakeindex-2"]',
|
||||
});
|
||||
|
@ -699,7 +699,7 @@ describe('utils', () => {
|
|||
expect(wroteWarningStatus).toBeTruthy();
|
||||
expect(foundNoIndices).toBeFalsy();
|
||||
expect(ruleExecutionLogger.logStatusChange).toHaveBeenCalledWith({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message:
|
||||
'The following indices are missing the timestamp field "@timestamp": ["myfakeindex-1","myfakeindex-2"]',
|
||||
});
|
||||
|
@ -732,7 +732,7 @@ describe('utils', () => {
|
|||
expect(wroteWarningStatus).toBeTruthy();
|
||||
expect(foundNoIndices).toBeTruthy();
|
||||
expect(ruleExecutionLogger.logStatusChange).toHaveBeenCalledWith({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message:
|
||||
'This rule is attempting to query data from Elasticsearch indices listed in the "Index patterns" section of the rule definition, however no index matching: ["logs-endpoint.alerts-*"] was found. This warning will continue to appear until a matching index is created or this rule is disabled. If you have recently enrolled agents enabled with Endpoint Security through Fleet, this warning should stop once an alert is sent from an agent.',
|
||||
});
|
||||
|
@ -766,7 +766,7 @@ describe('utils', () => {
|
|||
expect(wroteWarningStatus).toBeTruthy();
|
||||
expect(foundNoIndices).toBeTruthy();
|
||||
expect(ruleExecutionLogger.logStatusChange).toHaveBeenCalledWith({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message:
|
||||
'This rule is attempting to query data from Elasticsearch indices listed in the "Index patterns" section of the rule definition, however no index matching: ["logs-endpoint.alerts-*"] was found. This warning will continue to appear until a matching index is created or this rule is disabled.',
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ import { parseDuration } from '@kbn/alerting-plugin/server';
|
|||
import type { ExceptionListClient, ListClient, ListPluginSetup } from '@kbn/lists-plugin/server';
|
||||
import type { TimestampOverride } from '../../../../../common/api/detection_engine/model/rule_schema';
|
||||
import type { Privilege } from '../../../../../common/api/detection_engine';
|
||||
import { RuleExecutionStatus } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '../../../../../common/api/detection_engine/rule_monitoring';
|
||||
import type {
|
||||
BulkResponseErrorAggregation,
|
||||
SignalHit,
|
||||
|
@ -94,7 +94,7 @@ export const hasReadIndexPrivileges = async (args: {
|
|||
const indexesString = JSON.stringify(indexesWithNoReadPrivileges);
|
||||
warningStatusMessage = `This rule may not have the required read privileges to the following index patterns: ${indexesString}`;
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message: warningStatusMessage,
|
||||
});
|
||||
return { wroteWarningMessage: true, warningStatusMessage };
|
||||
|
@ -129,7 +129,7 @@ export const hasTimestampFields = async (args: {
|
|||
}`;
|
||||
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message: errorString.trimEnd(),
|
||||
});
|
||||
|
||||
|
@ -157,7 +157,7 @@ export const hasTimestampFields = async (args: {
|
|||
)}`;
|
||||
|
||||
await ruleExecutionLogger.logStatusChange({
|
||||
newStatus: RuleExecutionStatus['partial failure'],
|
||||
newStatus: RuleExecutionStatusEnum['partial failure'],
|
||||
message: errorString,
|
||||
});
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import type {
|
|||
RouteValidationResultFactory,
|
||||
RouteValidationError,
|
||||
} from '@kbn/core/server';
|
||||
import type { TypeOf, ZodType } from 'zod';
|
||||
import { stringifyZodError } from '@kbn/securitysolution-es-utils';
|
||||
import type { GenericIntersectionC } from '../runtime_types';
|
||||
import { excess } from '../runtime_types';
|
||||
|
||||
|
@ -65,3 +67,14 @@ export const buildRouteValidationWithExcess =
|
|||
(validatedInput: A) => validationResult.ok(validatedInput)
|
||||
)
|
||||
);
|
||||
|
||||
export const buildRouteValidationWithZod =
|
||||
<T extends ZodType, A = TypeOf<T>>(schema: T): RouteValidationFunction<A> =>
|
||||
(inputValue: unknown, validationResult: RouteValidationResultFactory) => {
|
||||
const decoded = schema.safeParse(inputValue);
|
||||
if (decoded.success) {
|
||||
return validationResult.ok(decoded.data);
|
||||
} else {
|
||||
return validationResult.badRequest(stringifyZodError(decoded.error));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { orderBy } from 'lodash';
|
||||
import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import {
|
||||
EqlRuleCreateProps,
|
||||
QueryRuleCreateProps,
|
||||
|
@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatus['partial failure']
|
||||
RuleExecutionStatusEnum['partial failure']
|
||||
);
|
||||
expect(signalsOpen.hits.hits.length).eql(0);
|
||||
});
|
||||
|
@ -340,7 +340,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatus['partial failure']
|
||||
RuleExecutionStatusEnum['partial failure']
|
||||
);
|
||||
expect(signalsOpen.hits.hits.length).eql(0);
|
||||
});
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
QueryRuleCreateProps,
|
||||
AlertSuppressionMissingFieldsStrategy,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import { Ancestor } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/types';
|
||||
import {
|
||||
ALERT_ANCESTORS,
|
||||
|
@ -797,7 +797,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
undefined,
|
||||
afterTimestamp
|
||||
);
|
||||
|
@ -873,7 +873,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
undefined,
|
||||
afterTimestamp
|
||||
);
|
||||
|
|
|
@ -34,7 +34,7 @@ import {
|
|||
ALERT_ORIGINAL_EVENT_MODULE,
|
||||
ALERT_ORIGINAL_TIME,
|
||||
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import { RuleExecutionStatusEnum } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import {
|
||||
previewRule,
|
||||
|
@ -173,7 +173,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
100
|
||||
);
|
||||
expect(alerts.hits.hits.length).equal(88);
|
||||
|
@ -354,7 +354,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
100
|
||||
);
|
||||
expect(alerts.hits.hits.length).equal(88);
|
||||
|
@ -557,7 +557,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRuleTerm,
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
100
|
||||
);
|
||||
const alertsMatch = await getOpenSignals(
|
||||
|
@ -565,7 +565,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
es,
|
||||
createdRuleMatch,
|
||||
RuleExecutionStatus.succeeded,
|
||||
RuleExecutionStatusEnum.succeeded,
|
||||
100
|
||||
);
|
||||
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
import type SuperTest from 'supertest';
|
||||
import type { Client } from '@elastic/elasticsearch';
|
||||
import type { ToolingLog } from '@kbn/tooling-log';
|
||||
import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import {
|
||||
RuleExecutionStatus,
|
||||
RuleExecutionStatusEnum,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import type { RuleResponse } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
|
||||
import { waitForRuleStatus } from './wait_for_rule_status';
|
||||
|
@ -20,7 +23,7 @@ export const getOpenSignals = async (
|
|||
log: ToolingLog,
|
||||
es: Client,
|
||||
rule: RuleResponse,
|
||||
status: RuleExecutionStatus = RuleExecutionStatus.succeeded,
|
||||
status: RuleExecutionStatus = RuleExecutionStatusEnum.succeeded,
|
||||
size?: number,
|
||||
afterDate?: Date
|
||||
) => {
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
import type { ToolingLog } from '@kbn/tooling-log';
|
||||
import type SuperTest from 'supertest';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import {
|
||||
RuleExecutionStatus,
|
||||
RuleExecutionStatusEnum,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import { waitFor } from './wait_for';
|
||||
import { routeWithNamespace } from './route_with_namespace';
|
||||
|
||||
|
@ -70,10 +73,10 @@ export const waitForRuleStatus = async (
|
|||
};
|
||||
|
||||
export const waitForRuleSuccess = (params: WaitForRuleStatusParams): Promise<void> =>
|
||||
waitForRuleStatus(RuleExecutionStatus.succeeded, params);
|
||||
waitForRuleStatus(RuleExecutionStatusEnum.succeeded, params);
|
||||
|
||||
export const waitForRulePartialFailure = (params: WaitForRuleStatusParams): Promise<void> =>
|
||||
waitForRuleStatus(RuleExecutionStatus['partial failure'], params);
|
||||
waitForRuleStatus(RuleExecutionStatusEnum['partial failure'], params);
|
||||
|
||||
export const waitForRuleFailure = (params: WaitForRuleStatusParams): Promise<void> =>
|
||||
waitForRuleStatus(RuleExecutionStatus.failed, params);
|
||||
waitForRuleStatus(RuleExecutionStatusEnum.failed, params);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue