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][Exceptions] Add lowercase normalizer for case-insensitivity + deprecate _tags field (new OS field) #77379

Merged
merged 36 commits into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e7db631
Finish adding .lower to exceptionable fields
madirey Sep 18, 2020
4219411
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 18, 2020
3693404
Add back migrations
madirey Sep 18, 2020
e36002d
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 21, 2020
614d504
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 21, 2020
af305c5
.lower -> .caseless
madirey Sep 21, 2020
04f4b42
Add separate field for os type
madirey Sep 22, 2020
b2888b5
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 22, 2020
dd355ed
updates
madirey Sep 23, 2020
d838ec1
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 23, 2020
1d658c6
Type updates
madirey Sep 23, 2020
fce8899
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 23, 2020
dee7cb6
Switch over to osTypes
madirey Sep 23, 2020
586b0e5
get rid of _tags
madirey Sep 23, 2020
c2f17ff
Add tests for schema validation
madirey Sep 24, 2020
46c2c98
Remove remaining references to _tags
madirey Sep 24, 2020
f4d925d
Another round of test fixes
madirey Sep 24, 2020
f5c0766
DefaultArray tests
madirey Sep 24, 2020
86bbf25
More test fixes
madirey Sep 24, 2020
33237c6
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 24, 2020
237a546
Fix remaining test failures
madirey Sep 24, 2020
f84b4dd
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 24, 2020
6f0cd65
types / tests
madirey Sep 24, 2020
67edc99
more test updates
madirey Sep 24, 2020
fe159c0
lowercase os values
madirey Sep 24, 2020
288b05b
Address feedback + fix test failure
madirey Sep 25, 2020
8ae05b9
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Sep 25, 2020
b54ffc4
Merge master, fix conflicts
madirey Oct 1, 2020
70c0cd1
tests
madirey Oct 1, 2020
3815908
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Oct 1, 2020
f03fada
Fix integration test
madirey Oct 1, 2020
0753542
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Oct 1, 2020
7921a1f
Merge branch 'master' into lowercase-normalizer
elasticmachine Oct 1, 2020
6a70761
Merge branch 'master' into lowercase-normalizer
elasticmachine Oct 1, 2020
24ced09
Merge branch 'master' of github.com:elastic/kibana into lowercase-nor…
madirey Oct 2, 2020
62d0362
process.executable.path -> process.executable.caseless
madirey Oct 2, 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 @@ -212,6 +212,37 @@ test('tests processing keyword field with multi fields with analyzed text field'
expect(mappings).toEqual(keywordWithAnalyzedMultiFieldsMapping);
});

test('tests processing keyword field with multi fields with normalized keyword field', () => {
const keywordWithNormalizedMultiFieldsLiteralYml = `
- name: keywordWithNormalizedMultiField
type: keyword
multi_fields:
- name: normalized
type: keyword
normalizer: lowercase
`;

const keywordWithNormalizedMultiFieldsMapping = {
properties: {
keywordWithNormalizedMultiField: {
ignore_above: 1024,
type: 'keyword',
fields: {
normalized: {
type: 'keyword',
ignore_above: 1024,
normalizer: 'lowercase',
},
},
},
},
};
const fields: Field[] = safeLoad(keywordWithNormalizedMultiFieldsLiteralYml);
const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
expect(mappings).toEqual(keywordWithNormalizedMultiFieldsMapping);
});

test('tests processing object field with no other attributes', () => {
const objectFieldLiteralYml = `
- name: objectField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ function generateKeywordMapping(field: Field): IndexTemplateMapping {
if (field.ignore_above) {
mapping.ignore_above = field.ignore_above;
}
if (field.normalizer) {
mapping.normalizer = field.normalizer;
}
return mapping;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Field {
index?: boolean;
required?: boolean;
multi_fields?: Fields;
normalizer?: string;
doc_values?: boolean;
copy_to?: string;
analyzer?: string;
Expand Down
20 changes: 2 additions & 18 deletions x-pack/plugins/lists/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,6 @@ You should see the new exception list created like so:

```sh
{
"_tags": [
"endpoint",
"process",
"malware",
"os:linux"
],
"created_at": "2020-05-28T19:16:31.052Z",
"created_by": "yo",
"description": "This is a sample endpoint type exception",
Expand All @@ -141,12 +135,6 @@ And you can attach exception list items like so:

```ts
{
"_tags": [
"endpoint",
"process",
"malware",
"os:linux"
],
"comments": [],
"created_at": "2020-05-28T19:17:21.099Z",
"created_by": "yo",
Expand All @@ -173,6 +161,7 @@ And you can attach exception list items like so:
"list_id": "endpoint_list",
"name": "Sample Endpoint Exception List",
"namespace_type": "single",
"os_types": ["linux"],
"tags": [
"user added string for a tag",
"malware"
Expand Down Expand Up @@ -222,19 +211,14 @@ or for finding exception lists:
{
"data": [
{
"_tags": [
"endpoint",
"process",
"malware",
"os:linux"
],
"created_at": "2020-05-28T19:16:31.052Z",
"created_by": "yo",
"description": "This is a sample endpoint type exception",
"id": "bcb94680-a117-11ea-ad9d-c71f4820e65b",
"list_id": "endpoint_list",
"name": "Sample Endpoint Exception List",
"namespace_type": "single",
"os_types": ["linux"],
"tags": [
"user added string for a tag",
"malware"
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/lists/common/constants.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import moment from 'moment';

import { OsTypeArray } from './schemas/common';
import { EntriesArray } from './schemas/types';
import { EndpointEntriesArray } from './schemas/types/endpoint';
export const DATE_NOW = '2020-04-20T15:25:31.830Z';
Expand Down Expand Up @@ -68,7 +69,7 @@ export const ENDPOINT_ENTRIES: EndpointEntriesArray = [
{ field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' },
];
export const ITEM_TYPE = 'simple';
export const _TAGS = [];
export const OS_TYPES: OsTypeArray = ['windows'];
export const TAGS = [];
export const COMMENTS = [];
export const FILTER = 'name:Nicolas Bourbaki';
Expand Down
33 changes: 33 additions & 0 deletions x-pack/plugins/lists/common/schemas/common/schemas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
esDataTypeUnion,
exceptionListType,
operator,
osType,
osTypeArrayOrUndefined,
type,
} from './schemas';

Expand Down Expand Up @@ -379,4 +381,35 @@ describe('Common schemas', () => {
expect(message.schema).toEqual({});
});
});

describe('osType', () => {
test('it will validate a correct osType', () => {
const payload = 'windows';
const decoded = osType.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});

test('it will fail to validate an incorrect osType', () => {
const payload = 'foo';
const decoded = osType.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "foo" supplied to ""linux" | "macos" | "windows""',
]);
expect(message.schema).toEqual({});
});

test('it will default to an empty array when osTypeArrayOrUndefined is used', () => {
const payload = undefined;
const decoded = osTypeArrayOrUndefined.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual([]);
});
});
});
20 changes: 14 additions & 6 deletions x-pack/plugins/lists/common/schemas/common/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import * as t from 'io-ts';

import { DefaultNamespace } from '../types/default_namespace';
import { DefaultStringArray, NonEmptyString } from '../../shared_imports';
import { DefaultArray, DefaultStringArray, NonEmptyString } from '../../shared_imports';

export const name = t.string;
export type Name = t.TypeOf<typeof name>;
Expand Down Expand Up @@ -211,11 +211,6 @@ export type Tags = t.TypeOf<typeof tags>;
export const tagsOrUndefined = t.union([tags, t.undefined]);
export type TagsOrUndefined = t.TypeOf<typeof tagsOrUndefined>;

export const _tags = DefaultStringArray;
export type _Tags = t.TypeOf<typeof _tags>;
export const _tagsOrUndefined = t.union([_tags, t.undefined]);
export type _TagsOrUndefined = t.TypeOf<typeof _tagsOrUndefined>;

export const exceptionListType = t.keyof({ detection: null, endpoint: null });
export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]);
export type ExceptionListType = t.TypeOf<typeof exceptionListType>;
Expand Down Expand Up @@ -317,3 +312,16 @@ export type Immutable = t.TypeOf<typeof immutable>;

export const immutableOrUndefined = t.union([immutable, t.undefined]);
export type ImmutableOrUndefined = t.TypeOf<typeof immutableOrUndefined>;

export const osType = t.keyof({
linux: null,
macos: null,
windows: null,
});
export type OsType = t.TypeOf<typeof osType>;

export const osTypeArray = DefaultArray(osType);
export type OsTypeArray = t.TypeOf<typeof osTypeArray>;

export const osTypeArrayOrUndefined = t.union([osTypeArray, t.undefined]);
export type OsTypeArrayOrUndefined = t.OutputOf<typeof osTypeArray>;
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ import {
ITEM_TYPE,
META,
NAME,
OS_TYPES,
TAGS,
_TAGS,
} from '../../constants.mock';

import { CreateEndpointListItemSchema } from './create_endpoint_list_item_schema';

export const getCreateEndpointListItemSchemaMock = (): CreateEndpointListItemSchema => ({
_tags: _TAGS,
comments: COMMENTS,
description: DESCRIPTION,
entries: ENDPOINT_ENTRIES,
item_id: undefined,
meta: META,
name: NAME,
os_types: OS_TYPES,
tags: TAGS,
type: ITEM_TYPE,
});
Original file line number Diff line number Diff line change
Expand Up @@ -174,19 +174,6 @@ describe('create_endpoint_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});

test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
const outputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload._tags;
outputPayload._tags = [];
const decoded = createEndpointListItemSchema.decode(inputPayload);
const checked = exactCheck(inputPayload, decoded);
const message = pipe(checked, foldLeftRight);
delete (message.schema as CreateEndpointListItemSchema).item_id;
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(outputPayload);
});

test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => {
const inputPayload = getCreateEndpointListItemSchemaMock();
delete inputPayload.item_id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import * as t from 'io-ts';

import {
ItemId,
OsTypeArray,
Tags,
_Tags,
_tags,
description,
exceptionListItemType,
meta,
name,
osTypeArrayOrUndefined,
tags,
} from '../common/schemas';
import { RequiredKeepUndefined } from '../../types';
Expand All @@ -34,10 +34,10 @@ export const createEndpointListItemSchema = t.intersection([
),
t.exact(
t.partial({
_tags, // defaults to empty array if not set during decode
comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode
item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode
meta, // defaults to undefined if not set during decode
os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode
tags, // defaults to empty array if not set during decode
})
),
Expand All @@ -48,11 +48,11 @@ export type CreateEndpointListItemSchema = t.OutputOf<typeof createEndpointListI
// This type is used after a decode since some things are defaults after a decode.
export type CreateEndpointListItemSchemaDecoded = Omit<
RequiredKeepUndefined<t.TypeOf<typeof createEndpointListItemSchema>>,
'_tags' | 'tags' | 'item_id' | 'entries' | 'comments'
'tags' | 'item_id' | 'entries' | 'comments' | 'os_types'
> & {
_tags: _Tags;
comments: CreateCommentsArray;
tags: Tags;
item_id: ItemId;
entries: EntriesArray;
os_types: OsTypeArray;
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ import {
META,
NAME,
NAMESPACE_TYPE,
OS_TYPES,
TAGS,
_TAGS,
} from '../../constants.mock';

import { CreateExceptionListItemSchema } from './create_exception_list_item_schema';

export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({
_tags: _TAGS,
comments: COMMENTS,
description: DESCRIPTION,
entries: ENTRIES,
Expand All @@ -30,6 +29,7 @@ export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemS
meta: META,
name: NAME,
namespace_type: NAMESPACE_TYPE,
os_types: OS_TYPES,
tags: TAGS,
type: ITEM_TYPE,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,6 @@ describe('create_exception_list_item_schema', () => {
expect(message.schema).toEqual(outputPayload);
});

test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
const outputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload._tags;
outputPayload._tags = [];
const decoded = createExceptionListItemSchema.decode(inputPayload);
const checked = exactCheck(inputPayload, decoded);
const message = pipe(checked, foldLeftRight);
delete (message.schema as CreateExceptionListItemSchema).item_id;
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(outputPayload);
});

test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => {
const inputPayload = getCreateExceptionListItemSchemaMock();
delete inputPayload.item_id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import * as t from 'io-ts';

import {
ItemId,
OsTypeArray,
Tags,
_Tags,
_tags,
description,
exceptionListItemType,
list_id,
meta,
name,
namespace_type,
osTypeArrayOrUndefined,
tags,
} from '../common/schemas';
import { RequiredKeepUndefined } from '../../types';
Expand All @@ -41,11 +41,11 @@ export const createExceptionListItemSchema = t.intersection([
),
t.exact(
t.partial({
_tags, // defaults to empty array if not set during decode
comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode
item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode
meta, // defaults to undefined if not set during decode
namespace_type, // defaults to 'single' if not set during decode
os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode
tags, // defaults to empty array if not set during decode
})
),
Expand All @@ -56,12 +56,12 @@ export type CreateExceptionListItemSchema = t.OutputOf<typeof createExceptionLis
// This type is used after a decode since some things are defaults after a decode.
export type CreateExceptionListItemSchemaDecoded = Omit<
RequiredKeepUndefined<t.TypeOf<typeof createExceptionListItemSchema>>,
'_tags' | 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments'
'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments'
> & {
_tags: _Tags;
comments: CreateCommentsArray;
tags: Tags;
item_id: ItemId;
entries: EntriesArray;
namespace_type: NamespaceType;
os_types: OsTypeArray;
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import {
import { CreateExceptionListSchema } from './create_exception_list_schema';

export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema => ({
_tags: [],
description: DESCRIPTION,
list_id: undefined,
meta: META,
name: NAME,
namespace_type: NAMESPACE_TYPE,
os_types: [],
tags: [],
type: ENDPOINT_TYPE,
version: VERSION,
Expand Down
Loading