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

[Security Solution] Use safe type in resolver backend #76969

Merged
merged 12 commits into from
Sep 10, 2020

Conversation

jonathan-buttner
Copy link
Contributor

This PR refactors the backend to use the SafeResolverEvent type to enforce checking for arrays and single values in ECS fields.

A couple places in front end tests had to be switched to casting the type back to ResolverEvent. This is because the resolver generator is now returning the safe type and the front end doesn't use it in all places yet. Once the front end is switched we can remove the casts.

@jonathan-buttner jonathan-buttner added v8.0.0 release_note:skip Skip the PR/issue when compiling release notes Team:Endpoint Data Visibility Team managing the endpoint resolver v7.10.0 labels Sep 8, 2020
@@ -46,12 +46,12 @@ export function values<T>(valueOrCollection: ECSField<T>): T[] {
if (Array.isArray(valueOrCollection)) {
const nonNullValues: T[] = [];
for (const value of valueOrCollection) {
if (value !== null) {
if (value !== null && value !== undefined) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid having [undefined]

);
}

export function timestampSafeVersion(event: SafeResolverEvent): string | undefined | number {
return isLegacyEventSafeVersion(event)
? firstNonNullValue(event.endgame?.timestamp_utc)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't seem to find references to event.endgame.timestamp_utc in the endgame test data we have in es archiver or the endgame schema. evrelay populates @timestamp, so I think it'd be simpler to keep these consistent between the legacy events and endpoint events. If we end up needing to use event.created we can create a new function to retrieve that and do the legacy check there.

@@ -75,11 +67,7 @@ export function timestampAsDateSafeVersion(event: SafeResolverEvent): Date | und
}

export function eventTimestamp(event: ResolverEvent): string | undefined | number {
if (isLegacyEvent(event)) {
return event.endgame.timestamp_utc;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

@@ -105,14 +93,7 @@ export function eventId(event: ResolverEvent): number | undefined | string {
return event.event.id;
}

export function eventSequence(event: ResolverEvent): number | undefined {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend was the only one using this function so I just removed it and switched the name of the safe version.

@@ -112,6 +112,27 @@ export interface ResolverChildNode extends ResolverLifecycleNode {
nextChild?: string | null;
}

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now I'm duplicating all the types that are used between the frontend and backend. The backend uses the safe versions and the frontend will use the unsafe ones for now.

@@ -198,7 +281,7 @@ export interface SafeResolverRelatedEvents {
*/
export interface ResolverRelatedAlerts {
entityID: string;
alerts: ResolverEvent[];
alerts: SafeResolverEvent[];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The frontend isn't referencing the related alerts yet so switching it over to use the safe version.

executable: string;
start: number;
thread?: ThreadFields[];
export type AlertEvent = Partial<{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping everything in ECSField and removing the duplication between this type and SafeEndpointEvent since we'll just combine them

@@ -578,7 +642,7 @@ export type ResolverEvent = EndpointEvent | LegacyEndpointEvent;
* All mappings in Elasticsearch support arrays. They can also return null values or be missing. For example, a `keyword` mapping could return `null` or `[null]` or `[]` or `'hi'`, or `['hi', 'there']`. We need to handle these cases in order to avoid throwing an error.
* When dealing with an value that comes from ES, wrap the underlying type in `ECSField`. For example, if you have a `keyword` or `text` value coming from ES, cast it to `ECSField<string>`.
*/
export type ECSField<T> = T | null | Array<T | null>;
export type ECSField<T> = T | null | undefined | Array<T | null>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to add undefined here even though ES won't ever return that. This avoids types errors that TS was giving me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This is safe since we want to account for the case of missing fields.

@@ -212,6 +212,10 @@ Object {
},
Object {
"metadata": Object {
"elapsedTime": Object {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changed because the timestampSafeVersion now retrieves the @timestamp field on the legacy event type as well. Previously, we were the isometric code was receiving an empty string which resulted in null be used for this field so it was excluded from the metadata.

@jonathan-buttner jonathan-buttner marked this pull request as ready for review September 8, 2020 20:12
@elasticmachine
Copy link
Contributor

Pinging @elastic/endpoint-data-visibility-team (Team:Endpoint Data Visibility)

Copy link
Contributor

@oatkiller oatkiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Thank you so much.

My only request is to have any TODO comments removed before merge. I only noticed one and I commented on it.

@@ -420,7 +434,7 @@ describe('data generator', () => {
}
}
// The root node must be first in the array or this fails
return tree[events[0].process.entity_id];
return tree[entityIDSafeVersion(events[0]) ?? ''];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think '' would be correct here. Maybe this instead?

Suggested change
return tree[entityIDSafeVersion(events[0]) ?? ''];
const entityID = entityIDSafeVersion(events[0])
if (entityID === undefined) {
// this should never happen.
throw new Error()
}
return tree[entityIDSafeVersion(events[0])];

Or this?

Suggested change
return tree[entityIDSafeVersion(events[0]) ?? ''];
return tree[entityIDSafeVersion(events[0])!];

I'm not that familiar with the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good point, I'll throw an error like you suggested 👍


describe('Generated documents', () => {
let generator: EndpointDocGenerator;
beforeEach(() => {
generator = new EndpointDocGenerator('seed');
});

// TODO what should I do about the cast? We can create a new safe descriptive name but
// the front end won't use that until the rest of it is converted
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cast is fine. This code doesn't belong in the model anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove this comment before merging? ty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh oops, thanks for catching that.

@@ -578,7 +642,7 @@ export type ResolverEvent = EndpointEvent | LegacyEndpointEvent;
* All mappings in Elasticsearch support arrays. They can also return null values or be missing. For example, a `keyword` mapping could return `null` or `[null]` or `[]` or `'hi'`, or `['hi', 'there']`. We need to handle these cases in order to avoid throwing an error.
* When dealing with an value that comes from ES, wrap the underlying type in `ECSField`. For example, if you have a `keyword` or `text` value coming from ES, cast it to `ECSField<string>`.
*/
export type ECSField<T> = T | null | Array<T | null>;
export type ECSField<T> = T | null | undefined | Array<T | null>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This is safe since we want to account for the case of missing fields.

@@ -40,7 +41,9 @@ describe('Resolver Data Middleware', () => {
// Generate a 'tree' using the Resolver generator code. This structure isn't the same as what the API returns.
const baseTree = generateBaseTree();
const tree = mockResolverTree({
events: baseTree.allEvents,
// Casting here because the generator returns the SafeResolverEvent type which isn't yet compatible with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Created this issue to handle the front end side of things: https://github.com/elastic/security-team/issues/239

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice, thanks Mike!

@jonathan-buttner
Copy link
Contributor Author

@elasticmachine merge upstream

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Build metrics

async chunks size

id value diff baseline
securitySolution 10.0MB -330.0B 10.0MB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@jonathan-buttner jonathan-buttner merged commit e7b02d0 into elastic:master Sep 10, 2020
@jonathan-buttner jonathan-buttner deleted the use-safe-type branch September 10, 2020 18:26
jonathan-buttner added a commit to jonathan-buttner/kibana that referenced this pull request Sep 10, 2020
* Moving generator to safe type version

* Finished generator and alert

* Gzipping again

* Finishing type conversions for backend

* Trying to cast front end tests back to unsafe type for now

* Working reducer tests

* Adding more comments and fixing alert type

* Restoring resolver test data

* Updating snapshot with timestamp info

* Removing todo and fixing test

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
jonathan-buttner added a commit that referenced this pull request Sep 10, 2020
* Moving generator to safe type version

* Finished generator and alert

* Gzipping again

* Finishing type conversions for backend

* Trying to cast front end tests back to unsafe type for now

* Working reducer tests

* Adding more comments and fixing alert type

* Restoring resolver test data

* Updating snapshot with timestamp info

* Removing todo and fixing test

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
jloleysens added a commit to jloleysens/kibana that referenced this pull request Sep 14, 2020
…s-for-710

* 'master' of github.com:elastic/kibana: (65 commits)
  Separate url forwarding logic and legacy services (elastic#76892)
  Bump yargs-parser to v13.1.2+ (elastic#77009)
  [Ingest Manager] Shared Fleet agent policy action (elastic#76013)
  [Search] Re-add support for aborting when a connection is closed (elastic#76470)
  [Search] Remove long-running query pop-up (elastic#75385)
  [Monitoring] Fix UI error when alerting is not available (elastic#77179)
  do not log plugin id format warning in dist mode (elastic#77134)
  [ML] Improving client side error handling (elastic#76743)
  [Alerting][Connectors] Refactor IBM Resilient: Generic Implementation (phase one) (elastic#74357)
  [Docs] some basic searchsource api docs (elastic#77038)
  add  cGroupOverrides to the legacy config (elastic#77180)
  Change saved object bulkUpdate to work across multiple namespaces (elastic#75478)
  [Security Solution][Resolver] Replace Selectable popover with badges (elastic#76997)
  Removing ml-state index from archive (elastic#77143)
  [Security Solution] Add unit tests for histograms (elastic#77081)
  [Lens] Filters aggregation  (elastic#75635)
  [Enterprise Search] Update WS Overview logic to use new config data (elastic#77122)
  Cleanup type output before building new types (elastic#77211)
  [Security Solution] Use safe type in resolver backend (elastic#76969)
  Use proper lodash syntax (elastic#77105)
  ...

# Conflicts:
#	x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/node_allocation.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release_note:skip Skip the PR/issue when compiling release notes Team:Endpoint Data Visibility Team managing the endpoint resolver v7.10.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants