Skip to content
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

Merge/restyle nodes table #69098

Merged
merged 82 commits into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
39b3aa6
linted rebase
Jun 12, 2020
3596269
remove unused const
Jun 12, 2020
ab02953
Merge remote-tracking branch 'upstream/master' into merge/restyle-nod…
Jun 13, 2020
94b0125
fixing upstream conflicts
Jun 13, 2020
ebaa753
fix import
Jun 13, 2020
40ae3d7
Merge remote-tracking branch 'upstream/master' into merge/restyle-nod…
Jun 13, 2020
867b977
fix import / conflicts
Jun 13, 2020
23f8e6a
fix asset bug with process detail
Jun 13, 2020
b3c542f
fix conflicts: List related by type
Jun 13, 2020
5d096be
repair conflicts: related detail
Jun 13, 2020
10ee8ec
style conflict
Jun 13, 2020
0fa1f07
style touchups
Jun 13, 2020
3d16dab
add counts
Jun 13, 2020
23837dc
linting
Jun 13, 2020
8f6a903
delete unused view
Jun 13, 2020
74e9bec
fixing deps
Jun 13, 2020
680b152
lint
Jun 13, 2020
67eb433
that wasn't really a selector
Jun 14, 2020
f13aa66
comments and fixes
Jun 14, 2020
e3047dc
theming adjustments
Jun 14, 2020
e5ea5af
consistent counts
Jun 14, 2020
3f7a70f
lint
Jun 14, 2020
e618094
i18n domain fixes
Jun 14, 2020
877d168
React key warnings
Jun 14, 2020
d4202e9
fixing names
Jun 15, 2020
0ee1886
J. Buttner review: name changes for process
Jun 15, 2020
499d942
J Buttner review: annotate return type
Jun 15, 2020
03da2ea
lint
Jun 15, 2020
90a9fc1
M Olorunnisola review: change prop name to be more clear
Jun 15, 2020
5824100
M Olorunnisola review: fix idFromParams to be more radable
Jun 15, 2020
be839a2
M Olorunnisola review: more reable descriptiveName
Jun 15, 2020
96732ae
M Olorunnisola review: parameter readability in middleware
Jun 15, 2020
0548b07
J Buttner review: set domain as optional type
Jun 15, 2020
bef40b6
R Austin review: does not comport
Jun 15, 2020
6772323
J Buttner review: remove unnecessary pid generation
Jun 15, 2020
ee1f0e6
R Austin review: Use broader i18n input
Jun 15, 2020
7c7fd1f
R Austin review: i18n on list comp.
Jun 15, 2020
06c5896
Merge branch 'master' into merge/restyle-nodes-table
elasticmachine Jun 15, 2020
48347d1
R Austin review: remove stray line
Jun 16, 2020
54a451f
R Austin review: move check inside middleware
Jun 16, 2020
8b4a3b3
R Austin review: use String() instead
Jun 16, 2020
951ef24
R Austin review: return number instead
Jun 16, 2020
efb24f1
R Austin review: format compound message
Jun 16, 2020
532233a
R Austin review: recomport for readability
Jun 16, 2020
ef9b028
R Austin review: name to primaryEventCategory
Jun 17, 2020
cf1fff0
R Austin review: move formatting out of model
Jun 17, 2020
1659805
R Austin review: chip shot at model data
Jun 17, 2020
6ca1004
R Austin review: remove unnecessary guard
Jun 17, 2020
81a865d
R Austin review: prefer const
Jun 17, 2020
a7cdb88
R Austin review: reorganize/rename actions
Jun 17, 2020
2445aba
K Qualters review: fix pid/ppid references
Jun 17, 2020
28c8e92
R Austin review: decouple view selection from render
Jun 17, 2020
8862823
Bug fix: No undefined siblings
Jun 17, 2020
feeadf3
Bug fix: reference error
Jun 17, 2020
fd47388
R Austin review: use App- actions to sync state
Jun 17, 2020
07f9082
change descriptive name code to use deep partial
Jun 17, 2020
66ab251
TS type
Jun 17, 2020
0ca4fb1
K Qualters review: fix pid/ppid again
Jun 17, 2020
6d76057
R Austin review: fix ecsEventType
Jun 17, 2020
89bd6a8
R Austin review: refactor state change
Jun 17, 2020
1058e68
R Austin review: new action for missing relateds
Jun 17, 2020
318eba2
R Austin review: Add comments to exports
Jun 17, 2020
6c20672
R Austin review: Comment on aggregate totals
Jun 17, 2020
7a0c375
R Austin review: export BoldCode
Jun 17, 2020
a75d74d
R Austin review: add comment to description list helper
Jun 17, 2020
436ae12
R Austin review: debride event model function
Jun 17, 2020
7229e50
CI: Typecheck
Jun 17, 2020
cc5858a
R Austin review: allow 0 as pid
Jun 18, 2020
ef3a539
R Austin review: remove EndpointEvent cast
Jun 18, 2020
074bfc3
R Austin review: allow 0 as parentPid
Jun 18, 2020
44a4d56
R Austin review: simplify ppid return
Jun 18, 2020
531070a
R Austin review: make processPid consistent
Jun 18, 2020
56db019
test file got lost in rebase
Jun 18, 2020
b203e41
fix test
Jun 18, 2020
35e327c
Merge branch 'master' into merge/restyle-nodes-table
elasticmachine Jun 18, 2020
7cc3d72
CI: typecheck events
Jun 18, 2020
4fef503
CI: typecheck fix
Jun 18, 2020
ed73b46
R Austin review: spelling in translation titles
Jun 18, 2020
14db86c
R Austin review: spelling / i18n
Jun 18, 2020
03476a9
CI: dup i18n title
Jun 18, 2020
1c76b99
CI: dup i18n title
Jun 18, 2020
1f05fdd
CI: derped the name
Jun 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions x-pack/plugins/security_solution/common/endpoint/generate_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ interface EventOptions {
eventType?: string;
eventCategory?: string | string[];
processName?: string;
pid?: number;
parentPid?: number;
extensions?: object;
oatkiller marked this conversation as resolved.
Show resolved Hide resolved
}

const Windows: HostOS[] = [
Expand Down Expand Up @@ -452,12 +455,36 @@ export class EndpointDocGenerator {
* @param options - Allows event field values to be specified
*/
public generateEvent(options: EventOptions = {}): EndpointEvent {
const processName = options.processName ? options.processName : randomProcessName();
const detailRecordForEventType =
oatkiller marked this conversation as resolved.
Show resolved Hide resolved
options.extensions ||
((eventCategory) => {
if (eventCategory === 'registry') {
return { registry: { key: `HKLM/Windows/Software/${this.randomString(5)}` } };
}
if (eventCategory === 'network') {
return {
network: {
direction: this.randomChoice(['inbound', 'outbound']),
forwarded_ip: `${this.randomIP()}`,
},
};
}
if (eventCategory === 'file') {
return { file: { path: 'C:\\My Documents\\business\\January\\processName' } };
}
if (eventCategory === 'dns') {
return { dns: { question: { name: `${this.randomIP()}` } } };
}
return {};
})(options.eventCategory);
return {
'@timestamp': options.timestamp ? options.timestamp : new Date().getTime(),
agent: { ...this.commonInfo.agent, type: 'endpoint' },
ecs: {
version: '1.4.0',
},
...detailRecordForEventType,
event: {
category: options.eventCategory ? options.eventCategory : 'process',
kind: 'event',
Expand All @@ -466,9 +493,30 @@ export class EndpointDocGenerator {
},
host: this.commonInfo.host,
process: {
pid:
'pid' in options && typeof options.pid !== 'undefined' ? options.pid : this.randomN(5000),
executable: `C:\\${processName}`,
args: `"C:\\${processName}" \\${this.randomString(3)}`,
code_signature: {
status: 'trusted',
subject_name: 'Microsoft',
},
hash: { md5: this.seededUUIDv4() },
entity_id: options.entityID ? options.entityID : this.randomString(10),
parent: options.parentEntityID ? { entity_id: options.parentEntityID } : undefined,
name: options.processName ? options.processName : randomProcessName(),
parent: options.parentEntityID
? {
entity_id: options.parentEntityID,
pid:
'parentPid' in options && typeof options.parentPid !== 'undefined'
? options.parentPid
: this.randomN(5000),
}
: undefined,
name: processName,
},
user: {
domain: this.randomString(10),
name: this.randomString(10),
},
};
}
Expand Down Expand Up @@ -701,6 +749,8 @@ export class EndpointDocGenerator {
ancestor = this.generateEvent({
timestamp,
parentEntityID: ancestor.process.entity_id,
parentPid: ancestor.process.pid,
pid: this.randomN(5000),
});
events.push(ancestor);
timestamp = timestamp + 1000;
Expand Down Expand Up @@ -1126,7 +1176,7 @@ export class EndpointDocGenerator {
return [...this.randomNGenerator(255, 6)].map((x) => x.toString(16)).join('-');
}

private randomIP(): string {
public randomIP(): string {
oatkiller marked this conversation as resolved.
Show resolved Hide resolved
return [10, ...this.randomNGenerator(255, 3)].map((x) => x.toString()).join('.');
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 { EndpointDocGenerator } from '../generate_data';
import { descriptiveName } from './event';

describe('Event descriptive names', () => {
let generator: EndpointDocGenerator;
beforeEach(() => {
generator = new EndpointDocGenerator('seed');
});

it('returns the right name for a registry event', () => {
const extensions = { registry: { key: `HKLM/Windows/Software/abc` } };
const event = generator.generateEvent({ eventCategory: 'registry', extensions });
expect(descriptiveName(event)).toEqual({ subject: `HKLM/Windows/Software/abc` });
});

it('returns the right name for a network event', () => {
const randomIP = `${generator.randomIP()}`;
const extensions = { network: { direction: 'outbound', forwarded_ip: randomIP } };
const event = generator.generateEvent({ eventCategory: 'network', extensions });
expect(descriptiveName(event)).toEqual({ subject: `${randomIP}`, descriptor: 'outbound' });
});

it('returns the right name for a file event', () => {
const extensions = { file: { path: 'C:\\My Documents\\business\\January\\processName' } };
const event = generator.generateEvent({ eventCategory: 'file', extensions });
expect(descriptiveName(event)).toEqual({
subject: 'C:\\My Documents\\business\\January\\processName',
});
});

it('returns the right name for a dns event', () => {
const extensions = { dns: { question: { name: `${generator.randomIP()}` } } };
const event = generator.generateEvent({ eventCategory: 'dns', extensions });
expect(descriptiveName(event)).toEqual({ subject: extensions.dns.question.name });
});
});
92 changes: 92 additions & 0 deletions x-pack/plugins/security_solution/common/endpoint/models/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,95 @@ export function parentEntityId(event: ResolverEvent): string | undefined {
}
return event.process.parent?.entity_id;
}

oatkiller marked this conversation as resolved.
Show resolved Hide resolved
/**
* @param event The event to get the category for
*/
export function primaryEventCategory(event: ResolverEvent): string | undefined {
// Returning "Process" as a catch-all here because it seems pretty general
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;
}
}

/**
* 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;
}

/**
* #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.
*/
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 } {
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) };
}

// Fall back on entityId if we can't fish a more descriptive name out.
return { subject: entityId(event) };
}
24 changes: 24 additions & 0 deletions x-pack/plugins/security_solution/common/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,14 +438,38 @@ export interface EndpointEvent {
kind: string;
};
host: Host;
network?: {
direction: unknown;
forwarded_ip: unknown;
};
dns?: {
question: { name: unknown };
};
process: {
entity_id: string;
name: string;
executable: string;
args: string;
code_signature: {
status: string;
subject_name: string;
};
pid: number;
hash: {
md5: string;
};
parent?: {
entity_id: string;
name?: string;
pid: number;
};
};
user?: {
domain?: string;
name: string;
};
file?: { path: unknown };
registry?: { path: unknown; key: unknown };
}

