[Security Solution] Added OpenAPI schema definitions for rules management (#162641)

**Related to: https://github.com/elastic/security-team/issues/7131**

## Summary

This PR introduces OpenAPI declarations for our public Rules Management
API endpoints.

⚠️ Please note, the added declarations are not final and are not
complete. They are missing some common declarations, such as the `Rule`
schema, the majority of rule types, the `RuleResponse` schema, etc.
Furthermore, these declarations haven't been tested for correctness, as
it is difficult to validate if the declared schemas match with our
current endpoint implementation without code generation. This
verification step will be necessary once we have schema generation in
place.

That said, the added OpenAPI declarations should be sufficient to
unblock progress on the following tickets:
- https://github.com/elastic/security-team/issues/7129
- https://github.com/elastic/security-team/issues/7134
This commit is contained in:
Dmitrii Shevchenko 2023-08-04 17:13:20 +02:00 committed by GitHub
parent 8f1041c81a
commit fda84111c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 2535 additions and 710 deletions

View file

@ -11,310 +11,125 @@ import { TimeDuration } from '.';
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
describe('TimeDuration', () => {
describe('with allowedDurations', () => {
test('it should validate a correctly formed TimeDuration with an allowed duration of 1s', () => {
const payload = '1s';
const decoded = TimeDuration({
allowedDurations: [
[1, 's'],
[2, 'h'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
test('it should validate a correctly formed TimeDuration with time unit of seconds', () => {
const payload = '1s';
const decoded = TimeDuration({ allowedUnits: ['s'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
test('it should validate a correctly formed TimeDuration with an allowed duration of 7d', () => {
const payload = '1s';
const decoded = TimeDuration({
allowedDurations: [
[1, 's'],
[2, 'h'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
test('it should NOT validate a time duration if the allowed durations does not include it', () => {
const payload = '24h';
const decoded = TimeDuration({
allowedDurations: [
[1, 's'],
[2, 'h'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "24h" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a an allowed duration with a negative number', () => {
const payload = '10s';
const decoded = TimeDuration({
allowedDurations: [
[1, 's'],
[-7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "[[1,"s"],[-7,"d"]]" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate an allowed duration with a fractional number', () => {
const payload = '1.5s';
const decoded = TimeDuration({
allowedDurations: [
[1, 's'],
[-7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "1.5s" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a an allowed duration with a duration of 0', () => {
const payload = '10s';
const decoded = TimeDuration({
allowedDurations: [
[0, 's'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "[[0,"s"],[7,"d"]]" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a TimeDuration with an invalid time unit', () => {
const payload = '10000000days';
const decoded = TimeDuration({
allowedDurations: [
[1, 'h'],
[1, 'd'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "10000000days" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a TimeDuration with a time interval with incorrect format', () => {
const payload = '100ff0000w';
const decoded = TimeDuration({
allowedDurations: [
[1, 'h'],
[1, 'd'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "100ff0000w" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate an empty string', () => {
const payload = '';
const decoded = TimeDuration({
allowedDurations: [
[1, 'h'],
[1, 'd'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate an number', () => {
const payload = 100;
const decoded = TimeDuration({
allowedDurations: [
[1, 'h'],
[1, 'd'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "100" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate an TimeDuration with a valid time unit but unsafe integer', () => {
const payload = `${Math.pow(2, 53)}h`;
const decoded = TimeDuration({
allowedDurations: [
[1, 'h'],
[1, 'd'],
[7, 'd'],
],
}).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
`Invalid value "${Math.pow(2, 53)}h" supplied to "TimeDuration"`,
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
describe('with allowedUnits', () => {
test('it should validate a correctly formed TimeDuration with time unit of seconds', () => {
const payload = '1s';
const decoded = TimeDuration({ allowedUnits: ['s'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
test('it should validate a correctly formed TimeDuration with time unit of minutes', () => {
const payload = '100m';
const decoded = TimeDuration({ allowedUnits: ['s', 'm'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should validate a correctly formed TimeDuration with time unit of minutes', () => {
const payload = '100m';
const decoded = TimeDuration({ allowedUnits: ['s', 'm'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
test('it should validate a correctly formed TimeDuration with time unit of hours', () => {
const payload = '10000000h';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should validate a correctly formed TimeDuration with time unit of hours', () => {
const payload = '10000000h';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
test('it should validate a correctly formed TimeDuration with time unit of days', () => {
const payload = '7d';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should validate a correctly formed TimeDuration with time unit of days', () => {
const payload = '7d';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});
test('it should NOT validate a correctly formed TimeDuration with time unit of seconds if it is not an allowed unit', () => {
const payload = '30s';
const decoded = TimeDuration({ allowedUnits: ['m', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate a correctly formed TimeDuration with time unit of seconds if it is not an allowed unit', () => {
const payload = '30s';
const decoded = TimeDuration({ allowedUnits: ['m', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "30s" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "30s" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a negative TimeDuration', () => {
const payload = '-10s';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate a negative TimeDuration', () => {
const payload = '-10s';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "-10s" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "-10s" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a fractional number', () => {
const payload = '1.5s';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate a fractional number', () => {
const payload = '1.5s';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "1.5s" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "1.5s" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a TimeDuration with an invalid time unit', () => {
const payload = '10000000days';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate a TimeDuration with an invalid time unit', () => {
const payload = '10000000days';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "10000000days" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "10000000days" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate a TimeDuration with a time interval with incorrect format', () => {
const payload = '100ff0000w';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate a TimeDuration with a time interval with incorrect format', () => {
const payload = '100ff0000w';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "100ff0000w" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "100ff0000w" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate an empty string', () => {
const payload = '';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate an empty string', () => {
const payload = '';
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "TimeDuration"']);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate an number', () => {
const payload = 100;
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate an number', () => {
const payload = 100;
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "100" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "100" supplied to "TimeDuration"',
]);
expect(message.schema).toEqual({});
});
test('it should NOT validate an TimeDuration with a valid time unit but unsafe integer', () => {
const payload = `${Math.pow(2, 53)}h`;
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
test('it should NOT validate an TimeDuration with a valid time unit but unsafe integer', () => {
const payload = `${Math.pow(2, 53)}h`;
const decoded = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).decode(payload);
const message = foldLeftRight(decoded);
expect(getPaths(left(message.errors))).toEqual([
`Invalid value "${Math.pow(2, 53)}h" supplied to "TimeDuration"`,
]);
expect(message.schema).toEqual({});
});
expect(getPaths(left(message.errors))).toEqual([
`Invalid value "${Math.pow(2, 53)}h" supplied to "TimeDuration"`,
]);
expect(message.schema).toEqual({});
});
});

View file

@ -16,20 +16,14 @@ import { Either } from 'fp-ts/lib/Either';
*/
type TimeUnits = 's' | 'm' | 'h' | 'd' | 'w' | 'y';
interface TimeDurationWithAllowedDurations {
allowedDurations: Array<[number, TimeUnits]>;
allowedUnits?: never;
}
interface TimeDurationWithAllowedUnits {
allowedUnits: TimeUnits[];
allowedDurations?: never;
}
type TimeDurationType = TimeDurationWithAllowedDurations | TimeDurationWithAllowedUnits;
interface TimeDurationType {
allowedUnits: TimeUnits[];
}
const isTimeSafe = (time: number) => time >= 1 && Number.isSafeInteger(time);
export const TimeDuration = ({ allowedUnits, allowedDurations }: TimeDurationType) => {
export const TimeDuration = ({ allowedUnits }: TimeDurationType) => {
return new t.Type<string, string, unknown>(
'TimeDuration',
t.string.is,
@ -42,17 +36,7 @@ export const TimeDuration = ({ allowedUnits, allowedDurations }: TimeDurationTyp
if (!isTimeSafe(time)) {
return t.failure(input, context);
}
if (allowedDurations) {
for (const [allowedTime, allowedUnit] of allowedDurations) {
if (!isTimeSafe(allowedTime)) {
return t.failure(allowedDurations, context);
}
if (allowedTime === time && allowedUnit === unit) {
return t.success(input);
}
}
return t.failure(input, context);
} else if (allowedUnits.includes(unit as TimeUnits)) {
if (allowedUnits.includes(unit as TimeUnits)) {
return t.success(input);
} else {
return t.failure(input, context);

View file

@ -0,0 +1,33 @@
openapi: 3.0.0
info:
title: Error Schema
version: 'not applicable'
paths: {}
components:
schemas:
ErrorSchema:
type: object
required:
- error
properties:
id:
type: string
rule_id:
$ref: './rule_schema/common_attributes.schema.yaml#/components/schemas/RuleSignatureId'
list_id:
type: string
minLength: 1
item_id:
type: string
minLength: 1
error:
type: object
required:
- status_code
- message
properties:
status_code:
type: integer
minimum: 400
message:
type: string

View file

@ -5,13 +5,12 @@
* 2.0.
*/
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 { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { getErrorSchemaMock } from './error_schema.mocks';
import { getErrorSchemaMock } from './error_schema.mock';
describe('error_schema', () => {
test('it should validate an error with a UUID given for id', () => {

View file

@ -0,0 +1,243 @@
openapi: 3.0.0
info:
title: Common Rule Attributes
version: 'not applicable'
paths: {}
components:
schemas:
UUID:
type: string
format: uuid
description: A universally unique identifier
RuleObjectId:
type: string
RuleSignatureId:
type: string
description: Could be any string, not necessarily a UUID
RuleName:
type: string
minLength: 1
RuleDescription:
type: string
minLength: 1
RuleVersion:
type: string
format: version
IsRuleImmutable:
type: boolean
IsRuleEnabled:
type: boolean
RuleTagArray:
type: array
items:
type: string
RuleMetadata:
type: object
additionalProperties: true
RuleLicense:
type: string
RuleAuthorArray:
type: array
items:
type: string
RuleFalsePositiveArray:
type: array
items:
type: string
RuleReferenceArray:
type: array
items:
type: string
InvestigationGuide:
type: string
SetupGuide:
type: string
BuildingBlockType:
type: string
AlertsIndex:
type: string
AlertsIndexNamespace:
type: string
MaxSignals:
type: integer
minimum: 1
Subtechnique:
type: object
properties:
id:
type: string
description: Subtechnique ID
name:
type: string
description: Subtechnique name
reference:
type: string
description: Subtechnique reference
required:
- id
- name
- reference
Technique:
type: object
properties:
id:
type: string
description: Technique ID
name:
type: string
description: Technique name
reference:
type: string
description: Technique reference
subtechnique:
type: array
items:
$ref: '#/components/schemas/Subtechnique'
description: Array containing more specific information on the attack technique
required:
- id
- name
- reference
Threat:
type: object
properties:
framework:
type: string
description: Relevant attack framework
tactic:
type: object
properties:
id:
type: string
description: Tactic ID
name:
type: string
description: Tactic name
reference:
type: string
description: Tactic reference
required:
- id
- name
- reference
technique:
type: array
items:
$ref: '#/components/schemas/Technique'
description: Array containing information on the attack techniques (optional)
required:
- framework
- tactic
ThreatArray:
type: array
items:
$ref: '#/components/schemas/Threat' # Assuming a schema named 'Threat' is defined in the components section.
IndexPatternArray:
type: array
items:
type: string
DataViewId:
type: string
RuleQuery:
type: string
RuleFilterArray:
type: array
items:
type: object
additionalProperties: true
RuleNameOverride:
type: string
TimestampOverride:
type: string
TimestampOverrideFallbackDisabled:
type: boolean
RequiredField:
type: object
properties:
name:
type: string
minLength: 1
type:
type: string
minLength: 1
ecs:
type: boolean
RequiredFieldArray:
type: array
items:
$ref: '#/components/schemas/RequiredField'
TimelineTemplateId:
type: string
TimelineTemplateTitle:
type: string
SavedObjectResolveOutcome:
type: string
enum:
- exactMatch
- aliasMatch
- conflict
SavedObjectResolveAliasTargetId:
type: string
SavedObjectResolveAliasPurpose:
type: string
enum:
- savedObjectConversion
- savedObjectImport
RelatedIntegration:
type: object
properties:
package:
type: string
minLength: 1
version:
type: string
minLength: 1
integration:
type: string
minLength: 1
required:
- package
- version
RelatedIntegrationArray:
type: array
items:
$ref: '#/components/schemas/RelatedIntegration'

View file

@ -0,0 +1,259 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as t from 'io-ts';
import { listArray } from '@kbn/securitysolution-io-ts-list-types';
import { NonEmptyString, version, UUID } from '@kbn/securitysolution-io-ts-types';
import { max_signals, threat } from '@kbn/securitysolution-io-ts-alerting-types';
export type RuleObjectId = t.TypeOf<typeof RuleObjectId>;
export const RuleObjectId = UUID;
/**
* NOTE: Never make this a strict uuid, we allow the rule_id to be any string at the moment
* in case we encounter 3rd party rule systems which might be using auto incrementing numbers
* or other different things.
*/
export type RuleSignatureId = t.TypeOf<typeof RuleSignatureId>;
export const RuleSignatureId = t.string; // should be non-empty string?
export type RuleName = t.TypeOf<typeof RuleName>;
export const RuleName = NonEmptyString;
export type RuleDescription = t.TypeOf<typeof RuleDescription>;
export const RuleDescription = NonEmptyString;
export type RuleVersion = t.TypeOf<typeof RuleVersion>;
export const RuleVersion = version;
export type IsRuleImmutable = t.TypeOf<typeof IsRuleImmutable>;
export const IsRuleImmutable = t.boolean;
export type IsRuleEnabled = t.TypeOf<typeof IsRuleEnabled>;
export const IsRuleEnabled = t.boolean;
export type RuleTagArray = t.TypeOf<typeof RuleTagArray>;
export const RuleTagArray = t.array(t.string); // should be non-empty strings?
/**
* Note that this is a non-exact io-ts type as we allow extra meta information
* to be added to the meta object
*/
export type RuleMetadata = t.TypeOf<typeof RuleMetadata>;
export const RuleMetadata = t.object; // should be a more specific type?
export type RuleLicense = t.TypeOf<typeof RuleLicense>;
export const RuleLicense = t.string;
export type RuleAuthorArray = t.TypeOf<typeof RuleAuthorArray>;
export const RuleAuthorArray = t.array(t.string); // should be non-empty strings?
export type RuleFalsePositiveArray = t.TypeOf<typeof RuleFalsePositiveArray>;
export const RuleFalsePositiveArray = t.array(t.string); // should be non-empty strings?
export type RuleReferenceArray = t.TypeOf<typeof RuleReferenceArray>;
export const RuleReferenceArray = t.array(t.string); // should be non-empty strings?
export type InvestigationGuide = t.TypeOf<typeof InvestigationGuide>;
export const InvestigationGuide = t.string;
/**
* Any instructions for the user for setting up their environment in order to start receiving
* source events for a given rule.
*
* It's a multiline text. Markdown is supported.
*/
export type SetupGuide = t.TypeOf<typeof SetupGuide>;
export const SetupGuide = t.string;
export type BuildingBlockType = t.TypeOf<typeof BuildingBlockType>;
export const BuildingBlockType = t.string;
export type AlertsIndex = t.TypeOf<typeof AlertsIndex>;
export const AlertsIndex = t.string;
export type AlertsIndexNamespace = t.TypeOf<typeof AlertsIndexNamespace>;
export const AlertsIndexNamespace = t.string;
export type ExceptionListArray = t.TypeOf<typeof ExceptionListArray>;
export const ExceptionListArray = listArray;
export type MaxSignals = t.TypeOf<typeof MaxSignals>;
export const MaxSignals = max_signals;
export type ThreatArray = t.TypeOf<typeof ThreatArray>;
export const ThreatArray = t.array(threat);
export type IndexPatternArray = t.TypeOf<typeof IndexPatternArray>;
export const IndexPatternArray = t.array(t.string);
export type DataViewId = t.TypeOf<typeof DataViewId>;
export const DataViewId = t.string;
export type RuleQuery = t.TypeOf<typeof RuleQuery>;
export const RuleQuery = t.string;
/**
* TODO: Right now the filters is an "unknown", when it could more than likely
* become the actual ESFilter as a type.
*/
export type RuleFilterArray = t.TypeOf<typeof RuleFilterArray>; // Filters are not easily type-able yet
export const RuleFilterArray = t.array(t.unknown); // Filters are not easily type-able yet
export type RuleNameOverride = t.TypeOf<typeof RuleNameOverride>;
export const RuleNameOverride = t.string; // should be non-empty string?
export type TimestampOverride = t.TypeOf<typeof TimestampOverride>;
export const TimestampOverride = t.string; // should be non-empty string?
export type TimestampOverrideFallbackDisabled = t.TypeOf<typeof TimestampOverrideFallbackDisabled>;
export const TimestampOverrideFallbackDisabled = t.boolean;
/**
* Almost all types of Security rules check source event documents for a match to some kind of
* query or filter. If a document has certain field with certain values, then it's a match and
* the rule will generate an alert.
*
* Required field is an event field that must be present in the source indices of a given rule.
*
* @example
* const standardEcsField: RequiredField = {
* name: 'event.action',
* type: 'keyword',
* ecs: true,
* };
*
* @example
* const nonEcsField: RequiredField = {
* name: 'winlog.event_data.AttributeLDAPDisplayName',
* type: 'keyword',
* ecs: false,
* };
*/
export type RequiredField = t.TypeOf<typeof RequiredField>;
export const RequiredField = t.exact(
t.type({
name: NonEmptyString,
type: NonEmptyString,
ecs: t.boolean,
})
);
/**
* Array of event fields that must be present in the source indices of a given rule.
*
* @example
* const x: RequiredFieldArray = [
* {
* name: 'event.action',
* type: 'keyword',
* ecs: true,
* },
* {
* name: 'event.code',
* type: 'keyword',
* ecs: true,
* },
* {
* name: 'winlog.event_data.AttributeLDAPDisplayName',
* type: 'keyword',
* ecs: false,
* },
* ];
*/
export type RequiredFieldArray = t.TypeOf<typeof RequiredFieldArray>;
export const RequiredFieldArray = t.array(RequiredField);
export type TimelineTemplateId = t.TypeOf<typeof TimelineTemplateId>;
export const TimelineTemplateId = t.string; // should be non-empty string?
export type TimelineTemplateTitle = t.TypeOf<typeof TimelineTemplateTitle>;
export const TimelineTemplateTitle = t.string; // should be non-empty string?
/**
* Outcome is a property of the saved object resolve api
* will tell us info about the rule after 8.0 migrations
*/
export type SavedObjectResolveOutcome = t.TypeOf<typeof SavedObjectResolveOutcome>;
export const SavedObjectResolveOutcome = t.union([
t.literal('exactMatch'),
t.literal('aliasMatch'),
t.literal('conflict'),
]);
export type SavedObjectResolveAliasTargetId = t.TypeOf<typeof SavedObjectResolveAliasTargetId>;
export const SavedObjectResolveAliasTargetId = t.string;
export type SavedObjectResolveAliasPurpose = t.TypeOf<typeof SavedObjectResolveAliasPurpose>;
export const SavedObjectResolveAliasPurpose = t.union([
t.literal('savedObjectConversion'),
t.literal('savedObjectImport'),
]);
/**
* Related integration is a potential dependency of a rule. It's assumed that if the user installs
* one of the related integrations of a rule, the rule might start to work properly because it will
* have source events (generated by this integration) potentially matching the rule's query.
*
* NOTE: Proper work is not guaranteed, because a related integration, if installed, can be
* configured differently or generate data that is not necessarily relevant for this rule.
*
* Related integration is a combination of a Fleet package and (optionally) one of the
* package's "integrations" that this package contains. It is represented by 3 properties:
*
* - `package`: name of the package (required, unique id)
* - `version`: version of the package (required, semver-compatible)
* - `integration`: name of the integration of this package (optional, id within the package)
*
* There are Fleet packages like `windows` that contain only one integration; in this case,
* `integration` should be unspecified. There are also packages like `aws` and `azure` that contain
* several integrations; in this case, `integration` should be specified.
*
* @example
* const x: RelatedIntegration = {
* package: 'windows',
* version: '1.5.x',
* };
*
* @example
* const x: RelatedIntegration = {
* package: 'azure',
* version: '~1.1.6',
* integration: 'activitylogs',
* };
*/
export type RelatedIntegration = t.TypeOf<typeof RelatedIntegration>;
export const RelatedIntegration = t.exact(
t.intersection([
t.type({
package: NonEmptyString,
version: NonEmptyString,
}),
t.partial({
integration: NonEmptyString,
}),
])
);
/**
* Array of related integrations.
*
* @example
* const x: RelatedIntegrationArray = [
* {
* package: 'windows',
* version: '1.5.x',
* },
* {
* package: 'azure',
* version: '~1.1.6',
* integration: 'activitylogs',
* },
* ];
*/
export type RelatedIntegrationArray = t.TypeOf<typeof RelatedIntegrationArray>;
export const RelatedIntegrationArray = t.array(RelatedIntegration);

View file

@ -1,17 +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 type RuleNameOverride = t.TypeOf<typeof RuleNameOverride>;
export const RuleNameOverride = t.string; // should be non-empty string?
export type TimestampOverride = t.TypeOf<typeof TimestampOverride>;
export const TimestampOverride = t.string; // should be non-empty string?
export type TimestampOverrideFallbackDisabled = t.TypeOf<typeof TimestampOverrideFallbackDisabled>;
export const TimestampOverrideFallbackDisabled = t.boolean;

View file

@ -1,105 +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 { listArray } from '@kbn/securitysolution-io-ts-list-types';
import { NonEmptyString, version, UUID } from '@kbn/securitysolution-io-ts-types';
import { max_signals, threat } from '@kbn/securitysolution-io-ts-alerting-types';
export type RuleObjectId = t.TypeOf<typeof RuleObjectId>;
export const RuleObjectId = UUID;
/**
* NOTE: Never make this a strict uuid, we allow the rule_id to be any string at the moment
* in case we encounter 3rd party rule systems which might be using auto incrementing numbers
* or other different things.
*/
export type RuleSignatureId = t.TypeOf<typeof RuleSignatureId>;
export const RuleSignatureId = t.string; // should be non-empty string?
export type RuleName = t.TypeOf<typeof RuleName>;
export const RuleName = NonEmptyString;
export type RuleDescription = t.TypeOf<typeof RuleDescription>;
export const RuleDescription = NonEmptyString;
export type RuleVersion = t.TypeOf<typeof RuleVersion>;
export const RuleVersion = version;
export type IsRuleImmutable = t.TypeOf<typeof IsRuleImmutable>;
export const IsRuleImmutable = t.boolean;
export type IsRuleEnabled = t.TypeOf<typeof IsRuleEnabled>;
export const IsRuleEnabled = t.boolean;
export type RuleTagArray = t.TypeOf<typeof RuleTagArray>;
export const RuleTagArray = t.array(t.string); // should be non-empty strings?
/**
* Note that this is a non-exact io-ts type as we allow extra meta information
* to be added to the meta object
*/
export type RuleMetadata = t.TypeOf<typeof RuleMetadata>;
export const RuleMetadata = t.object; // should be a more specific type?
export type RuleLicense = t.TypeOf<typeof RuleLicense>;
export const RuleLicense = t.string;
export type RuleAuthorArray = t.TypeOf<typeof RuleAuthorArray>;
export const RuleAuthorArray = t.array(t.string); // should be non-empty strings?
export type RuleFalsePositiveArray = t.TypeOf<typeof RuleFalsePositiveArray>;
export const RuleFalsePositiveArray = t.array(t.string); // should be non-empty strings?
export type RuleReferenceArray = t.TypeOf<typeof RuleReferenceArray>;
export const RuleReferenceArray = t.array(t.string); // should be non-empty strings?
export type InvestigationGuide = t.TypeOf<typeof InvestigationGuide>;
export const InvestigationGuide = t.string;
/**
* Any instructions for the user for setting up their environment in order to start receiving
* source events for a given rule.
*
* It's a multiline text. Markdown is supported.
*/
export type SetupGuide = t.TypeOf<typeof SetupGuide>;
export const SetupGuide = t.string;
export type BuildingBlockType = t.TypeOf<typeof BuildingBlockType>;
export const BuildingBlockType = t.string;
export type AlertsIndex = t.TypeOf<typeof AlertsIndex>;
export const AlertsIndex = t.string;
export type AlertsIndexNamespace = t.TypeOf<typeof AlertsIndexNamespace>;
export const AlertsIndexNamespace = t.string;
export type ExceptionListArray = t.TypeOf<typeof ExceptionListArray>;
export const ExceptionListArray = listArray;
export type MaxSignals = t.TypeOf<typeof MaxSignals>;
export const MaxSignals = max_signals;
export type ThreatArray = t.TypeOf<typeof ThreatArray>;
export const ThreatArray = t.array(threat);
export type IndexPatternArray = t.TypeOf<typeof IndexPatternArray>;
export const IndexPatternArray = t.array(t.string);
export type DataViewId = t.TypeOf<typeof DataViewId>;
export const DataViewId = t.string;
export type RuleQuery = t.TypeOf<typeof RuleQuery>;
export const RuleQuery = t.string;
/**
* TODO: Right now the filters is an "unknown", when it could more than likely
* become the actual ESFilter as a type.
*/
export type RuleFilterArray = t.TypeOf<typeof RuleFilterArray>; // Filters are not easily type-able yet
export const RuleFilterArray = t.array(t.unknown); // Filters are not easily type-able yet

View file

@ -1,73 +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 { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
/**
* Related integration is a potential dependency of a rule. It's assumed that if the user installs
* one of the related integrations of a rule, the rule might start to work properly because it will
* have source events (generated by this integration) potentially matching the rule's query.
*
* NOTE: Proper work is not guaranteed, because a related integration, if installed, can be
* configured differently or generate data that is not necessarily relevant for this rule.
*
* Related integration is a combination of a Fleet package and (optionally) one of the
* package's "integrations" that this package contains. It is represented by 3 properties:
*
* - `package`: name of the package (required, unique id)
* - `version`: version of the package (required, semver-compatible)
* - `integration`: name of the integration of this package (optional, id within the package)
*
* There are Fleet packages like `windows` that contain only one integration; in this case,
* `integration` should be unspecified. There are also packages like `aws` and `azure` that contain
* several integrations; in this case, `integration` should be specified.
*
* @example
* const x: RelatedIntegration = {
* package: 'windows',
* version: '1.5.x',
* };
*
* @example
* const x: RelatedIntegration = {
* package: 'azure',
* version: '~1.1.6',
* integration: 'activitylogs',
* };
*/
export type RelatedIntegration = t.TypeOf<typeof RelatedIntegration>;
export const RelatedIntegration = t.exact(
t.intersection([
t.type({
package: NonEmptyString,
version: NonEmptyString,
}),
t.partial({
integration: NonEmptyString,
}),
])
);
/**
* Array of related integrations.
*
* @example
* const x: RelatedIntegrationArray = [
* {
* package: 'windows',
* version: '1.5.x',
* },
* {
* package: 'azure',
* version: '~1.1.6',
* integration: 'activitylogs',
* },
* ];
*/
export type RelatedIntegrationArray = t.TypeOf<typeof RelatedIntegrationArray>;
export const RelatedIntegrationArray = t.array(RelatedIntegration);

View file

@ -1,64 +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 { NonEmptyString } from '@kbn/securitysolution-io-ts-types';
/**
* Almost all types of Security rules check source event documents for a match to some kind of
* query or filter. If a document has certain field with certain values, then it's a match and
* the rule will generate an alert.
*
* Required field is an event field that must be present in the source indices of a given rule.
*
* @example
* const standardEcsField: RequiredField = {
* name: 'event.action',
* type: 'keyword',
* ecs: true,
* };
*
* @example
* const nonEcsField: RequiredField = {
* name: 'winlog.event_data.AttributeLDAPDisplayName',
* type: 'keyword',
* ecs: false,
* };
*/
export type RequiredField = t.TypeOf<typeof RequiredField>;
export const RequiredField = t.exact(
t.type({
name: NonEmptyString,
type: NonEmptyString,
ecs: t.boolean,
})
);
/**
* Array of event fields that must be present in the source indices of a given rule.
*
* @example
* const x: RequiredFieldArray = [
* {
* name: 'event.action',
* type: 'keyword',
* ecs: true,
* },
* {
* name: 'event.code',
* type: 'keyword',
* ecs: true,
* },
* {
* name: 'winlog.event_data.AttributeLDAPDisplayName',
* type: 'keyword',
* ecs: false,
* },
* ];
*/
export type RequiredFieldArray = t.TypeOf<typeof RequiredFieldArray>;
export const RequiredFieldArray = t.array(RequiredField);

View file

@ -1,28 +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';
/**
* Outcome is a property of the saved object resolve api
* will tell us info about the rule after 8.0 migrations
*/
export type SavedObjectResolveOutcome = t.TypeOf<typeof SavedObjectResolveOutcome>;
export const SavedObjectResolveOutcome = t.union([
t.literal('exactMatch'),
t.literal('aliasMatch'),
t.literal('conflict'),
]);
export type SavedObjectResolveAliasTargetId = t.TypeOf<typeof SavedObjectResolveAliasTargetId>;
export const SavedObjectResolveAliasTargetId = t.string;
export type SavedObjectResolveAliasPurpose = t.TypeOf<typeof SavedObjectResolveAliasPurpose>;
export const SavedObjectResolveAliasPurpose = t.union([
t.literal('savedObjectConversion'),
t.literal('savedObjectImport'),
]);

View file

@ -1,14 +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 type TimelineTemplateId = t.TypeOf<typeof TimelineTemplateId>;
export const TimelineTemplateId = t.string; // should be non-empty string?
export type TimelineTemplateTitle = t.TypeOf<typeof TimelineTemplateTitle>;
export const TimelineTemplateTitle = t.string; // should be non-empty string?

View file

@ -5,12 +5,7 @@
* 2.0.
*/
export * from './common_attributes/field_overrides';
export * from './common_attributes/misc_attributes';
export * from './common_attributes/related_integrations';
export * from './common_attributes/required_fields';
export * from './common_attributes/saved_objects';
export * from './common_attributes/timeline_template';
export * from './common_attributes';
export * from './specific_attributes/eql_attributes';
export * from './specific_attributes/new_terms_attributes';

View file

@ -0,0 +1,849 @@
openapi: 3.0.0
info:
title: Security Rule Schema
version: 'not applicable'
paths: {}
components:
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.
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
Action:
type: object
properties:
action_type_id:
type: string
description: The action type used for sending notifications.
group:
type: string
description: Optionally groups actions by use cases. Use `default` for alert notifications.
id:
type: string
description: The connector ID.
params:
type: object
description: Object containing the allowed connector fields, which varies according to the connector type.
uuid:
type: string
alerts_filter:
type: object
description: TODO implement the schema type
frequency:
type: object
description: TODO implement the schema type
required:
- action_type_id
- group
- id
- params
AlertSuppression:
type: object
properties:
group_by:
type: array
items:
type: string
minItems: 1
maxItems: 3
duration:
type: object
properties:
value:
type: integer
minimum: 1
unit:
type: string
enum:
- s
- m
- h
required:
- value
- unit
missing_fields_strategy:
type: string
enum:
- doNotSuppress
- suppress
required:
- group_by
BaseRule:
type: object
properties:
name:
type: string
description: Rule name
description:
type: string
description: Rule description
risk_score:
type: integer
description: Risk score (0 to 100)
minimum: 0
maximum: 100
severity:
type: string
enum: [low, medium, high, critical]
description: Severity of the rule
rule_name_override:
type: string
description: Sets the source field for the alert's signal.rule.name value
timestamp_override:
type: string
description: Sets the time field used to query indices (optional)
timeline_id:
type: string
description: Timeline template ID
timeline_title:
type: string
description: Timeline template title
outcome:
type: string
enum:
- exactMatch
- aliasMatch
- conflict
alias_target_id:
type: string
description: TODO
alias_purpose:
type: string
enum:
- savedObjectConversion
- savedObjectImport
description: TODO
license:
type: string
description: The rules license.
note:
type: string
description: Notes to help investigate alerts produced by the rule.
building_block_type:
type: string
description: 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.
output_index:
type: string
description: (deprecated) Has no effect.
namespace:
type: string
description: Has no effect.
meta:
type: object
description: Stores rule metadata.
throttle:
type: string
description: Defines the interval on which a rule's actions are executed.
version:
type: integer
minimum: 1
default: 1
description: The rules version number. Defaults to 1.
tags:
type: array
items:
type: string
default: []
description: String array containing words and phrases to help categorize, filter, and search rules. Defaults to an empty array.
enabled:
type: boolean
default: true
description: Determines whether the rule is enabled. Defaults to true.
risk_score_mapping:
type: array
items:
type: object
properties:
field:
type: string
operator:
type: string
enum:
- equals
value:
type: string
risk_score:
type: integer
minimum: 0
maximum: 100
required:
- field
- value
- operator
description: Overrides generated alerts' risk_score with a value from the source event
default: []
severity_mapping:
type: array
items:
type: object
properties:
field:
type: string
operator:
type: string
enum:
- equals
severity:
type: string
enum:
- low
- medium
- high
- critical
value:
type: string
required:
- field
- operator
- severity
- value
description: Overrides generated alerts' severity with values from the source event
default: []
interval:
type: string
description: Frequency of rule execution, using a date math range. For example, "1h" means the rule runs every hour. Defaults to 5m (5 minutes).
default: 5m
from:
type: string
description: 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).
default: now-6m
to:
type: string
description: TODO
default: now
actions:
type: array
items:
$ref: '#/components/schemas/Action'
default: []
exceptions_list:
type: array
items:
type: object
properties:
id:
type: string
description: ID of the exception container
minLength: 1
list_id:
type: string
description: List ID of the exception container
minLength: 1
type:
type: string
description: The exception type
enum:
- detection
- rule_default
- endpoint
- endpoint_trusted_apps
- endpoint_events
- endpoint_host_isolation_exceptions
- endpoint_blocklists
namespace_type:
type: string
description: Determines the exceptions validity in rule's Kibana space
enum:
- agnostic
- single
required:
- id
- list_id
- type
- namespace_type
default: []
author:
type: array
items:
type: string
default: []
false_positives:
type: array
items:
type: string
default: []
references:
type: array
items:
type: string
default: []
max_signals:
type: integer
minimum: 1
default: 100
threat:
type: array
items:
type: object
properties:
framework:
type: string
description: Relevant attack framework
tactic:
type: object
properties:
id:
type: string
name:
type: string
reference:
type: string
required:
- id
- name
- reference
technique:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
reference:
type: string
subtechnique:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
reference:
type: string
required:
- id
- name
- reference
required:
- id
- name
- reference
required:
- framework
- tactic
required:
- id
- name
- description
- risk_score
- severity
QueryRule:
allOf:
- $ref: '#/components/schemas/BaseRule'
- type: object
properties:
type:
type: string
enum: [query]
description: Rule type
index:
type: array
items:
type: string
data_view_id:
type: string
filters:
type: array
items: {} # unknown
saved_id:
type: string
response_actions:
type: array
items:
type: object
description: TODO
alert_suppression:
$ref: '#/components/schemas/AlertSuppression'
query:
type: string
description: Query to execute
default: ''
language:
type: string
enum:
- kuery
- lucene
default: kuery
description: Query language to use.
required:
- type
SavedQueryRule:
allOf:
- $ref: '#/components/schemas/BaseRule'
- type: object
properties:
type:
type: string
enum: [saved_query]
description: Rule type
index:
type: array
items:
type: string
data_view_id:
type: string
filters:
type: array
items: {} # unknown
saved_id:
type: string
response_actions:
type: array
items:
type: object
description: TODO
alert_suppression:
$ref: '#/components/schemas/AlertSuppression'
query:
type: string
description: Query to execute
language:
type: string
enum:
- kuery
- lucene
default: kuery
description: Query language to use.
required:
- type
- saved_id
ThresholdRule:
allOf:
- $ref: '#/components/schemas/BaseRule'
- type: object
properties:
type:
type: string
enum: [threshold]
description: Rule type
query:
type: string
threshold:
type: object
properties:
field:
oneOf:
- type: string
- type: array
items:
type: string
description: Field to aggregate on
value:
type: integer
minimum: 1
description: Threshold value
cardinality:
type: array
items:
type: object
properties:
field:
type: string
value:
type: integer
minimum: 0
index:
type: array
items:
type: string
data_view_id:
type: string
filters:
type: array
items: {} # unknown
saved_id:
type: string
language:
type: string
enum:
- kuery
- lucene
default: kuery
description: Query language to use.
required:
- type
- query
- threshold
ThreatMatchRule:
allOf:
- $ref: '#/components/schemas/BaseRule'
- type: object
properties:
type:
type: string
enum: [threat_match]
description: Rule type
query:
type: string
threat_query:
type: string
description: Query to execute
threat_mapping:
type: array
minItems: 1
items:
type: object
properties:
entries:
type: array
items:
type: object
properties:
field:
type: string
minLength: 1
type:
type: string
enum:
- mapping
value:
type: string
minLength: 1
threat_index:
type: array
items:
type: string
index:
type: array
items:
type: string
data_view_id:
type: string
filters:
type: array
items: {} # unknown
saved_id:
type: string
threat_filters:
type: array
items:
description: Query and filter context array used to filter documents from the Elasticsearch index containing the threat values
threat_indicator_path:
type: string
description: Defines the path to the threat indicator in the indicator documents (optional)
threat_language:
type: string
enum:
- kuery
- lucene
description: Query language to use.
concurrent_searches:
type: integer
minimum: 1
items_per_search:
type: integer
minimum: 1
language:
type: string
enum:
- kuery
- lucene
default: kuery
description: Query language to use.
required:
- type
- query
- threat_query
- threat_mapping
- threat_index
MlRule:
allOf:
- $ref: '#/components/schemas/BaseRule'
- type: object
properties:
type:
type: string
enum: [machine_learning]
description: Rule type
anomaly_threshold:
type: integer
minimum: 0
description: Anomaly threshold
machine_learning_job_id:
oneOf:
- type: string
- type: array
items:
type: string
minItems: 1
description: Machine learning job ID
required:
- type
- machine_learning_job_id
- anomaly_threshold
EqlRule:
allOf:
- $ref: '#/components/schemas/BaseRule'
- type: object
properties:
type:
type: string
enum: [eql]
description: Rule type
language:
type: string
enum:
- eql
query:
type: string
description: EQL query to execute
index:
type: array
items:
type: string
data_view_id:
type: string
filters:
type: array
items: {} # unknown
event_category_field:
type: string
description: Contains the event classification
tiebreaker_field:
type: string
description: Sets a secondary field for sorting events
timestamp_field:
type: string
description: Contains the event timestamp used for sorting a sequence of events
required:
- type
- language
- query
NewTermsRule:
allOf:
- $ref: '#/components/schemas/BaseRule'
- type: object
properties:
type:
type: string
enum: [new_terms]
description: Rule type
query:
type: string
new_terms_fields:
type: array
items:
type: string
minItems: 1
maxItems: 3
history_window_size:
type: string
minLength: 1
index:
type: array
items:
type: string
data_view_id:
type: string
filters:
type: array
items: {} # unknown
language:
type: string
enum:
- kuery
- lucene
default: kuery
required:
- type
- query
- new_terms_fields
- history_window_start
Rule:
oneOf:
- $ref: '#/components/schemas/QueryRule'
- $ref: '#/components/schemas/SavedQueryRule'
- $ref: '#/components/schemas/ThresholdRule'
- $ref: '#/components/schemas/ThreatMatchRule'
- $ref: '#/components/schemas/MlRule'
- $ref: '#/components/schemas/EqlRule'
- $ref: '#/components/schemas/NewTermsRule'
Throttle:
type: string
description: Defines the maximum interval in which a rule's actions are executed.
enum:
- rule
- 1h
- 1d
- 7d
Subtechnique:
type: object
properties:
id:
type: string
description: Subtechnique ID
name:
type: string
description: Subtechnique name
reference:
type: string
description: Subtechnique reference
required:
- id
- name
- reference
Technique:
type: object
properties:
id:
type: string
description: Technique ID
name:
type: string
description: Technique name
reference:
type: string
description: Technique reference
subtechnique:
type: array
items:
$ref: '#/components/schemas/Subtechnique'
description: Array containing more specific information on the attack technique
required:
- id
- name
- reference
Threat:
type: object
properties:
framework:
type: string
description: Relevant attack framework
tactic:
type: object
properties:
id:
type: string
description: Tactic ID
name:
type: string
description: Tactic name
reference:
type: string
description: Tactic reference
required:
- id
- name
- reference
technique:
type: array
items:
$ref: '#/components/schemas/Technique'
description: Array containing information on the attack techniques (optional)
required:
- framework
- tactic
RuleResponse:
type: object
RuleCreateProps:
type: object
RuleUpdateProps:
type: object
RulePatchProps:
type: object

View file

@ -50,6 +50,8 @@ import {
IsRuleEnabled,
IsRuleImmutable,
MaxSignals,
RelatedIntegrationArray,
RequiredFieldArray,
RuleAuthorArray,
RuleDescription,
RuleFalsePositiveArray,
@ -57,28 +59,23 @@ import {
RuleLicense,
RuleMetadata,
RuleName,
RuleNameOverride,
RuleObjectId,
RuleQuery,
RuleReferenceArray,
RuleSignatureId,
RuleTagArray,
RuleVersion,
SetupGuide,
ThreatArray,
} from './common_attributes/misc_attributes';
import {
RuleNameOverride,
TimestampOverride,
TimestampOverrideFallbackDisabled,
} from './common_attributes/field_overrides';
import {
SavedObjectResolveAliasPurpose,
SavedObjectResolveAliasTargetId,
SavedObjectResolveOutcome,
} from './common_attributes/saved_objects';
import { RelatedIntegrationArray } from './common_attributes/related_integrations';
import { RequiredFieldArray } from './common_attributes/required_fields';
import { TimelineTemplateId, TimelineTemplateTitle } from './common_attributes/timeline_template';
SetupGuide,
ThreatArray,
TimelineTemplateId,
TimelineTemplateTitle,
TimestampOverride,
TimestampOverrideFallbackDisabled,
} from './common_attributes';
import {
EventCategoryOverride,
TiebreakerField,
@ -535,7 +532,7 @@ export const TypeSpecificResponse = t.union([
// Final combined schemas
export type RuleCreateProps = t.TypeOf<typeof RuleCreateProps>;
export const RuleCreateProps = t.intersection([SharedCreateProps, TypeSpecificCreateProps]);
export const RuleCreateProps = t.intersection([TypeSpecificCreateProps, SharedCreateProps]);
export type RuleUpdateProps = t.TypeOf<typeof RuleUpdateProps>;
export const RuleUpdateProps = t.intersection([TypeSpecificCreateProps, SharedUpdateProps]);

View file

@ -0,0 +1,22 @@
openapi: 3.0.0
info:
title: Warning Schema
version: 'not applicable'
paths: {}
components:
schemas:
WarningSchema:
type: object
properties:
type:
type: string
message:
type: string
actionPath:
type: string
buttonLabel:
type: string
required:
- type
- message
- actionPath

View file

@ -0,0 +1,61 @@
openapi: 3.0.0
info:
title: Prebuilt Rules Status API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/prepackaged/_status:
get:
operationId: GetPrebuiltRulesStatus
x-codegen-enabled: false
summary: Get the status of Elastic prebuilt rules
tags:
- Prebuilt Rules API
responses:
200:
description: Indicates a successful call
content:
application/json:
schema:
$ref: '#/components/schemas/GetPrebuiltRulesStatusResponse'
components:
schemas:
GetPrebuiltRulesStatusResponse:
type: object
properties:
rules_custom_installed:
type: integer
description: The total number of custom rules
minimum: 0
rules_installed:
type: integer
description: The total number of installed prebuilt rules
minimum: 0
rules_not_installed:
type: integer
description: The total number of available prebuilt rules that are not installed
minimum: 0
rules_not_updated:
type: integer
description: The total number of outdated prebuilt rules
minimum: 0
timelines_installed:
type: integer
description: The total number of installed prebuilt timelines
minimum: 0
timelines_not_installed:
type: integer
description: The total number of available prebuilt timelines that are not installed
minimum: 0
timelines_not_updated:
type: integer
description: The total number of outdated prebuilt timelines
minimum: 0
required:
- rules_custom_installed
- rules_installed
- rules_not_installed
- rules_not_updated
- timelines_installed
- timelines_not_installed
- timelines_not_updated

View file

@ -0,0 +1,46 @@
openapi: 3.0.0
info:
title: Install Prebuilt Rules API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/prepackaged:
put:
operationId: InstallPrebuiltRules
x-codegen-enabled: false
summary: Installs all Elastic prebuilt rules and timelines
tags:
- Prebuilt Rules API
responses:
200:
description: Indicates a successful call
content:
application/json:
schema:
$ref: '#/components/schemas/InstallPrebuiltRulesResponse'
components:
schemas:
InstallPrebuiltRulesResponse:
type: object
properties:
rules_installed:
type: integer
description: The number of rules installed
minimum: 0
rules_updated:
type: integer
description: The number of rules updated
minimum: 0
timelines_installed:
type: integer
description: The number of timelines installed
minimum: 0
timelines_updated:
type: integer
description: The number of timelines updated
minimum: 0
required:
- rules_installed
- rules_updated
- timelines_installed
- timelines_updated

View file

@ -0,0 +1,461 @@
openapi: 3.0.0
info:
title: Bulk Actions API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/_bulk_action:
post:
operationId: PerformBulkAction
x-codegen-enabled: false
summary: Applies a bulk action to multiple rules
description: The bulk action is applied to all rules that match the filter or to the list of rules by their IDs.
tags:
- Bulk API
parameters:
- name: dry_run
in: query
description: Enables dry run mode for the request call.
required: false
schema:
type: boolean
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PerformBulkActionRequest'
responses:
200:
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/BulkEditActionResponse'
components:
schemas:
BulkEditSkipReason:
type: string
enum:
- RULE_NOT_MODIFIED
BulkActionSkipResult:
type: object
properties:
id:
type: string
name:
type: string
skip_reason:
$ref: '#/components/schemas/BulkEditSkipReason'
required:
- id
- skip_reason
RuleDetailsInError:
type: object
properties:
id:
type: string
name:
type: string
required:
- id
BulkActionsDryRunErrCode:
type: string
NormalizedRuleError:
type: object
properties:
message:
type: string
status_code:
type: integer
err_code:
$ref: '#/components/schemas/BulkActionsDryRunErrCode'
rules:
type: array
items:
$ref: '#/components/schemas/RuleDetailsInError'
required:
- message
- status_code
- rules
BulkEditActionResults:
type: object
properties:
updated:
type: array
items:
$ref: '../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'
created:
type: array
items:
$ref: '../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'
deleted:
type: array
items:
$ref: '../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'
skipped:
type: array
items:
$ref: '#/components/schemas/BulkActionSkipResult'
required:
- updated
- created
- deleted
- skipped
BulkEditActionSummary:
type: object
properties:
failed:
type: integer
skipped:
type: integer
succeeded:
type: integer
total:
type: integer
required:
- failed
- skipped
- succeeded
- total
BulkEditActionSuccessResponse:
type: object
properties:
success:
type: boolean
rules_count:
type: integer
attributes:
type: object
properties:
results:
$ref: '#/components/schemas/BulkEditActionResults'
summary:
$ref: '#/components/schemas/BulkEditActionSummary'
required:
- results
- summary
required:
- success
- rules_count
- attributes
BulkEditActionErrorResponse:
type: object
properties:
status_code:
type: integer
message:
type: string
attributes:
type: object
properties:
results:
$ref: '#/components/schemas/BulkEditActionResults'
summary:
$ref: '#/components/schemas/BulkEditActionSummary'
errors:
type: array
items:
$ref: '#/components/schemas/NormalizedRuleError'
required:
- results
- summary
required:
- status_code
- message
- attributes
BulkEditActionResponse:
oneOf:
- $ref: '#/components/schemas/BulkEditActionSuccessResponse'
- $ref: '#/components/schemas/BulkEditActionErrorResponse'
BulkActionBase:
oneOf:
- type: object
properties:
query:
type: string
description: Query to filter rules
required:
- query
additionalProperties: false
- type: object
properties:
ids:
type: array
description: Array of rule IDs
minItems: 1
items:
type: string
additionalProperties: false
BulkDeleteRules:
allOf:
- $ref: '#/components/schemas/BulkActionBase'
- type: object
properties:
action:
type: string
enum: [delete]
required:
- action
BulkDisableRules:
allOf:
- $ref: '#/components/schemas/BulkActionBase'
- type: object
properties:
action:
type: string
enum: [disable]
required:
- action
BulkEnableRules:
allOf:
- $ref: '#/components/schemas/BulkActionBase'
- type: object
properties:
action:
type: string
enum: [enable]
required:
- action
BulkExportRules:
allOf:
- $ref: '#/components/schemas/BulkActionBase'
- type: object
properties:
action:
type: string
enum: [export]
required:
- action
BulkDuplicateRules:
allOf:
- $ref: '#/components/schemas/BulkActionBase'
- type: object
properties:
action:
type: string
enum: [duplicate]
duplicate:
type: object
properties:
include_exceptions:
type: boolean
description: Whether to copy exceptions from the original rule
include_expired_exceptions:
type: boolean
description: Whether to copy expired exceptions from the original rule
required:
- action
RuleActionSummary:
type: boolean
description: Action summary indicates whether we will send a summary notification about all the generate alerts or notification per individual alert
RuleActionNotifyWhen:
type: string
description: "The condition for throttling the notification: 'onActionGroupChange', 'onActiveAlert', or 'onThrottleInterval'"
enum:
- onActionGroupChange
- onActiveAlert
- onThrottleInterval
RuleActionThrottle:
type: string
description: "The condition for throttling the notification: 'rule', 'no_actions', or time duration"
RuleActionFrequency:
type: object
properties:
summary:
$ref: '#/components/schemas/RuleActionSummary'
notifyWhen:
$ref: '#/components/schemas/RuleActionNotifyWhen'
throttle:
$ref: '#/components/schemas/RuleActionThrottle'
nullable: true
BulkActionType:
type: string
enum:
- enable
- disable
- export
- delete
- duplicate
- edit
BulkActionEditType:
type: string
enum:
- add_tags
- delete_tags
- set_tags
- add_index_patterns
- delete_index_patterns
- set_index_patterns
- set_timeline
- add_rule_actions
- set_rule_actions
- set_schedule
BulkActionEditPayloadRuleActions:
type: object
properties:
type:
type: string
enum: [add_rule_actions, set_rule_actions]
value:
type: object
properties:
throttle:
$ref: '#/components/schemas/RuleActionThrottle'
actions:
type: array
items:
type: object
properties:
group:
type: string
description: Action group
id:
type: string
description: Action ID
params:
type: object
description: Action parameters
frequency:
$ref: '#/components/schemas/RuleActionFrequency'
description: Action frequency
required:
- group
- id
- params
required:
- actions
required:
- type
- value
BulkActionEditPayloadSchedule:
type: object
properties:
type:
type: string
enum: [set_schedule]
value:
type: object
properties:
interval:
type: string
description: Interval in which the rule is executed
lookback:
type: string
description: Lookback time for the rule
required:
- interval
- lookback
BulkActionEditPayloadIndexPatterns:
type: object
properties:
type:
type: string
enum:
- add_index_patterns
- delete_index_patterns
- set_index_patterns
value:
$ref: '../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/IndexPatternArray'
overwrite_data_views:
type: boolean
required:
- type
- value
BulkActionEditPayloadTags:
type: object
properties:
type:
type: string
enum:
- add_tags
- delete_tags
- set_tags
value:
$ref: '../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleTagArray'
required:
- type
- value
BulkActionEditPayloadTimeline:
type: object
properties:
type:
type: string
enum:
- set_timeline
value:
type: object
properties:
timeline_id:
$ref: '../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/TimelineTemplateId'
timeline_title:
$ref: '../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/TimelineTemplateTitle'
required:
- timeline_id
- timeline_title
required:
- type
- value
BulkActionEditPayload:
anyOf:
- $ref: '#/components/schemas/BulkActionEditPayloadTags'
- $ref: '#/components/schemas/BulkActionEditPayloadIndexPatterns'
- $ref: '#/components/schemas/BulkActionEditPayloadTimeline'
- $ref: '#/components/schemas/BulkActionEditPayloadRuleActions'
- $ref: '#/components/schemas/BulkActionEditPayloadSchedule'
BulkEditRules:
allOf:
- $ref: '#/components/schemas/BulkActionBase'
- type: object
properties:
action:
type: string
x-type: literal
enum: [edit]
edit:
type: array
description: Array of objects containing the edit operations
items:
$ref: '#/components/schemas/BulkActionEditPayload'
required:
- action
- rule
PerformBulkActionRequest:
oneOf:
- $ref: '#/components/schemas/BulkDeleteRules'
- $ref: '#/components/schemas/BulkDisableRules'
- $ref: '#/components/schemas/BulkEnableRules'
- $ref: '#/components/schemas/BulkExportRules'
- $ref: '#/components/schemas/BulkDuplicateRules'
- $ref: '#/components/schemas/BulkEditRules'

View file

@ -53,13 +53,9 @@ export enum BulkActionEditType {
export type ThrottleForBulkActions = t.TypeOf<typeof ThrottleForBulkActions>;
export const ThrottleForBulkActions = t.union([
t.literal('rule'),
TimeDuration({
allowedDurations: [
[1, 'h'],
[1, 'd'],
[7, 'd'],
],
}),
t.literal('1h'),
t.literal('1d'),
t.literal('7d'),
]);
type BulkActionEditPayloadTags = t.TypeOf<typeof BulkActionEditPayloadTags>;

View file

@ -0,0 +1,29 @@
openapi: 3.0.0
info:
title: Bulk Create API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/_bulk_create:
post:
operationId: CreateRulesBulk
x-codegen-enabled: false
deprecated: true
description: Creates new detection rules in bulk.
tags:
- Bulk API
requestBody:
description: A JSON array of rules, where each rule contains the required fields.
required: true
content:
application/json:
schema:
type: array
items:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleCreateProps'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../response_schema.schema.yaml#/components/schemas/BulkCrudRulesResponse'

View file

@ -0,0 +1,34 @@
openapi: 3.0.0
info:
title: Bulk Delete API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/_bulk_delete:
delete:
operationId: DeleteRulesBulk
x-codegen-enabled: false
deprecated: true
description: Deletes multiple rules.
tags:
- Bulk API
requestBody:
description: A JSON array of `id` or `rule_id` fields of the rules you want to delete.
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
$ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleObjectId'
rule_id:
$ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleSignatureId'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../response_schema.schema.yaml#/components/schemas/BulkCrudRulesResponse'

View file

@ -0,0 +1,29 @@
openapi: 3.0.0
info:
title: Bulk Patch API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/_bulk_update:
patch:
operationId: PatchRulesBulk
x-codegen-enabled: false
deprecated: true
description: Updates multiple rules using the `PATCH` method.
tags:
- Bulk API
requestBody:
description: A JSON array of rules, where each rule contains the required fields.
required: true
content:
application/json:
schema:
type: array
items:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RulePatchProps'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../response_schema.schema.yaml#/components/schemas/BulkCrudRulesResponse'

View file

@ -6,10 +6,10 @@
*/
import * as t from 'io-ts';
import { PatchRuleRequestBody } from '../../crud/patch_rule/patch_rule_route';
import { RulePatchProps } from '../../../model';
/**
* Request body parameters of the API route.
*/
export type BulkPatchRulesRequestBody = t.TypeOf<typeof BulkPatchRulesRequestBody>;
export const BulkPatchRulesRequestBody = t.array(PatchRuleRequestBody);
export const BulkPatchRulesRequestBody = t.array(RulePatchProps);

View file

@ -0,0 +1,29 @@
openapi: 3.0.0
info:
title: Bulk Update API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/_bulk_update:
put:
operationId: UpdateRulesBulk
x-codegen-enabled: false
deprecated: true
description: Updates multiple rules using the `PUT` method.
tags:
- Bulk API
requestBody:
description: A JSON array where each element includes the `id` or `rule_id` field of the rule you want to update and the fields you want to modify.
required: true
content:
application/json:
schema:
type: array
items:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleUpdateProps'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../response_schema.schema.yaml#/components/schemas/BulkCrudRulesResponse'

View file

@ -0,0 +1,13 @@
openapi: 3.0.0
info:
title: Bulk Response Schema
version: 8.9.0
paths: {}
components:
schemas:
BulkCrudRulesResponse:
type: array
items:
oneOf:
- $ref: '../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'
- $ref: '../../model/error_schema.schema.yaml#/components/schemas/ErrorSchema'

View file

@ -11,7 +11,7 @@ import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts
import type { RuleResponse, ErrorSchema } from '../../model';
import { getRulesSchemaMock } from '../../model/rule_schema/mocks';
import { getErrorSchemaMock } from '../../model/error_schema.mocks';
import { getErrorSchemaMock } from '../../model/error_schema.mock';
import { BulkCrudRulesResponse } from './response_schema';

View file

@ -0,0 +1,25 @@
openapi: 3.0.0
info:
title: Create Rule API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules:
post:
operationId: CreateRule
x-codegen-enabled: false
description: Create a single detection rule
tags:
- Rules API
requestBody:
required: true
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleCreateProps'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'

View file

@ -0,0 +1,32 @@
openapi: 3.0.0
info:
title: Delete Rule API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules:
delete:
operationId: DeleteRule
x-codegen-enabled: false
description: Deletes a single rule using the `rule_id` or `id` field.
tags:
- Rules API
parameters:
- name: id
in: query
required: false
description: The rule's `id` value.
schema:
$ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleSignatureId'
- name: rule_id
in: query
required: false
description: The rule's `rule_id` value.
schema:
$ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleObjectId'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'

View file

@ -7,6 +7,10 @@
import type * as t from 'io-ts';
import { RuleResponse } from '../../../model';
import { QueryRuleByIds } from '../../model/query_rule_by_ids';
export const DeleteRuleRequestQuery = QueryRuleByIds;
export type DeleteRuleRequestQuery = t.TypeOf<typeof DeleteRuleRequestQuery>;
export const DeleteRuleResponse = RuleResponse;
export type DeleteRuleResponse = t.TypeOf<typeof DeleteRuleResponse>;

View file

@ -5,7 +5,8 @@
* 2.0.
*/
import type { PatchRuleRequestBody, ThresholdPatchRuleRequestBody } from './patch_rule_route';
import type { ThresholdRulePatchProps } from '../../../model';
import type { PatchRuleRequestBody } from './patch_rule_route';
export const getPatchRulesSchemaMock = (): PatchRuleRequestBody => ({
description: 'some description',
@ -18,7 +19,7 @@ export const getPatchRulesSchemaMock = (): PatchRuleRequestBody => ({
rule_id: 'rule-1',
});
export const getPatchThresholdRulesSchemaMock = (): ThresholdPatchRuleRequestBody => ({
export const getPatchThresholdRulesSchemaMock = (): ThresholdRulePatchProps => ({
description: 'some description',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',

View file

@ -0,0 +1,25 @@
openapi: 3.0.0
info:
title: Patch Rule API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules:
patch:
operationId: PatchRule
x-codegen-enabled: false
description: Patch a single rule
tags:
- Rules API
requestBody:
required: true
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RulePatchProps'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'

View file

@ -6,7 +6,7 @@
*/
import type * as t from 'io-ts';
import { RulePatchProps, RuleResponse, ThresholdRulePatchProps } from '../../../model';
import { RulePatchProps, RuleResponse } from '../../../model';
/**
* Request body parameters of the API route.
@ -15,8 +15,5 @@ import { RulePatchProps, RuleResponse, ThresholdRulePatchProps } from '../../../
export type PatchRuleRequestBody = RulePatchProps;
export const PatchRuleRequestBody = RulePatchProps;
export type ThresholdPatchRuleRequestBody = ThresholdRulePatchProps;
export const ThresholdPatchRuleRequestBody = ThresholdRulePatchProps;
export const PatchRuleResponse = RuleResponse;
export type PatchRuleResponse = t.TypeOf<typeof PatchRuleResponse>;

View file

@ -5,7 +5,8 @@
* 2.0.
*/
import type { PatchRuleRequestBody, ThresholdPatchRuleRequestBody } from './patch_rule_route';
import type { ThresholdRulePatchProps } from '../../../model';
import type { PatchRuleRequestBody } from './patch_rule_route';
import { getPatchRulesSchemaMock, getPatchThresholdRulesSchemaMock } from './patch_rule_route.mock';
import { validatePatchRuleRequestBody } from './request_schema_validation';
@ -72,7 +73,7 @@ describe('Patch rule request schema, additional validation', () => {
});
test('threshold.value is required and has to be bigger than 0 when type is threshold and validates with it', () => {
const schema: ThresholdPatchRuleRequestBody = {
const schema: ThresholdRulePatchProps = {
...getPatchThresholdRulesSchemaMock(),
threshold: {
field: '',
@ -84,7 +85,7 @@ describe('Patch rule request schema, additional validation', () => {
});
test('threshold.field should contain 3 items or less', () => {
const schema: ThresholdPatchRuleRequestBody = {
const schema: ThresholdRulePatchProps = {
...getPatchThresholdRulesSchemaMock(),
threshold: {
field: ['field-1', 'field-2', 'field-3', 'field-4'],
@ -96,7 +97,7 @@ describe('Patch rule request schema, additional validation', () => {
});
test('threshold.cardinality[0].field should not be in threshold.field', () => {
const schema: ThresholdPatchRuleRequestBody = {
const schema: ThresholdRulePatchProps = {
...getPatchThresholdRulesSchemaMock(),
threshold: {
field: ['field-1', 'field-2', 'field-3'],

View file

@ -0,0 +1,32 @@
openapi: 3.0.0
info:
title: Read Rule API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules:
get:
operationId: ReadRule
x-codegen-enabled: false
description: Read a single rule
tags:
- Rules API
parameters:
- name: id
in: query
required: false
description: The rule's `id` value.
schema:
$ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleSignatureId'
- name: rule_id
in: query
required: false
description: The rule's `rule_id` value.
schema:
$ref: '../../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleObjectId'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'

View file

@ -0,0 +1,25 @@
openapi: 3.0.0
info:
title: Update Rule API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules:
put:
operationId: UpdateRule
x-codegen-enabled: false
description: Update a single rule
tags:
- Rules API
requestBody:
required: true
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleUpdateProps'
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
$ref: '../../../model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'

View file

@ -0,0 +1,57 @@
openapi: 3.0.0
info:
title: Export Rules API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/_export:
summary: Exports rules to an `.ndjson` file
post:
operationId: ExportRules
x-codegen-enabled: false
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:
- Import/Export API
parameters:
- name: exclude_export_details
in: query
required: false
description: Determines whether a summary of the exported rules is returned.
schema:
type: boolean
default: false
- name: file_name
in: query
required: false
description: File name for saving the exported rules.
schema:
type: string
default: export.ndjson
requestBody:
required: false
content:
application/json:
schema:
type: object
required:
- objects
properties:
objects:
type: array
items:
type: object
required:
- rule_id
properties:
rule_id:
type: string
description: Array of `rule_id` fields. Exports all rules when unspecified.
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
type: string
format: binary
description: An `.ndjson` file containing the returned rules.

View file

@ -7,7 +7,7 @@
import * as t from 'io-ts';
import { DefaultPerPage, DefaultPage } from '@kbn/securitysolution-io-ts-alerting-types';
import type { PerPage, Page, RuleResponse } from '../../model';
import type { RuleResponse } from '../../model';
import { SortOrder, queryFilter, fields } from '../../model';
export type FindRulesSortField = t.TypeOf<typeof FindRulesSortField>;
@ -46,11 +46,6 @@ export const FindRulesRequestQuery = t.exact(
})
);
export type FindRulesRequestQueryDecoded = Omit<FindRulesRequestQuery, 'per_page'> & {
page: Page;
per_page: PerPage;
};
export interface FindRulesResponse {
page: number;
perPage: number;

View file

@ -0,0 +1,54 @@
openapi: 3.0.0
info:
title: Import Rules API endpoint
version: 2023-10-31
paths:
/api/detection_engine/rules/_import:
summary: Imports rules from an `.ndjson` file
post:
operationId: ImportRules
x-codegen-enabled: false
summary: Import rules
description: Imports rules from an `.ndjson` file, including actions and exception lists.
tags:
- Import/Export API
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: The `.ndjson` file containing the rules.
parameters:
- name: overwrite
in: query
required: false
description: Determines whether existing rules with the same `rule_id` are overwritten.
schema:
type: boolean
default: false
- name: overwrite_exceptions
in: query
required: false
description: Determines whether existing exception lists with the same `list_id` are overwritten.
schema:
type: boolean
default: false
- name: overwrite_action_connectors
in: query
required: false
description: Determines whether existing actions with the same `kibana.alert.rule.actions.id` are overwritten.
schema:
type: boolean
default: false
responses:
200:
description: Indicates a successful call.
content:
application/json:
schema:
type: object

View file

@ -19,15 +19,12 @@ export const ImportRulesRequestQuery = t.exact(
);
export type ImportRulesRequestQuery = t.TypeOf<typeof ImportRulesRequestQuery>;
export type ImportRulesRequestQueryDecoded = Omit<
ImportRulesRequestQuery,
'overwrite' | 'overwrite_exceptions' | 'as_new_list' | 'overwrite_action_connectors'
> & {
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(

View file

@ -31,4 +31,4 @@ 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 './tags/read_tags/read_tags_route';
export * from './read_tags/read_tags_route';

View file

@ -10,11 +10,7 @@ import type { QueryRuleByIds } from './query_rule_by_ids';
/**
* Additional validation that is implemented outside of the schema itself.
*/
export const validateQueryRuleByIds = (schema: QueryRuleByIds): string[] => {
return [...validateId(schema)];
};
const validateId = (rule: QueryRuleByIds): string[] => {
export const validateQueryRuleByIds = (rule: QueryRuleByIds): string[] => {
if (rule.id != null && rule.rule_id != null) {
return ['both "id" and "rule_id" cannot exist, choose one or the other'];
} else if (rule.id == null && rule.rule_id == null) {

View file

@ -0,0 +1,20 @@
openapi: 3.0.0
info:
title: Tags API endpoint
version: 2023-10-31
paths:
/api/detection_engine/tags:
summary: Aggregates and returns rule tags
get:
operationId: GetTags
x-codegen-enabled: false
summary: Aggregates and returns all unique tags from all rules
tags:
- Tags API
responses:
200:
description: Indicates a successful call
content:
application/json:
schema:
$ref: '../../model/rule_schema/common_attributes.schema.yaml#/components/schemas/RuleTagArray'

View file

@ -5,10 +5,9 @@
* 2.0.
*/
import { chunk } from 'lodash';
import type { AggregationsMultiBucketAggregateBase } from '@elastic/elasticsearch/lib/api/types';
import type { AggregationsTopHitsAggregate } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { SavedObjectsBulkCreateObject, SavedObjectsClientContract } from '@kbn/core/server';
import type { SavedObjectsClientContract } from '@kbn/core/server';
import { invariant } from '../../../../../../common/utils/invariant';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset';
@ -17,7 +16,6 @@ import { PREBUILT_RULE_ASSETS_SO_TYPE } from './prebuilt_rule_assets_type';
import type { RuleVersionSpecifier } from '../../model/rule_versions/rule_version_specifier';
const MAX_PREBUILT_RULES_COUNT = 10_000;
const MAX_ASSETS_PER_BULK_CREATE_REQUEST = 500;
export interface IPrebuiltRuleAssetsClient {
fetchLatestAssets: () => Promise<PrebuiltRuleAsset[]>;
@ -25,8 +23,6 @@ export interface IPrebuiltRuleAssetsClient {
fetchLatestVersions(): Promise<RuleVersionSpecifier[]>;
fetchAssetsByVersion(versions: RuleVersionSpecifier[]): Promise<PrebuiltRuleAsset[]>;
bulkCreateAssets(assets: PrebuiltRuleAsset[]): Promise<void>;
}
export const createPrebuiltRuleAssetsClient = (
@ -150,26 +146,5 @@ export const createPrebuiltRuleAssetsClient = (
return validatePrebuiltRuleAssets(ruleAssets);
});
},
bulkCreateAssets: (assets: PrebuiltRuleAsset[]): Promise<void> => {
return withSecuritySpan('IPrebuiltRuleAssetsClient.bulkCreateAssets', async () => {
const validAssets = validatePrebuiltRuleAssets(assets);
const bulkCreateObjects: Array<SavedObjectsBulkCreateObject<PrebuiltRuleAsset>> =
validAssets.map((asset) => ({
id: `${asset.rule_id}_${asset.version}`,
type: PREBUILT_RULE_ASSETS_SO_TYPE,
attributes: asset,
}));
const bulkCreateChunks = chunk(bulkCreateObjects, MAX_ASSETS_PER_BULK_CREATE_REQUEST);
for (const chunkOfObjects of bulkCreateChunks) {
await savedObjectsClient.bulkCreate<PrebuiltRuleAsset>(chunkOfObjects, {
refresh: false,
overwrite: true,
});
}
});
},
};
};

View file

@ -5,11 +5,13 @@
* 2.0.
*/
import { transformError } from '@kbn/securitysolution-es-utils';
import type { IKibanaResponse } from '@kbn/core/server';
import { RuleCreateProps } from '../../../../../../../common/api/detection_engine/model/rule_schema';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { CreateRuleResponse } from '../../../../../../../common/api/detection_engine/rule_management';
import { validateCreateRuleProps } from '../../../../../../../common/api/detection_engine/rule_management';
import {
CreateRuleRequestBody,
validateCreateRuleProps,
} from '../../../../../../../common/api/detection_engine/rule_management';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants';
import type { SetupPlugins } from '../../../../../../plugin';
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
@ -31,7 +33,7 @@ export const createRuleRoute = (
{
path: DETECTION_ENGINE_RULES_URL,
validate: {
body: buildRouteValidation(RuleCreateProps),
body: buildRouteValidation(CreateRuleRequestBody),
},
options: {
tags: ['access:securitySolution'],

View file

@ -5,20 +5,17 @@
* 2.0.
*/
import { transformError } from '@kbn/securitysolution-es-utils';
import type { IKibanaResponse } from '@kbn/core/server';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { DeleteRuleResponse } from '../../../../../../../common/api/detection_engine/rule_management';
import {
QueryRuleByIds,
DeleteRuleRequestQuery,
validateQueryRuleByIds,
} from '../../../../../../../common/api/detection_engine/rule_management';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants';
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { buildRouteValidation } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import { deleteRules } from '../../../logic/crud/delete_rules';
import { readRules } from '../../../logic/crud/read_rules';
import { getIdError, transform } from '../../../utils/utils';
@ -28,7 +25,7 @@ export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => {
{
path: DETECTION_ENGINE_RULES_URL,
validate: {
query: buildRouteValidation(QueryRuleByIds),
query: buildRouteValidation(DeleteRuleRequestQuery),
},
options: {
tags: ['access:securitySolution'],

View file

@ -9,10 +9,7 @@ import type { IKibanaResponse, Logger } from '@kbn/core/server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { DETECTION_ENGINE_RULES_URL_FIND } from '../../../../../../../common/constants';
import type {
FindRulesRequestQueryDecoded,
FindRulesResponse,
} from '../../../../../../../common/api/detection_engine/rule_management';
import type { FindRulesResponse } from '../../../../../../../common/api/detection_engine/rule_management';
import {
FindRulesRequestQuery,
validateFindRulesRequestQuery,
@ -29,9 +26,7 @@ export const findRulesRoute = (router: SecuritySolutionPluginRouter, logger: Log
{
path: DETECTION_ENGINE_RULES_URL_FIND,
validate: {
query: buildRouteValidation<typeof FindRulesRequestQuery, FindRulesRequestQueryDecoded>(
FindRulesRequestQuery
),
query: buildRouteValidation(FindRulesRequestQuery),
},
options: {
tags: ['access:securitySolution'],

View file

@ -7,9 +7,11 @@
import type { IKibanaResponse } from '@kbn/core/server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { RuleUpdateProps } from '../../../../../../../common/api/detection_engine/model/rule_schema';
import type { UpdateRuleResponse } from '../../../../../../../common/api/detection_engine/rule_management';
import { validateUpdateRuleProps } from '../../../../../../../common/api/detection_engine/rule_management';
import {
UpdateRuleRequestBody,
validateUpdateRuleProps,
} from '../../../../../../../common/api/detection_engine/rule_management';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants';
import type { SetupPlugins } from '../../../../../../plugin';
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
@ -29,7 +31,7 @@ export const updateRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupP
{
path: DETECTION_ENGINE_RULES_URL,
validate: {
body: buildRouteValidation(RuleUpdateProps),
body: buildRouteValidation(UpdateRuleRequestBody),
},
options: {
tags: ['access:securitySolution'],