Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Detections] Adds framework for replacing API schemas #82462

Merged
merged 25 commits into from
Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7db7782
Adds framework for replacing API schemas
marshallmain Nov 3, 2020
8c22cc5
Merge branch 'master' into new-rule-schemas
marshallmain Nov 3, 2020
2ca8b1f
Update integration tests with new schema
marshallmain Nov 4, 2020
0166475
Fix response type on createRule helper
marshallmain Nov 4, 2020
c1081b2
Merge branch 'master' into new-rule-schemas
marshallmain Nov 4, 2020
e3b4449
Add unit tests for new rule schema, add defaults for some array field…
marshallmain Nov 5, 2020
328676e
Naming updates and linting fixes
marshallmain Nov 6, 2020
c41aecb
Replace create_rules_bulk_schema and refactor route
marshallmain Nov 8, 2020
494a8d9
Merge branch 'master' into new-rule-schemas
marshallmain Nov 8, 2020
428685d
Convert update_rules_route to new schema
marshallmain Nov 9, 2020
7b94ce7
Merge branch 'master' into new-rule-schemas
marshallmain Nov 9, 2020
b9a0fef
Fix missing name error
marshallmain Nov 9, 2020
78afd08
Fix more tests
marshallmain Nov 10, 2020
196e05c
Fix import
marshallmain Nov 10, 2020
3bdb900
Update patch route with internal schema validation
marshallmain Nov 10, 2020
d8a2b41
Reorganize new schema as drop-in replacement for create_rules_schema
marshallmain Nov 10, 2020
417974a
Replace updateRulesSchema with new version
marshallmain Nov 11, 2020
2ef4122
Cleanup - remove references to specific files within request folder
marshallmain Nov 11, 2020
56344ad
Merge branch 'master' into new-rule-schemas
marshallmain Nov 11, 2020
6bf3572
Fix imports
marshallmain Nov 11, 2020
e7cbd53
Fix tests
marshallmain Nov 11, 2020
6ffcbaa
Merge branch 'master' into new-rule-schemas
marshallmain Nov 11, 2020
9143785
Allow a few more fields to be undefined in internal schema
marshallmain Nov 12, 2020
beccc22
Add static types back to test payloads, add more tests, add NonEmptyA…
marshallmain Nov 12, 2020
9aac141
Pull defaults into reusable function
marshallmain Nov 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';

import {
SavedObjectAttributes,
SavedObjectAttribute,
SavedObjectAttributeSingle,
} from 'src/core/types';
import { RiskScore } from '../types/risk_score';
import { UUID } from '../types/uuid';
import { IsoDateString } from '../types/iso_date_string';
Expand Down Expand Up @@ -66,14 +71,30 @@ export type ExcludeExportDetails = t.TypeOf<typeof exclude_export_details>;
export const filters = t.array(t.unknown); // Filters are not easily type-able yet
export type Filters = t.TypeOf<typeof filters>; // Filters are not easily type-able yet

export const filtersOrUndefined = t.union([filters, t.undefined]);
export type FiltersOrUndefined = t.TypeOf<typeof filtersOrUndefined>;

export const saved_object_attribute_single: t.Type<SavedObjectAttributeSingle> = t.recursion(
'saved_object_attribute_single',
() => t.union([t.string, t.number, t.boolean, t.null, t.undefined, saved_object_attributes])
);
export const saved_object_attribute: t.Type<SavedObjectAttribute> = t.recursion(
'saved_object_attribute',
() => t.union([saved_object_attribute_single, t.array(saved_object_attribute_single)])
);
export const saved_object_attributes: t.Type<SavedObjectAttributes> = t.recursion(
'saved_object_attributes',
() => t.record(t.string, saved_object_attribute)
);

