Skip to content

Commit

Permalink
[Security Solution][Exceptions] - Adds support for file.Ext.code_sign…
Browse files Browse the repository at this point in the history
…ature to be object or array of objects (#77611)

* init commit

* lots of cleanup

* starting on tests... problems

* Ready for review

* remove sample data

* remove comment and fix type

* pr changes

* fix type

* scratchy

* sourcerer in timeline

* sourcerer in timeline

* wip

* moving to redux

* working on types

* fixed

* more adjustments, tests fixed

* FF off

* pr ready

* renaming

* url state working, hoc not working

* url state working for timeline and default scope

* script to build fields for beat doc

* refactor sourcerer

* refactor host to useSourcerer

* refactor network to useSourcerer

* refactor overview  to useSourcerer

* refactor detections to useSourcerer

* wip for timelines to remove all useSource

* wip indexes timeline

* do component tests

* start container tests

* start container tests

* update selection widget of index patterns + remove last useWithSource

* modifications to allow file.Ext.code_signature to be either an object or array of objects

* tweaked helper

* add indexeNames in network kpi

* fix type errors

* fix type

* missing merge master

* get existing index from config file

* fixing broken tests

* add saving button to avoid to many queries to be aborted

* try to get data from ecs data

* reducer timeline tests broke

* need to rewind

* much better

* timeline saving index names + clean up url state to only manage default

* more test fixing

* more test changes

* remove all the useWithSource + deprecated the graphql until we delete it in a new PR + delete all the beat doc

* default timeline to all index when creation + filter index patterns to make sure you do not add one who we do not know

* fix types

* test for stateful timeline render

* we should not have change that

* no chnages + snapshot

* fix test + bugs from review

* fix uncommon processes indexNames

* review III

* change design for main page of the sourcerer from design

* bug fixes when opening old timeline + implementation of new design

* fix circular deps

* remove unused attributes for event details

* design cleanup

* moved exeptions modal to use ecs data instead of non ecs timeline data

* trying to fix some merge issues

* added pr feedback

* remove unused component prop

* updated per feedback

Co-authored-by: Steph Milovic <[email protected]>
Co-authored-by: Xavier Mouligneau <[email protected]>
Co-authored-by: Patryk Kopycinski <[email protected]>
  • Loading branch information
4 people authored Sep 29, 2020
1 parent 66866d0 commit 0329f92
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 258 deletions.
15 changes: 15 additions & 0 deletions x-pack/plugins/security_solution/common/ecs/file/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

export interface CodeSignature {
subject_name: string[];
trusted: string[];
}
export interface Ext {
code_signature: CodeSignature[] | CodeSignature;
}
export interface Hash {
sha256: string[];
}

export interface FileEcs {
name?: string[];

Expand All @@ -13,6 +24,8 @@ export interface FileEcs {

extension?: string[];

Ext?: Ext;

type?: string[];

device?: string[];
Expand All @@ -34,4 +47,6 @@ export interface FileEcs {
mtime?: string[];

ctime?: string[];

hash?: Hash;
}
1 change: 1 addition & 0 deletions x-pack/plugins/security_solution/common/ecs/rule/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
export interface RuleEcs {
id?: string[];
rule_id?: string[];
name?: string[];
false_positives: string[];
saved_id?: string[];
timeline_id?: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { useAddOrUpdateException } from '../use_add_exception';
import { useFetchOrCreateRuleExceptionList } from '../use_fetch_or_create_rule_exception_list';
import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index';
import { Ecs } from '../../../../../common/ecs';
import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline';
import * as builder from '../builder';
import * as helpers from '../helpers';
import { getExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_item_schema.mock';
Expand Down Expand Up @@ -148,10 +147,8 @@ describe('When the add exception modal is opened', () => {
describe('when there is alert data passed to an endpoint list exception', () => {
let wrapper: ReactWrapper;
beforeEach(async () => {
const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = {
ecsData: { _id: 'test-id' },
nonEcsData: [{ field: 'file.path', value: ['test/path'] }],
};
const alertDataMock: Ecs = { _id: 'test-id', file: { path: ['test/path'] } };

wrapper = mount(
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>
<AddExceptionModal
Expand Down Expand Up @@ -201,10 +198,8 @@ describe('When the add exception modal is opened', () => {
describe('when there is alert data passed to a detection list exception', () => {
let wrapper: ReactWrapper;
beforeEach(async () => {
const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = {
ecsData: { _id: 'test-id' },
nonEcsData: [{ field: 'file.path', value: ['test/path'] }],
};
const alertDataMock: Ecs = { _id: 'test-id', file: { path: ['test/path'] } };

wrapper = mount(
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>
<AddExceptionModal
Expand Down Expand Up @@ -271,10 +266,7 @@ describe('When the add exception modal is opened', () => {
},
},
]);
const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = {
ecsData: { _id: 'test-id' },
nonEcsData: [{ field: 'file.path', value: ['test/path'] }],
};
const alertDataMock: Ecs = { _id: 'test-id', file: { path: ['test/path'] } };
wrapper = mount(
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>
<AddExceptionModal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import * as i18nCommon from '../../../translations';
import * as i18n from './translations';
import * as sharedI18n from '../translations';
import { Ecs } from '../../../../../common/ecs';
import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline';
import { useAppToasts } from '../../../hooks/use_app_toasts';
import { useKibana } from '../../../lib/kibana';
import { ExceptionBuilderComponent } from '../builder';
Expand All @@ -47,28 +46,21 @@ import {
defaultEndpointExceptionItems,
entryHasListType,
entryHasNonEcsType,
getMappedNonEcsValue,
} from '../helpers';
import { ErrorInfo, ErrorCallout } from '../error_callout';
import { ExceptionsBuilderExceptionItem } from '../types';
import { useFetchIndex } from '../../../containers/source';

export interface AddExceptionModalBaseProps {
export interface AddExceptionModalProps {
ruleName: string;
ruleId: string;
exceptionListType: ExceptionListType;
ruleIndices: string[];
alertData?: {
ecsData: Ecs;
nonEcsData: TimelineNonEcsData[];
};
}

export interface AddExceptionModalProps extends AddExceptionModalBaseProps {
alertData?: Ecs;
alertStatus?: Status;
onCancel: () => void;
onConfirm: (didCloseAlert: boolean, didBulkCloseAlert: boolean) => void;
onRuleChange?: () => void;
alertStatus?: Status;
}

const Modal = styled(EuiModal)`
Expand Down Expand Up @@ -218,12 +210,12 @@ export const AddExceptionModal = memo(function AddExceptionModal({
});

const initialExceptionItems = useMemo((): ExceptionsBuilderExceptionItem[] => {
if (exceptionListType === 'endpoint' && alertData !== undefined && ruleExceptionList) {
if (exceptionListType === 'endpoint' && alertData != null && ruleExceptionList) {
return defaultEndpointExceptionItems(
exceptionListType,
ruleExceptionList.list_id,
ruleName,
alertData.nonEcsData
alertData
);
} else {
return [];
Expand Down Expand Up @@ -275,15 +267,12 @@ export const AddExceptionModal = memo(function AddExceptionModal({

const retrieveAlertOsTypes = useCallback((): string[] => {
const osDefaults = ['windows', 'macos'];
if (alertData) {
const osTypes = getMappedNonEcsValue({
data: alertData.nonEcsData,
fieldName: 'host.os.family',
});
if (osTypes.length === 0) {
return osDefaults;
if (alertData != null) {
const osTypes = alertData.host && alertData.host.os && alertData.host.os.family;
if (osTypes != null && osTypes.length > 0) {
return osTypes;
}
return osTypes;
return osDefaults;
}
return osDefaults;
}, [alertData]);
Expand All @@ -304,10 +293,10 @@ export const AddExceptionModal = memo(function AddExceptionModal({
}, [comment, exceptionItemsToAdd, exceptionListType, retrieveAlertOsTypes]);

const onAddExceptionConfirm = useCallback((): void => {
if (addOrUpdateExceptionItems !== null) {
const alertIdToClose = shouldCloseAlert && alertData ? alertData.ecsData._id : undefined;
if (addOrUpdateExceptionItems != null) {
const alertIdToClose = shouldCloseAlert && alertData ? alertData._id : undefined;
const bulkCloseIndex =
shouldBulkCloseAlert && signalIndexName !== null ? [signalIndexName] : undefined;
shouldBulkCloseAlert && signalIndexName != null ? [signalIndexName] : undefined;
addOrUpdateExceptionItems(ruleId, enrichExceptionItems(), alertIdToClose, bulkCloseIndex);
}
}, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import {
entryHasNonEcsType,
prepareExceptionItemsForBulkClose,
lowercaseHashValues,
getPrepopulatedItem,
getCodeSignatureValue,
defaultEndpointExceptionItems,
} from './helpers';
import { EmptyEntry } from './types';
import {
Expand Down Expand Up @@ -708,4 +711,207 @@ describe('Exception helpers', () => {
]);
});
});

describe('getPrepopulatedItem', () => {
test('it returns prepopulated items', () => {
const prepopulatedItem = getPrepopulatedItem({
listType: 'endpoint',
listId: 'some_id',
ruleName: 'my rule',
codeSignature: { subjectName: '', trusted: '' },
filePath: '',
sha256Hash: '',
eventCode: '',
});

expect(prepopulatedItem.entries).toEqual([
{
entries: [
{ field: 'subject_name', operator: 'included', type: 'match', value: '' },
{ field: 'trusted', operator: 'included', type: 'match', value: '' },
],
field: 'file.Ext.code_signature',
type: 'nested',
},
{ field: 'file.path.text', operator: 'included', type: 'match', value: '' },
{ field: 'file.hash.sha256', operator: 'included', type: 'match', value: '' },
{ field: 'event.code', operator: 'included', type: 'match', value: '' },
]);
});

test('it returns prepopulated items with values', () => {
const prepopulatedItem = getPrepopulatedItem({
listType: 'endpoint',
listId: 'some_id',
ruleName: 'my rule',
codeSignature: { subjectName: 'someSubjectName', trusted: 'false' },
filePath: 'some-file-path',
sha256Hash: 'some-hash',
eventCode: 'some-event-code',
});

expect(prepopulatedItem.entries).toEqual([
{
entries: [
{
field: 'subject_name',
operator: 'included',
type: 'match',
value: 'someSubjectName',
},
{ field: 'trusted', operator: 'included', type: 'match', value: 'false' },
],
field: 'file.Ext.code_signature',
type: 'nested',
},
{ field: 'file.path.text', operator: 'included', type: 'match', value: 'some-file-path' },
{ field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some-hash' },
{ field: 'event.code', operator: 'included', type: 'match', value: 'some-event-code' },
]);
});
});

describe('getCodeSignatureValue', () => {
test('it works when file.Ext.code_signature is an object', () => {
const codeSignatures = getCodeSignatureValue({
_id: '123',
file: {
Ext: {
code_signature: {
subject_name: ['some_subject'],
trusted: ['false'],
},
},
},
});

expect(codeSignatures).toEqual([{ subjectName: 'some_subject', trusted: 'false' }]);
});

test('it works when file.Ext.code_signature is nested type', () => {
const codeSignatures = getCodeSignatureValue({
_id: '123',
file: {
Ext: {
code_signature: [
{ subject_name: ['some_subject'], trusted: ['false'] },
{ subject_name: ['some_subject_2'], trusted: ['true'] },
],
},
},
});

expect(codeSignatures).toEqual([
{ subjectName: 'some_subject', trusted: 'false' },
{
subjectName: 'some_subject_2',
trusted: 'true',
},
]);
});

test('it returns default when file.Ext.code_signatures values are empty', () => {
const codeSignatures = getCodeSignatureValue({
_id: '123',
file: {
Ext: {
code_signature: { subject_name: [], trusted: [] },
},
},
});

expect(codeSignatures).toEqual([{ subjectName: '', trusted: '' }]);
});

test('it returns default when file.Ext.code_signatures is empty array', () => {
const codeSignatures = getCodeSignatureValue({
_id: '123',
file: {
Ext: {
code_signature: [],
},
},
});

expect(codeSignatures).toEqual([{ subjectName: '', trusted: '' }]);
});

test('it returns default when file.Ext.code_signatures does not exist', () => {
const codeSignatures = getCodeSignatureValue({
_id: '123',
});

expect(codeSignatures).toEqual([{ subjectName: '', trusted: '' }]);
});
});

describe('defaultEndpointExceptionItems', () => {
test('it should return pre-populated items', () => {
const defaultItems = defaultEndpointExceptionItems('endpoint', 'list_id', 'my_rule', {
_id: '123',
file: {
Ext: {
code_signature: [
{ subject_name: ['some_subject'], trusted: ['false'] },
{ subject_name: ['some_subject_2'], trusted: ['true'] },
],
},
path: ['some file path'],
hash: {
sha256: ['some hash'],
},
},
event: {
code: ['some event code'],
},
});

expect(defaultItems[0].entries).toEqual([
{
entries: [
{
field: 'subject_name',
operator: 'included',
type: 'match',
value: 'some_subject',
},
{ field: 'trusted', operator: 'included', type: 'match', value: 'false' },
],
field: 'file.Ext.code_signature',
type: 'nested',
},
{
field: 'file.path.text',
operator: 'included',
type: 'match',
value: 'some file path',
},
{ field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some hash' },
{ field: 'event.code', operator: 'included', type: 'match', value: 'some event code' },
]);
expect(defaultItems[1].entries).toEqual([
{
entries: [
{
field: 'subject_name',
operator: 'included',
type: 'match',
value: 'some_subject_2',
},
{ field: 'trusted', operator: 'included', type: 'match', value: 'true' },
],
field: 'file.Ext.code_signature',
type: 'nested',
},
{
field: 'file.path.text',
operator: 'included',
type: 'match',
value: 'some file path',
},
{ field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some hash' },
{ field: 'event.code', operator: 'included', type: 'match', value: 'some event code' },
]);
});
});
});
Loading

0 comments on commit 0329f92

Please sign in to comment.