export type ResolverEvent = EndpointEvent | LegacyEndpointEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,39 @@ import {
} from '../../../common/endpoint_alerts/types';
import { ImmutableMiddlewareFactory } from '../../common/store';
import { cloneHttpFetchQuery } from '../../common/utils/clone_http_fetch_query';

bkimmel marked this conversation as resolved.
Show resolved Hide resolved
import {
isOnAlertPage,
apiQueryParams,
hasSelectedAlert,
uiQueryParams,
hasSelectedAlert,
isAlertPageTabChange,
} from './selectors';

export const alertMiddlewareFactory: ImmutableMiddlewareFactory<AlertListState> = (
coreStart,
depsStart
) => {
let lastSelectedAlert: string | null = null;
/**
* @returns <boolean> true once per change of `selectedAlert` in query params.
*
* As opposed to `hasSelectedAlert` which always returns true if the alert is present
* query params, which can cause unnecessary requests and re-renders in some cases.
*/
const selectedAlertHasChanged = (params: ReturnType<typeof uiQueryParams>): boolean => {
const { selected_alert: selectedAlert } = params;
const shouldNotChange = selectedAlert === lastSelectedAlert;
if (shouldNotChange) {
return false;
}
if (typeof selectedAlert !== 'string') {
return false;
}
lastSelectedAlert = selectedAlert;
return true;
};

async function fetchIndexPatterns(): Promise<IIndexPattern[]> {
const { indexPatterns } = depsStart.data;
const fields = await indexPatterns.getFieldsForWildcard({
Expand All @@ -50,7 +71,7 @@ export const alertMiddlewareFactory: ImmutableMiddlewareFactory<AlertListState>
});
api.dispatch({ type: 'serverReturnedAlertsData', payload: listResponse });

if (hasSelectedAlert(state)) {
if (hasSelectedAlert(state) && selectedAlertHasChanged(uiQueryParams(state))) {
const uiParams = uiQueryParams(state);
const detailsResponse: AlertDetails = await coreStart.http.get(
`/api/endpoint/alerts/${uiParams.selected_alert}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ export function factory(processes: ResolverEvent[]): IndexedProcessTree {
currentProcessAdjacencyMap.parent = uniqueParentPid;
}
} else {
idToChildren.set(uniqueParentPid, [process]);

if (uniqueParentPid) {
idToChildren.set(uniqueParentPid, [process]);
/**
* Get the parent's map, otherwise set an empty one
*/
Expand Down
Loading