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

Add Internal Alert/Signal ID to Endpoint Alert telemetry #126216

Merged
merged 12 commits into from
Mar 3, 2022

Conversation

donaherc
Copy link
Contributor

@donaherc donaherc commented Feb 23, 2022

Summary

This PR Adds the "signal_id" from the created Kibana signal to the raw endpoint alert telemetry documents. This field is needed to ensure that we can properly combine Alert telemetry with user actions on those alerts ("acknowledged", "closed", etc)

Checklist

Delete any items that are not applicable to this PR.

@donaherc donaherc requested review from a team as code owners February 23, 2022 00:14
@donaherc donaherc requested a review from pjhampton February 23, 2022 00:14
Copy link
Contributor Author

@donaherc donaherc left a comment

Choose a reason for hiding this comment

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

Just some context for you @pjhampton

logger,
eventsTelemetry,
enrichedEvents,
createdItems,
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 are the response objects generated after the signals are created.

buildRuleMessage: BuildRuleMessage
) {
if (eventsTelemetry === undefined) {
return;
}
// Create map of ancenstor_id -> alert_id
let signalIdMap = new Map<string, string>();
Copy link
Contributor Author

@donaherc donaherc Feb 23, 2022

Choose a reason for hiding this comment

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

We build a map of <endpoint_alert_id>, <signal_id> so that we can look it up when we send the payload

Copy link
Contributor

@pjhampton pjhampton Feb 23, 2022

Choose a reason for hiding this comment

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

This code isn't pragmatic.

createdEvents.map(function (obj: SignalSource) {
  signalIdMap = signalIdMap.set(String(obj['kibana.alert.original_event.id']), String(obj._id));
  return null;
});

Yields an array of nulls where the length is the number of events. You are also side-effecting unnecessarily and not checking if the ancestor_id exists. A safer and more functional way of writing this code would be:

type AncestorId = string;
type AlertId = string;

const signalIdMap = createdEvents.reduce((signalMap, obj) => {
  const ancestorId = String(obj['kibana.alert.original_event.id']);
  const alertId = String(obj._id);
    
  if (ancestorId !== null && ancestorId !== undefined) {
    signalMap = signalIdMap.set(ancestorId, alertId);
  }
    
  return signalMap;
}, new Map<AncestorId, AlertId>())

This results in 1 array instead of 2. I think I would also like to see a unit test around this function - one where the ancestor id exists and another when it doesn't.

@donaherc donaherc self-assigned this Feb 23, 2022
@donaherc donaherc added auto-backport Deprecated - use backport:version if exact versions are needed v8.1.0 v8.2.0 release_note:skip Skip the PR/issue when compiling release notes Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. labels Feb 23, 2022
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-solution (Team: SecuritySolution)

@donaherc
Copy link
Contributor Author

@elasticmachine merge upstream

@donaherc donaherc force-pushed the alert-telemetry-signal-id branch from 5b68982 to 5d0e015 Compare February 23, 2022 01:54
@donaherc
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Contributor

@pjhampton pjhampton left a comment

Choose a reason for hiding this comment

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

I think the logic is there - but it needs polish. Thanks for the hard work you put into this!

I won't be approving this for 8.1 as it wouldn't be professional to merge this a couple of hours before the last release BC. With the way Elastic ships stack components there are no take backs. I want to observe this code running in staging first and analyze if we have an uptick in telemetry burned - you have made changes to security telemetry hot path. We can merge this next week and backport it into the 8.1 branch if you want this to go out in 8.1.1. You are welcome to get a second opinion of course.

buildRuleMessage: BuildRuleMessage
) {
if (eventsTelemetry === undefined) {
return;
}
// Create map of ancenstor_id -> alert_id
let signalIdMap = new Map<string, string>();
Copy link
Contributor

@pjhampton pjhampton Feb 23, 2022

Choose a reason for hiding this comment

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

This code isn't pragmatic.

createdEvents.map(function (obj: SignalSource) {
  signalIdMap = signalIdMap.set(String(obj['kibana.alert.original_event.id']), String(obj._id));
  return null;
});

Yields an array of nulls where the length is the number of events. You are also side-effecting unnecessarily and not checking if the ancestor_id exists. A safer and more functional way of writing this code would be:

type AncestorId = string;
type AlertId = string;

const signalIdMap = createdEvents.reduce((signalMap, obj) => {
  const ancestorId = String(obj['kibana.alert.original_event.id']);
  const alertId = String(obj._id);
    
  if (ancestorId !== null && ancestorId !== undefined) {
    signalMap = signalIdMap.set(ancestorId, alertId);
  }
    
  return signalMap;
}, new Map<AncestorId, AlertId>())

This results in 1 array instead of 2. I think I would also like to see a unit test around this function - one where the ancestor id exists and another when it doesn't.

// @ts-expect-error @elastic/elasticsearch _source is optional
const sources: TelemetryEvent[] = filteredEvents.hits.hits.map(function (
obj: SearchResultWithSource
): TelemetryEvent {
obj._source.signal_id = signalIdMap.get(obj._source.event.id);
Copy link
Contributor

Choose a reason for hiding this comment

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

You could be doing a lot of unnecessary work here, particularly if the security user isn't using the endpoint package, and using other detection rules. I'm also not sure how I feel about this mutation, particularly because .event.id isn't necessarily read-safe.

It might be intuitive to write this as:

  -> Collect Telemetry Events
  -> Filter for Endpoint Alerts
  -> Enrich with ancestor signal id, default null
  -> return

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 that flow sounds better, I can do that.

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 think "ancestor signal id" is actually confusing/ maybe not semantically correct either since in signal parlance, a detection engine alert can have multiple ancestors (It's an array in the return). Do you think the name signal_id is sufficient or would you prefer another name?

@donaherc donaherc force-pushed the alert-telemetry-signal-id branch 2 times, most recently from 5e6fd29 to 3ba3958 Compare March 1, 2022 15:11
@donaherc donaherc added v8.1.1 and removed v8.1.0 labels Mar 1, 2022
Copy link
Contributor

@pjhampton pjhampton left a comment

Choose a reason for hiding this comment

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

🌔 🚀 ✨ LGTM ✨ 🚀 🌔

@donaherc
Copy link
Contributor Author

donaherc commented Mar 1, 2022

@elasticmachine merge upstream

@donaherc
Copy link
Contributor Author

donaherc commented Mar 2, 2022

Oof @pjhampton my logic to not enrich or queue events if none were created I think broke this test?
https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts#L110

The doc in this test doesn't remotely resemble an endpoint alert, and was just executing eventsTelemetry.queueTelemetryEvents on an empty list I think.

@donaherc
Copy link
Contributor Author

donaherc commented Mar 2, 2022

@elasticmachine merge upstream

1 similar comment
@donaherc
Copy link
Contributor Author

donaherc commented Mar 2, 2022

@elasticmachine merge upstream

@donaherc
Copy link
Contributor Author

donaherc commented Mar 2, 2022

Rerunning because it's flaky.

@donaherc
Copy link
Contributor Author

donaherc commented Mar 2, 2022

@elasticmachine merge upstream

@pjhampton
Copy link
Contributor

@elasticmachine merge upstream

1 similar comment
@donaherc
Copy link
Contributor Author

donaherc commented Mar 2, 2022

@elasticmachine merge upstream

@donaherc donaherc force-pushed the alert-telemetry-signal-id branch from b814eae to ed83033 Compare March 2, 2022 19:06
@donaherc donaherc force-pushed the alert-telemetry-signal-id branch from ed83033 to b223ddd Compare March 2, 2022 19:06
@donaherc
Copy link
Contributor Author

donaherc commented Mar 2, 2022

@elasticmachine merge upstream

@kibana-ci
Copy link
Collaborator

💛 Build succeeded, but was flaky

Test Failures

  • [job] [logs] Default CI Group #18 / apis Machine Learning filters get_filters should return 400 if filterId does not exist

Metrics [docs]

Unknown metric groups

ESLint disabled in files

id before after diff
securitySolution 66 67 +1

Total ESLint disabled count

id before after diff
securitySolution 513 514 +1

History

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

cc @donaherc

@donaherc donaherc merged commit 2f083bf into main Mar 3, 2022
@donaherc donaherc deleted the alert-telemetry-signal-id branch March 3, 2022 00:39
kibanamachine pushed a commit to kibanamachine/kibana that referenced this pull request Mar 3, 2022
)

* Attach the internal signal_id to the endpoint alert to join with insights

* Ensure we forward signal_id field properly

* Don't think we need to explicitly define the field in the top-level since it satisfies the key:string

* Updated unit test to check for signal id enrichment

* Addressed some comments about alert_id enrichment

* Refactored send_telemetry_events to be more performant and idiomatic

* Added test cases with a non-matching enrichment or non-existing enrichment

* Broke some tests that don't assume QueueTelemetryEvents are endpoint.alerts

* my types were still off

* Addressed comments to use more idiomatic 'toString' function

* Fixed 'Cannot access signalIdMap before initialization name' in reduce by instatiating map prior to reduce

Co-authored-by: Kibana Machine <[email protected]>
(cherry picked from commit 2f083bf)
@kibanamachine
Copy link
Contributor

💚 All backports created successfully

Status Branch Result
8.1

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

kibanamachine added a commit that referenced this pull request Mar 3, 2022
…126739)

* Attach the internal signal_id to the endpoint alert to join with insights

* Ensure we forward signal_id field properly

* Don't think we need to explicitly define the field in the top-level since it satisfies the key:string

* Updated unit test to check for signal id enrichment

* Addressed some comments about alert_id enrichment

* Refactored send_telemetry_events to be more performant and idiomatic

* Added test cases with a non-matching enrichment or non-existing enrichment

* Broke some tests that don't assume QueueTelemetryEvents are endpoint.alerts

* my types were still off

* Addressed comments to use more idiomatic 'toString' function

* Fixed 'Cannot access signalIdMap before initialization name' in reduce by instatiating map prior to reduce

Co-authored-by: Kibana Machine <[email protected]>
(cherry picked from commit 2f083bf)

Co-authored-by: Chris Donaher <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-backport Deprecated - use backport:version if exact versions are needed Feature:Telemetry release_note:skip Skip the PR/issue when compiling release notes Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.1.1 v8.2.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants