-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Endpoint] Alert Details Overview #58412
Changes from 15 commits
6eace7e
a54cd58
0f091d5
1769e1d
0d86e09
16a4953
9e82edd
6452fb6
67c8f11
b64ca21
fddef5d
f66f7f7
3854b02
ce5d25b
5500167
93f7cf0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
@@ -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[]; | ||
}>; | ||
|
||
/** | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ping @nnamdifrankie - Just FYI. Does not change the type, just breaks out into smaller |
||
} | ||
|
||
/** | ||
|
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 => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: I was going to create something similar as well - some of my Policy middleware tests started to fail in Release branch and kibana-operation turn them off. The only think I was thinking about doing that differs from this approach was that I was going to reject the Promise after some There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i do agree the test is kind of brittle, i think the goal is to remove it when qualters creates a functional test for the flyout/resolver in his next pr There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @paul-tavares we originally had it reject after 10 times, but since no additional actions are ever dispatched (no user exists after all) that limit was never hit. At the moment jest just times out after 4.5 seconds |
||
// 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 |
---|---|---|
|
@@ -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) => { | ||
|
@@ -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}` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. at some point - need to add error handling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, i have a task for it in the github issue |
||
); | ||
api.dispatch({ type: 'serverReturnedAlertDetailsData', payload: response }); | ||
} | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These fields are not going to be used by the UI, right? Anything the UI won't use should be optional or even removed from the types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but we can do that in subsequent PRs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding this to this ticket elastic/endpoint-app-team#189