-
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
Add Internal Alert/Signal ID to Endpoint Alert telemetry #126216
Conversation
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.
Just some context for you @pjhampton
logger, | ||
eventsTelemetry, | ||
enrichedEvents, | ||
createdItems, |
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.
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>(); |
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.
We build a map of <endpoint_alert_id>
, <signal_id>
so that we can look it up when we send the payload
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.
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.
x-pack/plugins/security_solution/server/lib/telemetry/filters.ts
Outdated
Show resolved
Hide resolved
Pinging @elastic/security-solution (Team: SecuritySolution) |
@elasticmachine merge upstream |
5b68982
to
5d0e015
Compare
@elasticmachine merge upstream |
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.
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>(); |
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.
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); |
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.
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
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.
Yeah that flow sounds better, I can do that.
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.
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?
.../plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.test.ts
Show resolved
Hide resolved
5e6fd29
to
3ba3958
Compare
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.
🌔 🚀 ✨ LGTM ✨ 🚀 🌔
@elasticmachine merge upstream |
Oof @pjhampton my logic to not enrich or queue events if none were created I think broke this test? The doc in this test doesn't remotely resemble an endpoint alert, and was just executing |
@elasticmachine merge upstream |
1 similar comment
@elasticmachine merge upstream |
Rerunning because it's flaky. |
@elasticmachine merge upstream |
x-pack/plugins/security_solution/server/lib/detection_engine/signals/send_telemetry_events.ts
Outdated
Show resolved
Hide resolved
@elasticmachine merge upstream |
1 similar comment
@elasticmachine merge upstream |
b814eae
to
ed83033
Compare
…ince it satisfies the key:string
…e by instatiating map prior to reduce
ed83033
to
b223ddd
Compare
@elasticmachine merge upstream |
💛 Build succeeded, but was flakyTest Failures
Metrics [docs]Unknown metric groupsESLint disabled in files
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: cc @donaherc |
) * 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)
💚 All backports created successfully
Note: Successful backport PRs will be merged automatically after passing CI. Questions ?Please refer to the Backport tool documentation |
…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]>
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.