-
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 all 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 |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* 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 { EndpointEvent, LegacyEndpointEvent } from '../types'; | ||
|
||
export function isLegacyEvent( | ||
event: EndpointEvent | LegacyEndpointEvent | ||
): event is LegacyEndpointEvent { | ||
return (event as LegacyEndpointEvent).endgame !== undefined; | ||
} | ||
|
||
export function eventTimestamp( | ||
event: EndpointEvent | LegacyEndpointEvent | ||
): string | undefined | number { | ||
if (isLegacyEvent(event)) { | ||
return event.endgame.timestamp_utc; | ||
} else { | ||
return event['@timestamp']; | ||
} | ||
} | ||
|
||
export function eventName(event: EndpointEvent | LegacyEndpointEvent): string { | ||
if (isLegacyEvent(event)) { | ||
return event.endgame.process_name ? event.endgame.process_name : ''; | ||
} else { | ||
return event.process.name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -311,8 +311,8 @@ export interface EndpointEvent { | |
version: string; | ||
}; | ||
event: { | ||
category: string; | ||
type: string; | ||
category: string | string[]; | ||
type: string | string[]; | ||
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. when would category and type be string arrays? 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'm not sure exactly when this is the case, I believe some events can have more than one of each in the case where it was hard to bucket into exactly one existing ECS value. @jonathan-buttner could probably tell you in more detail, this comes from kqualters-elastic#1 |
||
id: string; | ||
kind: string; | ||
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 a glance, we have three things ( 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. @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 commentThe 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. |
||
}; | ||
|
@@ -328,6 +328,7 @@ export interface EndpointEvent { | |
name: string; | ||
parent?: { | ||
entity_id: string; | ||
name?: string; | ||
}; | ||
}; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
* 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 { | ||
|
@@ -19,87 +20,104 @@ import * as selectors from '../../../../store/alerts/selectors'; | |
import { MetadataPanel } from './metadata_panel'; | ||
import { FormattedDate } from '../../formatted_date'; | ||
import { AlertDetailResolver } from '../../resolver'; | ||
import { ResolverEvent } from '../../../../../../../common/types'; | ||
import { TakeActionDropdown } from './take_action_dropdown'; | ||
|
||
export const AlertDetailsOverview = memo(() => { | ||
const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); | ||
if (alertDetailsData === undefined) { | ||
return null; | ||
} | ||
const selectedAlertIsLegacyEndpointEvent = useAlertListSelector( | ||
selectors.selectedAlertIsLegacyEndpointEvent | ||
); | ||
export const AlertDetailsOverview = styled( | ||
memo(() => { | ||
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: EuiTabbedContentTab[] = useMemo(() => { | ||
return [ | ||
{ | ||
id: 'overviewMetadata', | ||
'data-test-subj': '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: EuiTabbedContentTab[] = useMemo(() => { | ||
return [ | ||
{ | ||
id: 'overviewMetadata', | ||
'data-test-subj': 'overviewMetadata', | ||
name: i18n.translate( | ||
'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview', | ||
{ | ||
defaultMessage: 'Overview', | ||
} | ||
), | ||
content: ( | ||
<> | ||
<EuiSpacer /> | ||
<MetadataPanel /> | ||
</> | ||
), | ||
}, | ||
{ | ||
id: 'overviewResolver', | ||
'data-test-subj': 'overviewResolverTab', | ||
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> | ||
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"> | ||
{' '} | ||
<FormattedMessage | ||
id="xpack.endpoint.application.endpoint.alertDetails.endpoint.status.online" | ||
defaultMessage="Online" | ||
/> | ||
</EuiHealth> | ||
</EuiText> | ||
<EuiText> | ||
{' '} | ||
<FormattedMessage | ||
id="xpack.endpoint.application.endpoint.alertDetails.overview.title" | ||
defaultMessage="Detected Malicious File" | ||
id="xpack.endpoint.application.endpoint.alertDetails.alert.status.open" | ||
defaultMessage="Alert Status: Open" | ||
/> | ||
</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 /> | ||
<TakeActionDropdown /> | ||
<EuiSpacer /> | ||
</section> | ||
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} /> | ||
</> | ||
); | ||
}); | ||
</EuiText> | ||
<EuiSpacer /> | ||
<TakeActionDropdown /> | ||
<EuiSpacer /> | ||
</section> | ||
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} /> | ||
</> | ||
); | ||
}) | ||
)` | ||
height: 100%; | ||
width: 100%; | ||
`; |
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)!; | ||
} | ||
|
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.
❔ Should this stuff get typed as
readonly
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.
my suggestion is to leave the types in
types.ts
as plain as possible. this way, you could use them when constructing aEndpointEvent
. If you need a readonly (or partial, or deepreadonly, or deeppartial, etc) type, you can use a generic type for it. e.g.function takesReadonlyEvent(event: DeepReadonly<EndpointEvent>) {