Skip to content

Commit

Permalink
[Endpoint] Alert Details Overview (elastic#58412)
Browse files Browse the repository at this point in the history
  • Loading branch information
dplumlee authored and dgieselaar committed Mar 3, 2020
1 parent d1424cd commit 2d4f3f2
Show file tree
Hide file tree
Showing 22 changed files with 986 additions and 108 deletions.
144 changes: 119 additions & 25 deletions x-pack/plugins/endpoint/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,59 @@ export interface EndpointResultList {
request_page_index: number;
}

export interface OSFields {
full: string;
name: string;
version: string;
variant: string;
}
export interface HostFields {
id: string;
hostname: string;
ip: string[];
mac: string[];
os: OSFields;
}
export interface HashFields {
md5: string;
sha1: string;
sha256: string;
}
export interface MalwareClassifierFields {
identifier: string;
score: number;
threshold: number;
version: string;
}
export interface PrivilegesFields {
description: string;
name: string;
enabled: boolean;
}
export interface ThreadFields {
id: number;
service_name: string;
start: number;
start_address: number;
start_address_module: string;
}
export interface DllFields {
pe: {
architecture: string;
imphash: string;
};
code_signature: {
subject_name: string;
trusted: boolean;
};
compile_time: number;
hash: HashFields;
malware_classifier: MalwareClassifierFields;
mapped_address: number;
mapped_size: number;
path: string;
}

/**
* Describes an Alert Event.
* Should be in line with ECS schema.
Expand All @@ -109,26 +162,78 @@ export type AlertEvent = Immutable<{
event: {
id: string;
action: string;
category: string;
kind: string;
dataset: string;
module: string;
type: string;
};
file_classification: {
malware_classification: {
score: number;
process: {
code_signature: {
subject_name: string;
trusted: boolean;
};
};
process?: {
unique_pid: number;
command_line: string;
domain: string;
pid: number;
ppid: number;
entity_id: string;
parent: {
pid: number;
entity_id: string;
};
name: string;
hash: HashFields;
pe: {
imphash: string;
};
executable: string;
sid: string;
start: number;
malware_classifier: MalwareClassifierFields;
token: {
domain: string;
type: string;
user: string;
sid: string;
integrity_level: number;
integrity_level_name: string;
privileges: PrivilegesFields[];
};
thread: ThreadFields[];
uptime: number;
user: string;
};
host: {
hostname: string;
ip: string;
os: {
name: string;
file: {
owner: string;
name: string;
path: string;
accessed: number;
mtime: number;
created: number;
size: number;
hash: HashFields;
pe: {
imphash: string;
};
code_signature: {
trusted: boolean;
subject_name: string;
};
malware_classifier: {
features: {
data: {
buffer: string;
decompressed_size: number;
encoding: string;
};
};
} & MalwareClassifierFields;
temp_file_path: string;
};
host: HostFields;
thread: {};
endpoint?: {};
endgame?: {};
dll: DllFields[];
}>;

/**
Expand Down Expand Up @@ -161,18 +266,7 @@ export interface EndpointMetadata {
id: string;
name: string;
};
host: {
id: string;
hostname: string;
ip: string[];
mac: string[];
os: {
name: string;
full: string;
version: string;
variant: string;
};
};
host: HostFields;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Immutable } from '../../../../../common/types';
import { Immutable, AlertData } from '../../../../../common/types';
import { AlertListData } from '../../types';

interface ServerReturnedAlertsData {
readonly type: 'serverReturnedAlertsData';
readonly payload: Immutable<AlertListData>;
}

export type AlertAction = ServerReturnedAlertsData;
interface ServerReturnedAlertDetailsData {
readonly type: 'serverReturnedAlertDetailsData';
readonly payload: Immutable<AlertData>;
}

export type AlertAction = ServerReturnedAlertsData | ServerReturnedAlertDetailsData;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { Store, createStore, applyMiddleware } from 'redux';
import { History } from 'history';
import { alertListReducer } from './reducer';
import { AlertListState } from '../../types';
import { alertMiddlewareFactory } from './middleware';
import { AppAction } from '../action';
import { coreMock } from 'src/core/public/mocks';
import { createBrowserHistory } from 'history';

describe('alert details tests', () => {
let store: Store<AlertListState, AppAction>;
let coreStart: ReturnType<typeof coreMock.createStart>;
let history: History<never>;
/**
* A function that waits until a selector returns true.
*/
let selectorIsTrue: (selector: (state: AlertListState) => boolean) => Promise<void>;
beforeEach(() => {
coreStart = coreMock.createStart();
history = createBrowserHistory();
const middleware = alertMiddlewareFactory(coreStart);
store = createStore(alertListReducer, applyMiddleware(middleware));

selectorIsTrue = async selector => {
// If the selector returns true, we're done
while (selector(store.getState()) !== true) {
// otherwise, wait til the next state change occurs
await new Promise(resolve => {
const unsubscribe = store.subscribe(() => {
unsubscribe();
resolve();
});
});
}
};
});
describe('when the user is on the alert list page with a selected alert in the url', () => {
beforeEach(() => {
const firstResponse: Promise<unknown> = Promise.resolve(1);
const secondResponse: Promise<unknown> = Promise.resolve(2);
coreStart.http.get.mockReturnValueOnce(firstResponse).mockReturnValueOnce(secondResponse);

// Simulates user navigating to the /alerts page
store.dispatch({
type: 'userChangedUrl',
payload: {
...history.location,
pathname: '/alerts',
search: '?selected_alert=q9ncfh4q9ctrmc90umcq4',
},
});
});

it('should return alert details data', async () => {
// wait for alertDetails to be defined
await selectorIsTrue(state => state.alertDetails !== undefined);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { AlertResultList } from '../../../../../common/types';
import { AlertResultList, AlertData } from '../../../../../common/types';
import { AppAction } from '../action';
import { MiddlewareFactory, AlertListState } from '../../types';
import { isOnAlertPage, apiQueryParams } from './selectors';
import { isOnAlertPage, apiQueryParams, hasSelectedAlert, uiQueryParams } from './selectors';

export const alertMiddlewareFactory: MiddlewareFactory<AlertListState> = coreStart => {
return api => next => async (action: AppAction) => {
Expand All @@ -19,5 +19,12 @@ export const alertMiddlewareFactory: MiddlewareFactory<AlertListState> = coreSta
});
api.dispatch({ type: 'serverReturnedAlertsData', payload: response });
}
if (action.type === 'userChangedUrl' && isOnAlertPage(state) && hasSelectedAlert(state)) {
const uiParams = uiQueryParams(state);
const response: AlertData = await coreStart.http.get(
`/api/endpoint/alerts/${uiParams.selected_alert}`
);
api.dispatch({ type: 'serverReturnedAlertDetailsData', payload: response });
}
};
};
Loading

0 comments on commit 2d4f3f2

Please sign in to comment.