-
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] resolver v1 events #59233
Changes from 4 commits
a9fcd64
4223a42
8562b20
940959e
692a945
fa4c139
479f813
d9db350
ca85aa4
c160677
084ac79
5e06e8d
971866a
e5c0a1d
ed6c269
aa3912f
3eea057
074bba0
7b13c14
7b13e0e
9e4a829
840911c
f38635c
ef7b568
bcf1035
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 |
---|---|---|
|
@@ -31,9 +31,9 @@ export enum Direction { | |
|
||
export class EndpointAppConstants { | ||
static BASE_API_URL = '/api/endpoint'; | ||
static ALERT_INDEX_NAME = 'my-index'; | ||
static ENDPOINT_INDEX_NAME = 'endpoint-agent*'; | ||
static EVENT_INDEX_NAME = 'endpoint-events-*'; | ||
static ALERT_INDEX_NAME = 'events-endpoint-1'; | ||
static EVENT_INDEX_NAME = 'events-endpoint-*'; | ||
static DEFAULT_TOTAL_HITS = 10000; | ||
/** | ||
* Legacy events are stored in indices with endgame-* prefix | ||
|
@@ -315,23 +315,34 @@ export interface EndpointEvent { | |
category: string; | ||
type: string; | ||
id: string; | ||
kind: string; | ||
}; | ||
endpoint: { | ||
process: { | ||
entity_id: string; | ||
parent: { | ||
parent?: { | ||
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. why do we need process.parent.parent? 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. we don't, not sure how this was added. bad merge or something. removing. |
||
entity_id: string; | ||
}; | ||
name: string; | ||
}; | ||
}; | ||
agent: { | ||
id: string; | ||
type: string; | ||
}; | ||
process: { | ||
name: string; | ||
}; | ||
} | ||
|
||
export type ResolverEvent = EndpointEvent | LegacyEndpointEvent; | ||
|
||
export function isLegacyEvent( | ||
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. Would you plz move this out of 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. ❔ If its sole purpose seems to be as a type discriminant, where should it go? A 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. i think 'model' would be good. I'd say 'no' to selector as it doesn't take global state as its only param (doesn't take it at all). |
||
event: EndpointEvent | LegacyEndpointEvent | ||
): event is LegacyEndpointEvent { | ||
return (event as LegacyEndpointEvent).endgame !== undefined; | ||
} | ||
|
||
/** | ||
* The PageId type is used for the payload when firing userNavigatedToPage actions | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,91 +4,95 @@ | |
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
import React, { memo, useMemo } from 'react'; | ||
import styled from 'styled-components'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { FormattedMessage } from '@kbn/i18n/react'; | ||
import { EuiSpacer, EuiTitle, EuiText, EuiHealth, EuiTabbedContent } from '@elastic/eui'; | ||
import { useAlertListSelector } from '../../hooks/use_alerts_selector'; | ||
import * as selectors from '../../../../store/alerts/selectors'; | ||
import { ResolverEvent } from '../../../../../../../common/types'; | ||
import { MetadataPanel } from './metadata_panel'; | ||
import { FormattedDate } from '../../formatted_date'; | ||
import { AlertDetailResolver } from '../../resolver'; | ||
|
||
export const AlertDetailsOverview = memo(() => { | ||
const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); | ||
if (alertDetailsData === undefined) { | ||
return null; | ||
} | ||
const selectedAlertIsLegacyEndpointEvent = useAlertListSelector( | ||
selectors.selectedAlertIsLegacyEndpointEvent | ||
); | ||
export const AlertDetailsOverview = styled( | ||
memo(({ className }: { className?: string }) => { | ||
const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); | ||
if (alertDetailsData === undefined) { | ||
return null; | ||
} | ||
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. Could you please remove the 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. done |
||
|
||
const tabs = useMemo(() => { | ||
return [ | ||
{ | ||
id: 'overviewMetadata', | ||
name: i18n.translate( | ||
'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview', | ||
{ | ||
defaultMessage: 'Overview', | ||
} | ||
), | ||
content: ( | ||
<> | ||
<EuiSpacer /> | ||
<MetadataPanel /> | ||
</> | ||
), | ||
}, | ||
{ | ||
id: 'overviewResolver', | ||
name: i18n.translate( | ||
'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver', | ||
{ | ||
defaultMessage: 'Resolver', | ||
} | ||
), | ||
content: ( | ||
<> | ||
<EuiSpacer /> | ||
{selectedAlertIsLegacyEndpointEvent && <AlertDetailResolver />} | ||
</> | ||
), | ||
}, | ||
]; | ||
}, [selectedAlertIsLegacyEndpointEvent]); | ||
const tabs = useMemo(() => { | ||
return [ | ||
{ | ||
id: 'overviewMetadata', | ||
name: i18n.translate( | ||
'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview', | ||
{ | ||
defaultMessage: 'Overview', | ||
} | ||
), | ||
content: ( | ||
<> | ||
<EuiSpacer /> | ||
<MetadataPanel /> | ||
</> | ||
), | ||
}, | ||
{ | ||
id: 'overviewResolver', | ||
name: i18n.translate( | ||
'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver', | ||
{ | ||
defaultMessage: 'Resolver', | ||
} | ||
), | ||
content: ( | ||
<> | ||
<EuiSpacer /> | ||
<AlertDetailResolver selectedEvent={(alertDetailsData as unknown) as ResolverEvent} /> | ||
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. Why is this unsafe cast needed? can we work out a way to remove it? 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. imo we should get rid of the AlertData type, as it's really just a variation of an EndpointEvent 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. does removing the cast change anything then? 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. @oatkiller @kqualters-elastic @marshallmain ResolverEvent = EndpointEvent | LegacyEndpointEvent;
export type AlertData = AlertEvent & AlertMetadata; We have Does it make sense to model our types after these schemas? If so, I think our 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 agreed, wouldn't hurt to support 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 think that adds up. That being said, if malware_event, network, process, and registry are in the same index, I don't see why they shouldn't be the same schema. In fact, I don't see why ECS shouldn't just be a single schema. Its not Elastic Common Schemas is it? |
||
</> | ||
), | ||
}, | ||
]; | ||
}, [alertDetailsData]); | ||
|
||
return ( | ||
<> | ||
<section className="details-overview-summary"> | ||
<EuiTitle size="s"> | ||
<h3> | ||
<FormattedMessage | ||
id="xpack.endpoint.application.endpoint.alertDetails.overview.title" | ||
defaultMessage="Detected Malicious File" | ||
/> | ||
</h3> | ||
</EuiTitle> | ||
<EuiSpacer /> | ||
<EuiText> | ||
<p> | ||
<FormattedMessage | ||
id="xpack.endpoint.application.endpoint.alertDetails.overview.summary" | ||
defaultMessage="MalwareScore detected the opening of a document on {hostname} on {date}" | ||
values={{ | ||
hostname: alertDetailsData.host.hostname, | ||
date: <FormattedDate timestamp={alertDetailsData['@timestamp']} />, | ||
}} | ||
/> | ||
</p> | ||
</EuiText> | ||
<EuiSpacer /> | ||
<EuiText> | ||
Endpoint Status: <EuiHealth color="success">Online</EuiHealth> | ||
</EuiText> | ||
<EuiText>Alert Status: Open</EuiText> | ||
<EuiSpacer /> | ||
</section> | ||
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} /> | ||
</> | ||
); | ||
}); | ||
return ( | ||
<> | ||
<section className="details-overview-summary"> | ||
<EuiTitle size="s"> | ||
<h3> | ||
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 think the h3 might be ( ❔ ) redundant but maybe you have to use the |
||
<FormattedMessage | ||
id="xpack.endpoint.application.endpoint.alertDetails.overview.title" | ||
defaultMessage="Detected Malicious File" | ||
/> | ||
</h3> | ||
</EuiTitle> | ||
<EuiSpacer /> | ||
<EuiText> | ||
<p> | ||
<FormattedMessage | ||
id="xpack.endpoint.application.endpoint.alertDetails.overview.summary" | ||
defaultMessage="MalwareScore detected the opening of a document on {hostname} on {date}" | ||
values={{ | ||
hostname: alertDetailsData.host.hostname, | ||
date: <FormattedDate timestamp={alertDetailsData['@timestamp']} />, | ||
}} | ||
/> | ||
</p> | ||
</EuiText> | ||
<EuiSpacer /> | ||
<EuiText> | ||
Endpoint Status: <EuiHealth color="success">Online</EuiHealth> | ||
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. Would you please i18n this? |
||
</EuiText> | ||
<EuiText>Alert Status: Open</EuiText> | ||
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. Would you please i18n this? |
||
<EuiSpacer /> | ||
</section> | ||
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} className={className} /> | ||
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. 🆗 Nice use of |
||
</> | ||
); | ||
}) | ||
)` | ||
height: 100%; | ||
width: 100%; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,12 +10,12 @@ import { Provider } from 'react-redux'; | |
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; | ||
import { Resolver } from '../../../../embeddables/resolver/view'; | ||
import { EndpointPluginServices } from '../../../../plugin'; | ||
import { LegacyEndpointEvent } from '../../../../../common/types'; | ||
import { ResolverEvent } from '../../../../../common/types'; | ||
import { storeFactory } from '../../../../embeddables/resolver/store'; | ||
|
||
export const AlertDetailResolver = styled( | ||
React.memo( | ||
({ className, selectedEvent }: { className?: string; selectedEvent?: LegacyEndpointEvent }) => { | ||
({ className, selectedEvent }: { className?: string; selectedEvent?: ResolverEvent }) => { | ||
const context = useKibana<EndpointPluginServices>(); | ||
const { store } = storeFactory(context); | ||
|
||
|
@@ -33,4 +33,6 @@ export const AlertDetailResolver = styled( | |
width: 100%; | ||
display: flex; | ||
flex-grow: 1; | ||
// ugly demo hack until can play nice with eui | ||
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. If this is still here by the time we merge, let's remove the comment and add a github issue instead |
||
min-height: 1000px; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,15 +6,15 @@ | |
|
||
import { uniquePidForProcess, uniqueParentPidForProcess } from './process_event'; | ||
import { IndexedProcessTree } from '../types'; | ||
import { LegacyEndpointEvent } from '../../../../common/types'; | ||
import { ResolverEvent } from '../../../../common/types'; | ||
import { levelOrder as baseLevelOrder } from '../lib/tree_sequencers'; | ||
|
||
/** | ||
* Create a new IndexedProcessTree from an array of ProcessEvents | ||
*/ | ||
export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree { | ||
const idToChildren = new Map<number | undefined, LegacyEndpointEvent[]>(); | ||
const idToValue = new Map<number, LegacyEndpointEvent>(); | ||
export function factory(processes: ResolverEvent[]): IndexedProcessTree { | ||
const idToChildren = new Map<string | undefined, ResolverEvent[]>(); | ||
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. 🆗 Cool, I didn't know you could use Type directions in constructors that way. |
||
const idToValue = new Map<string, ResolverEvent>(); | ||
|
||
for (const process of processes) { | ||
idToValue.set(uniquePidForProcess(process), process); | ||
|
@@ -36,10 +36,7 @@ export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree { | |
/** | ||
* Returns an array with any children `ProcessEvent`s of the passed in `process` | ||
*/ | ||
export function children( | ||
tree: IndexedProcessTree, | ||
process: LegacyEndpointEvent | ||
): LegacyEndpointEvent[] { | ||
export function children(tree: IndexedProcessTree, process: ResolverEvent): ResolverEvent[] { | ||
const id = uniquePidForProcess(process); | ||
const processChildren = tree.idToChildren.get(id); | ||
return processChildren === undefined ? [] : processChildren; | ||
|
@@ -50,8 +47,8 @@ export function children( | |
*/ | ||
export function parent( | ||
tree: IndexedProcessTree, | ||
childProcess: LegacyEndpointEvent | ||
): LegacyEndpointEvent | undefined { | ||
childProcess: ResolverEvent | ||
): ResolverEvent | undefined { | ||
const uniqueParentPid = uniqueParentPidForProcess(childProcess); | ||
if (uniqueParentPid === undefined) { | ||
return undefined; | ||
|
@@ -74,7 +71,7 @@ export function root(tree: IndexedProcessTree) { | |
if (size(tree) === 0) { | ||
return null; | ||
} | ||
let current: LegacyEndpointEvent = tree.idToProcess.values().next().value; | ||
let current: ResolverEvent = tree.idToProcess.values().next().value; | ||
while (parent(tree, current) !== undefined) { | ||
current = parent(tree, current)!; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,50 +4,77 @@ | |
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { LegacyEndpointEvent } from '../../../../common/types'; | ||
import { isLegacyEvent, ResolverEvent } from '../../../../common/types'; | ||
|
||
/** | ||
* Returns true if the process's eventType is either 'processCreated' or 'processRan'. | ||
* Resolver will only render 'graphable' process events. | ||
*/ | ||
export function isGraphableProcess(passedEvent: LegacyEndpointEvent) { | ||
export function isGraphableProcess(passedEvent: ResolverEvent) { | ||
return eventType(passedEvent) === 'processCreated' || eventType(passedEvent) === 'processRan'; | ||
} | ||
|
||
/** | ||
* Returns a custom event type for a process event based on the event's metadata. | ||
*/ | ||
export function eventType(passedEvent: LegacyEndpointEvent) { | ||
const { | ||
endgame: { event_type_full: type, event_subtype_full: subType }, | ||
} = passedEvent; | ||
export function eventType(passedEvent: ResolverEvent) { | ||
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. could you add a return type here?
|
||
if (isLegacyEvent(passedEvent)) { | ||
const { | ||
endgame: { event_type_full: type, event_subtype_full: subType }, | ||
} = passedEvent; | ||
|
||
if (type === 'process_event') { | ||
if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') { | ||
return 'processCreated'; | ||
} else if (subType === 'already_running') { | ||
return 'processRan'; | ||
} else if (subType === 'termination_event') { | ||
return 'processTerminated'; | ||
} else { | ||
return 'unknownProcessEvent'; | ||
if (type === 'process_event') { | ||
if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') { | ||
return 'processCreated'; | ||
} else if (subType === 'already_running') { | ||
return 'processRan'; | ||
} else if (subType === 'termination_event') { | ||
return 'processTerminated'; | ||
} else { | ||
return 'unknownProcessEvent'; | ||
} | ||
} else if (type === 'alert_event') { | ||
return 'processCausedAlert'; | ||
} | ||
return 'unknownEvent'; | ||
} else { | ||
const { | ||
event: { type, category, kind }, | ||
} = passedEvent; | ||
if (category === 'process') { | ||
if (type === 'start' || type === 'change') { | ||
return 'processCreated'; | ||
} else if (type === 'info') { | ||
return 'processRan'; | ||
} else if (type === 'end') { | ||
return 'processTerminated'; | ||
} else { | ||
return 'unknownProcessEvent'; | ||
} | ||
} else if (kind === 'alert') { | ||
return 'processCausedAlert'; | ||
} | ||
} else if (type === 'alert_event') { | ||
return 'processCausedAlert'; | ||
} | ||
return 'unknownEvent'; | ||
} | ||
|
||
/** | ||
* Returns the process event's pid | ||
*/ | ||
export function uniquePidForProcess(event: LegacyEndpointEvent) { | ||
return event.endgame.unique_pid; | ||
export function uniquePidForProcess(event: ResolverEvent) { | ||
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. Could you add an explicit return type here |
||
if (isLegacyEvent(event)) { | ||
return String(event.endgame.unique_pid); | ||
} else { | ||
return event.endpoint.process.entity_id; | ||
} | ||
} | ||
|
||
/** | ||
* Returns the process event's parent pid | ||
*/ | ||
export function uniqueParentPidForProcess(event: LegacyEndpointEvent) { | ||
return event.endgame.unique_ppid; | ||
export function uniqueParentPidForProcess(event: ResolverEvent) { | ||
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. Could you add an explicit return type here |
||
if (isLegacyEvent(event)) { | ||
return String(event.endgame.unique_ppid); | ||
} else { | ||
return event.endpoint.process.parent?.entity_id; | ||
} | ||
} |
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.
❔ At a glance, we have three things (
category
,type
, and nowkind
) that are basically perfect synonyms. Can we do something to make this type more readable or useful? Either changing fromstring
to the things that can actually be there, or documenting the differences?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.
@bkimmel I can link you to the docs that describe the differences. We could use enums I suppose if we wanted but we'd have to update them as ecs changes. I think it'd probably go better the document the differences route.
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.
@bkimmel long term we need a way to keep these types in sync w/ ECS. Part of that should be getting descriptions of each field. In the meantime, getting comments on each field here is on my radar.