/**
* Params is an "object", since it is a type of AlertActionParams which is action templates.
* @see x-pack/plugins/alerts/common/alert.ts
*/
export const action_group = t.string;
export const action_id = t.string;
export const action_action_type_id = t.string;
export const action_params = t.object;
export const action_params = saved_object_attributes;
export const action = t.exact(
t.type({
group: action_group,
Expand All @@ -86,6 +107,18 @@ export const action = t.exact(
export const actions = t.array(action);
export type Actions = t.TypeOf<typeof actions>;

export const actionsCamel = t.array(
t.exact(
t.type({
group: action_group,
id: action_id,
actionTypeId: action_action_type_id,
params: action_params,
})
)
);
export type ActionsCamel = t.TypeOf<typeof actions>;

const stringValidator = (input: unknown): input is string => typeof input === 'string';
export const from = new t.Type<string, string, unknown>(
'From',
Expand Down Expand Up @@ -416,6 +449,10 @@ export const created_at = IsoDateString;
export const updated_at = IsoDateString;
export const updated_by = t.string;
export const created_by = t.string;
export const updatedByOrNull = t.union([updated_by, t.null]);
export type UpdatedByOrNull = t.TypeOf<typeof updatedByOrNull>;
export const createdByOrNull = t.union([created_by, t.null]);
export type CreatedByOrNull = t.TypeOf<typeof createdByOrNull>;

export const version = PositiveIntegerGreaterThanZero;
export type Version = t.TypeOf<typeof version>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/

import {
createRulesBulkSchema,
CreateRulesBulkSchema,
CreateRulesBulkSchemaDecoded,
} from './create_rules_bulk_schema';
import { createRulesBulkSchema, CreateRulesBulkSchema } from './create_rules_bulk_schema';
import { exactCheck } from '../../../exact_check';
import { foldLeftRight } from '../../../test_utils';
import {
getCreateRulesSchemaMock,
getCreateRulesSchemaDecodedMock,
} from './create_rules_schema.mock';
import { formatErrors } from '../../../format_errors';
import { CreateRulesSchema } from './create_rules_schema';
import { getCreateRulesSchemaMock } from './rule_schemas.mock';

// only the basics of testing are here.
// see: create_rules_schema.test.ts for the bulk of the validation tests
// see: rule_schemas.test.ts for the bulk of the validation tests
// this just wraps createRulesSchema in an array
describe('create_rules_bulk_schema', () => {
test('can take an empty array and validate it', () => {
Expand All @@ -38,13 +30,16 @@ describe('create_rules_bulk_schema', () => {
const decoded = createRulesBulkSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([
'Invalid value "undefined" supplied to "description"',
'Invalid value "undefined" supplied to "risk_score"',
'Invalid value "undefined" supplied to "name"',
'Invalid value "undefined" supplied to "severity"',
'Invalid value "undefined" supplied to "type"',
]);
expect(formatErrors(output.errors)).toContain(
'Invalid value "undefined" supplied to "description"'
);
expect(formatErrors(output.errors)).toContain(
'Invalid value "undefined" supplied to "risk_score"'
);
expect(formatErrors(output.errors)).toContain('Invalid value "undefined" supplied to "name"');
expect(formatErrors(output.errors)).toContain(
'Invalid value "undefined" supplied to "severity"'
);
expect(output.schema).toEqual({});
});

Expand All @@ -55,7 +50,7 @@ describe('create_rules_bulk_schema', () => {
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect(output.schema).toEqual([getCreateRulesSchemaDecodedMock()]);
expect(output.schema).toEqual(payload);
});

test('two array elements do validate', () => {
Expand All @@ -65,10 +60,7 @@ describe('create_rules_bulk_schema', () => {
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect(output.schema).toEqual([
getCreateRulesSchemaDecodedMock(),
getCreateRulesSchemaDecodedMock(),
]);
expect(output.schema).toEqual(payload);
});

test('single array element with a missing value (risk_score) will not validate', () => {
Expand Down Expand Up @@ -137,7 +129,7 @@ describe('create_rules_bulk_schema', () => {
});

test('two array elements where the first is invalid (extra key and value) but the second is valid will not validate', () => {
const singleItem: CreateRulesSchema & { madeUpValue: string } = {
const singleItem = {
...getCreateRulesSchemaMock(),
madeUpValue: 'something',
};
Expand All @@ -152,8 +144,8 @@ describe('create_rules_bulk_schema', () => {
});

test('two array elements where the second is invalid (extra key and value) but the first is valid will not validate', () => {
const singleItem: CreateRulesSchema = getCreateRulesSchemaMock();
const secondItem: CreateRulesSchema & { madeUpValue: string } = {
const singleItem = getCreateRulesSchemaMock();
const secondItem = {
...getCreateRulesSchemaMock(),
madeUpValue: 'something',
};
Expand All @@ -167,11 +159,11 @@ describe('create_rules_bulk_schema', () => {
});

test('two array elements where both are invalid (extra key and value) will not validate', () => {
const singleItem: CreateRulesSchema & { madeUpValue: string } = {
const singleItem = {
...getCreateRulesSchemaMock(),
madeUpValue: 'something',
};
const secondItem: CreateRulesSchema & { madeUpValue: string } = {
const secondItem = {
...getCreateRulesSchemaMock(),
madeUpValue: 'something',
};
Expand All @@ -184,28 +176,6 @@ describe('create_rules_bulk_schema', () => {
expect(output.schema).toEqual({});
});

test('The default for "from" will be "now-6m"', () => {
const { from, ...withoutFrom } = getCreateRulesSchemaMock();
const payload: CreateRulesBulkSchema = [withoutFrom];

const decoded = createRulesBulkSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect((output.schema as CreateRulesBulkSchemaDecoded)[0].from).toEqual('now-6m');
});

test('The default for "to" will be "now"', () => {
const { to, ...withoutTo } = getCreateRulesSchemaMock();
const payload: CreateRulesBulkSchema = [withoutTo];

const decoded = createRulesBulkSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect((output.schema as CreateRulesBulkSchemaDecoded)[0].to).toEqual('now');
});

test('You cannot set the severity to a value other than low, medium, high, or critical', () => {
const badSeverity = { ...getCreateRulesSchemaMock(), severity: 'madeup' };
const payload = [badSeverity];
Expand All @@ -226,9 +196,7 @@ describe('create_rules_bulk_schema', () => {
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect(output.schema).toEqual([
{ ...getCreateRulesSchemaDecodedMock(), note: '# test markdown' },
]);
expect(output.schema).toEqual(payload);
});

test('You can set "note" to an empty string', () => {
Expand All @@ -238,10 +206,10 @@ describe('create_rules_bulk_schema', () => {
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect(output.schema).toEqual([{ ...getCreateRulesSchemaDecodedMock(), note: '' }]);
expect(output.schema).toEqual(payload);
});

test('You can set "note" to anything other than string', () => {
test('You cant set "note" to anything other than string', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use cannot? :D

const payload = [
{
...getCreateRulesSchemaMock(),
Expand All @@ -259,26 +227,4 @@ describe('create_rules_bulk_schema', () => {
]);
expect(output.schema).toEqual({});
});

test('The default for "actions" will be an empty array', () => {
const { actions, ...withoutActions } = getCreateRulesSchemaMock();
const payload: CreateRulesBulkSchema = [withoutActions];

const decoded = createRulesBulkSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect((output.schema as CreateRulesBulkSchemaDecoded)[0].actions).toEqual([]);
});

test('The default for "throttle" will be null', () => {
const { throttle, ...withoutThrottle } = getCreateRulesSchemaMock();
const payload: CreateRulesBulkSchema = [withoutThrottle];

const decoded = createRulesBulkSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const output = foldLeftRight(checked);
expect(formatErrors(output.errors)).toEqual([]);
expect((output.schema as CreateRulesBulkSchemaDecoded)[0].throttle).toEqual(null);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

import * as t from 'io-ts';

import { createRulesSchema, CreateRulesSchemaDecoded } from './create_rules_schema';
import { createRulesSchema } from './rule_schemas';

export const createRulesBulkSchema = t.array(createRulesSchema);
export type CreateRulesBulkSchema = t.TypeOf<typeof createRulesBulkSchema>;

export type CreateRulesBulkSchemaDecoded = CreateRulesSchemaDecoded[];
Loading