-
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
[Resolver] Refactoring panel view #77928
Changes from all commits
fc9577e
62deec4
3841c7b
ef56595
3da7c42
29da625
28606f2
6c6387f
3c66a9d
9547a4d
d229fb2
e7a995c
9c0fbfc
bc06056
76cd15b
fcafdfe
0af4790
83e3f0c
e5e8a9f
23434f8
9558676
55caf5b
7e349d5
76addca
41711b7
c0ab9ed
5960e4d
244e5e9
29392ee
ad81c43
2681c0f
3fbc1b0
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 |
---|---|---|
|
@@ -104,18 +104,73 @@ export function timestampAsDateSafeVersion(event: TimestampFields): Date | undef | |
} | ||
} | ||
|
||
export function eventTimestamp(event: ResolverEvent): string | undefined | number { | ||
return event['@timestamp']; | ||
export function eventTimestamp(event: SafeResolverEvent): string | undefined | number { | ||
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. Converted this (among many things) to use safe types. |
||
return firstNonNullValue(event['@timestamp']); | ||
} | ||
|
||
export function eventName(event: ResolverEvent): string { | ||
/** | ||
* Find the name of the related process. | ||
*/ | ||
export function processName(event: ResolverEvent): 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. Named this to |
||
if (isLegacyEvent(event)) { | ||
return event.endgame.process_name ? event.endgame.process_name : ''; | ||
} else { | ||
return event.process.name; | ||
} | ||
} | ||
|
||
/** | ||
* First non-null value in the `user.name` field. | ||
*/ | ||
export function userName(event: SafeResolverEvent): string | undefined { | ||
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. The view was reading this directly. |
||
if (isLegacyEventSafeVersion(event)) { | ||
return undefined; | ||
} else { | ||
return firstNonNullValue(event.user?.name); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the process event's parent PID | ||
*/ | ||
export function parentPID(event: SafeResolverEvent): number | undefined { | ||
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. The view was reading this directly. |
||
return firstNonNullValue( | ||
isLegacyEventSafeVersion(event) ? event.endgame.ppid : event.process?.parent?.pid | ||
); | ||
} | ||
|
||
/** | ||
* First non-null value for the `process.hash.md5` field. | ||
*/ | ||
export function md5HashForProcess(event: SafeResolverEvent): string | undefined { | ||
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. The view was reading this directly. |
||
return firstNonNullValue(isLegacyEventSafeVersion(event) ? undefined : event.process?.hash?.md5); | ||
} | ||
|
||
/** | ||
* First non-null value for the `event.process.args` field. | ||
*/ | ||
export function argsForProcess(event: SafeResolverEvent): string | undefined { | ||
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. The view was reading this directly. |
||
if (isLegacyEventSafeVersion(event)) { | ||
// There is not currently a key for this on Legacy event types | ||
return undefined; | ||
} | ||
return firstNonNullValue(event.process?.args); | ||
} | ||
|
||
/** | ||
* First non-null value in the `user.name` field. | ||
*/ | ||
export function userDomain(event: SafeResolverEvent): string | undefined { | ||
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. The view was reading this directly. |
||
if (isLegacyEventSafeVersion(event)) { | ||
return undefined; | ||
} else { | ||
return firstNonNullValue(event.user?.domain); | ||
} | ||
} | ||
|
||
/** | ||
* Find the name of the related process. | ||
*/ | ||
export function processNameSafeVersion(event: SafeResolverEvent): string | undefined { | ||
if (isLegacyEventSafeVersion(event)) { | ||
return firstNonNullValue(event.endgame.process_name); | ||
|
@@ -124,11 +179,10 @@ export function processNameSafeVersion(event: SafeResolverEvent): string | undef | |
} | ||
} | ||
|
||
export function eventId(event: ResolverEvent): number | undefined | string { | ||
if (isLegacyEvent(event)) { | ||
return event.endgame.serial_event_id; | ||
} | ||
return event.event.id; | ||
export function eventID(event: SafeResolverEvent): number | undefined | 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. spelling and type safety 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. ❔ needs a doc comment? |
||
return firstNonNullValue( | ||
isLegacyEventSafeVersion(event) ? event.endgame.serial_event_id : event.event?.id | ||
); | ||
} | ||
|
||
/** | ||
|
@@ -275,18 +329,14 @@ export function ancestryArray(event: AncestryArrayFields): string[] | undefined | |
/** | ||
* Minimum fields needed from the `SafeResolverEvent` type for the function below to operate correctly. | ||
*/ | ||
type GetAncestryArrayFields = AncestryArrayFields & ParentEntityIDFields; | ||
type AncestryFields = AncestryArrayFields & ParentEntityIDFields; | ||
|
||
/** | ||
* Returns an array of strings representing the ancestry for a process. | ||
* | ||
* @param event an ES document | ||
*/ | ||
export function getAncestryAsArray(event: GetAncestryArrayFields | undefined): string[] { | ||
if (!event) { | ||
return []; | ||
} | ||
|
||
export function ancestry(event: AncestryFields): 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. changed name to match convention. no longer accepts undefined. |
||
const ancestors = ancestryArray(event); | ||
if (ancestors) { | ||
return ancestors; | ||
|
@@ -300,107 +350,33 @@ export function getAncestryAsArray(event: GetAncestryArrayFields | undefined): s | |
return []; | ||
} | ||
|
||
/** | ||
* @param event The event to get the category for | ||
*/ | ||
export function primaryEventCategory(event: ResolverEvent): string | undefined { | ||
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. Removed this concept entirely |
||
if (isLegacyEvent(event)) { | ||
const legacyFullType = event.endgame.event_type_full; | ||
if (legacyFullType) { | ||
return legacyFullType; | ||
} | ||
} else { | ||
const eventCategories = event.event.category; | ||
const category = typeof eventCategories === 'string' ? eventCategories : eventCategories[0]; | ||
|
||
return category; | ||
} | ||
} | ||
|
||
/** | ||
* @param event The event to get the full ECS category for | ||
*/ | ||
export function allEventCategories(event: ResolverEvent): string | string[] | undefined { | ||
if (isLegacyEvent(event)) { | ||
const legacyFullType = event.endgame.event_type_full; | ||
if (legacyFullType) { | ||
return legacyFullType; | ||
} | ||
} else { | ||
return event.event.category; | ||
} | ||
export function eventCategory(event: SafeResolverEvent): 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. This returns all event event categories now. 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. ❔ Should we call it event categor_ies_ (plural)? Because it could be many or stick with categor_y_ b/c ECS? 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. Not sure. Any opinion on that? 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. Plural makes sense, but following the ECS paradigm is probably best imo. Just makes it easier to tie it back 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. Regardless of the name, the TS type indicates that it returns an array. I think we can defer this decision. |
||
return values( | ||
isLegacyEventSafeVersion(event) ? event.endgame.event_type_full : event.event?.category | ||
); | ||
} | ||
|
||
/** | ||
* ECS event type will be things like 'creation', 'deletion', 'access', etc. | ||
* see: https://www.elastic.co/guide/en/ecs/current/ecs-event.html | ||
* @param event The ResolverEvent to get the ecs type for | ||
*/ | ||
export function ecsEventType(event: ResolverEvent): Array<string | undefined> { | ||
if (isLegacyEvent(event)) { | ||
return [event.endgame.event_subtype_full]; | ||
} | ||
return typeof event.event.type === 'string' ? [event.event.type] : event.event.type; | ||
export function eventType(event: SafeResolverEvent): 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. renamed from |
||
return values( | ||
isLegacyEventSafeVersion(event) ? event.endgame.event_subtype_full : event.event?.type | ||
); | ||
} | ||
|
||
/** | ||
* #Descriptive Names For Related Events: | ||
* | ||
* The following section provides facilities for deriving **Descriptive Names** for ECS-compliant event data. | ||
* There are drawbacks to trying to do this: It *will* require ongoing maintenance. It presents temptations to overarticulate. | ||
* On balance, however, it seems that the benefit of giving the user some form of information they can recognize & scan outweighs the drawbacks. | ||
* event.kind as an array. | ||
*/ | ||
type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T; | ||
/** | ||
* Based on the ECS category of the event, attempt to provide a more descriptive name | ||
* (e.g. the `event.registry.key` for `registry` or the `dns.question.name` for `dns`, etc.). | ||
* This function returns the data in the form of `{subject, descriptor}` where `subject` will | ||
* tend to be the more distinctive term (e.g. 137.213.212.7 for a network event) and the | ||
* `descriptor` can be used to present more useful/meaningful view (e.g. `inbound 137.213.212.7` | ||
* in the example above). | ||
* see: https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html | ||
* @param event The ResolverEvent to get the descriptive name for | ||
* @returns { descriptiveName } An attempt at providing a readable name to the user | ||
*/ | ||
export function descriptiveName(event: ResolverEvent): { subject: string; descriptor?: 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. Moved to a component. |
||
if (isLegacyEvent(event)) { | ||
return { subject: eventName(event) }; | ||
} | ||
|
||
// To be somewhat defensive, we'll check for the presence of these. | ||
const partialEvent: DeepPartial<ResolverEvent> = event; | ||
|
||
/** | ||
* This list of attempts can be expanded/adjusted as the underlying model changes over time: | ||
*/ | ||
|
||
// Stable, per ECS 1.5: https://www.elastic.co/guide/en/ecs/current/ecs-allowed-values-event-category.html | ||
|
||
if (partialEvent.network?.forwarded_ip) { | ||
return { | ||
subject: String(partialEvent.network?.forwarded_ip), | ||
descriptor: String(partialEvent.network?.direction), | ||
}; | ||
} | ||
|
||
if (partialEvent.file?.path) { | ||
return { | ||
subject: String(partialEvent.file?.path), | ||
}; | ||
} | ||
|
||
// Extended categories (per ECS 1.5): | ||
const pathOrKey = partialEvent.registry?.path || partialEvent.registry?.key; | ||
if (pathOrKey) { | ||
return { | ||
subject: String(pathOrKey), | ||
}; | ||
} | ||
|
||
if (partialEvent.dns?.question?.name) { | ||
return { subject: String(partialEvent.dns?.question?.name) }; | ||
export function eventKind(event: SafeResolverEvent): 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. I don't think this was exposed before. 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. ❔ Can 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. My reasoning: ES will accept and possibly return an array and the SIEM is permissive about it. I can bring up this topic with some people around the org, but I think it's something we should be consistent on within all of Elastic. |
||
if (isLegacyEventSafeVersion(event)) { | ||
return []; | ||
} else { | ||
return values(event.event?.kind); | ||
} | ||
|
||
// Fall back on entityId if we can't fish a more descriptive name out. | ||
return { subject: entityId(event) }; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -183,7 +183,7 @@ export interface ResolverTree { | |
relatedEvents: Omit<ResolverRelatedEvents, 'entityID'>; | ||
relatedAlerts: Omit<ResolverRelatedAlerts, 'entityID'>; | ||
ancestry: ResolverAncestry; | ||
lifecycle: ResolverEvent[]; | ||
lifecycle: SafeResolverEvent[]; | ||
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. You'll see a lot of this. We're using the safe version of this type in most places now. In a follow up PR we could probably finish the task (just in time to introduce an even safer version.) |
||
stats: ResolverNodeStats; | ||
} | ||
|
||
|
@@ -209,7 +209,7 @@ export interface SafeResolverTree { | |
*/ | ||
export interface ResolverLifecycleNode { | ||
entityID: string; | ||
lifecycle: ResolverEvent[]; | ||
lifecycle: SafeResolverEvent[]; | ||
/** | ||
* stats are only set when the entire tree is being fetched | ||
*/ | ||
|
@@ -263,7 +263,7 @@ export interface SafeResolverAncestry { | |
*/ | ||
export interface ResolverRelatedEvents { | ||
entityID: string; | ||
events: ResolverEvent[]; | ||
events: SafeResolverEvent[]; | ||
nextEvent: string | null; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,7 @@ export function noAncestorsTwoChildenInIndexCalledAwesomeIndex(): { | |
events: [ | ||
mockEndpointEvent({ | ||
entityID, | ||
name: 'event', | ||
processName: 'event', | ||
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. this field was renamed. |
||
timestamp: 0, | ||
}), | ||
], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ import { Provider } from 'react-redux'; | |
import { ResolverPluginSetup } from './types'; | ||
import { resolverStoreFactory } from './store/index'; | ||
import { ResolverWithoutProviders } from './view/resolver_without_providers'; | ||
import { noAncestorsTwoChildren } from './data_access_layer/mocks/no_ancestors_two_children'; | ||
import { noAncestorsTwoChildrenWithRelatedEventsOnOrigin } from './data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin'; | ||
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. 👍 |
||
|
||
/** | ||
* These exports are used by the plugin 'resolverTest' defined in x-pack's plugin_functional suite. | ||
|
@@ -23,7 +23,7 @@ export function resolverPluginSetup(): ResolverPluginSetup { | |
ResolverWithoutProviders, | ||
mocks: { | ||
dataAccessLayer: { | ||
noAncestorsTwoChildren, | ||
noAncestorsTwoChildrenWithRelatedEventsOnOrigin, | ||
}, | ||
}, | ||
}; | ||
|
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.
Moved these tests to the view layer