-
Notifications
You must be signed in to change notification settings - Fork 8.1k
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
[Defend Workflows] Common response actions tab in alert Flyout #155362
Conversation
…-ref HEAD~1..HEAD --fix'
…o response-actions-common-tab
# Conflicts: # x-pack/plugins/osquery/tsconfig.json # x-pack/plugins/security_solution/public/common/components/event_details/endpoint_response_actions_tab.tsx
@@ -37,7 +38,9 @@ export const securitySolutionSearchStrategyProvider = <T extends FactoryQueryTyp | |||
getSpaceId?: (request: KibanaRequest) => string, | |||
ruleDataClient?: IRuleDataClient | null | |||
): ISearchStrategy<StrategyRequestType<T>, StrategyResponseType<T>> => { | |||
const es = data.search.getSearchStrategy(ENHANCED_ES_SEARCH_STRATEGY); | |||
const es = data.search.getSearchStrategy( |
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.
why does the inferred return type no longer work here?
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 needed to enable using a type in between request and response, I called it StrategyParseResponseType
which default to IEsSearchResponse
because in my case the response getting into parse()
and whatever parse() returns are 2 different things, couldn't just use StrategyResponseType
.
This enabled us to add some more logic to search_strategy, instead of doing that on frontend queries.
How does this sound?
<Suspense fallback={null}> | ||
<OsqueryResult services={services} {...props} /> | ||
</Suspense> | ||
); |
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.
nit:
); | |
); | |
getLazyOsqueryResult.displayName = 'LazyOsQueryResult'; | |
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 👍
…s-common-tab # Conflicts: # x-pack/plugins/translations/translations/fr-FR.json
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.
Looks good! I've tested it out again for hostnames. I've a few small suggestions and a couple of questions. I do strongly feel that the hosts/hostnames info should go into EndpointActions.data
if it is in the actions doc and in the EndpointActions.data.output
if it is in the response. Let me know what you think or need help with anything.
@@ -45,6 +45,7 @@ export class EndpointActionGenerator extends BaseDataGenerator { | |||
agent: { | |||
id: [this.seededUUIDv4()], | |||
}, | |||
hosts: undefined, |
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 should be part of EndpointActions.data
. See my comment below.
import type { LogsEndpointActionWithHosts } from '../../../endpoint/types'; | ||
import type { ResponseActionsQueries } from '.'; | ||
|
||
export enum Direction { |
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.
Consider naming this SortOrder
expiration: string; | ||
actionId: string; | ||
sort: { | ||
direction: Direction; |
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.
Consider renaming this so that sort has sorting order
...
sort : {
order: SortOrder;
filed: string;
}
...
@@ -0,0 +1,29 @@ | |||
/* | |||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one |
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.
Consider renaming the file to action.ts
for naming consistency.
alertIds: string[]; | ||
agentId?: string; | ||
sort: { | ||
direction: Direction; |
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.
order: SortOrder
{ | ||
alertIds: [alertId], | ||
}, | ||
{ skip: shouldEarlyReturn } |
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 feel this skip name is confusing! Also, in the useGetAutomatedActionList
hook it is inverted. I would suggest that you do this instead. I also now notice that there's also a skip
with useGetAutomatedActionResponseList
. Consider changing that also similar to my suggestion here.
{ skip: shouldEarlyReturn } | |
{ enabled: !shouldEarlyReturn } |
You could also rename shouldEarlyReturn
to doNothing
.
@@ -71,7 +71,7 @@ export const ResponseActionsLog = memo< | |||
statuses: [], | |||
userIds: [], | |||
withOutputs: [], | |||
withAutomatedActions: true, | |||
withAutomatedActions: false, |
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.
Please also update some of the tests that have withAutomatedActions
as true
for default.
@@ -143,6 +144,7 @@ export const actionCreateService = ( | |||
agent: { | |||
id: payload.endpoint_ids, | |||
}, | |||
hosts: payload.hosts, |
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.
Either this should be in the response output or it should be within EndpointActions.data
. See my earlier comment.
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.
Move to EndpointActions.data
the change makes sense to me after giving it more thoughts. Thanks @ashokaditya 👍
import type { ActionRequestOptions } from '../../../../../../common/search_strategy/security_solution/response_actions'; | ||
import { ENDPOINT_ACTIONS_INDEX } from '../../../../../../common/endpoint/constants'; | ||
|
||
export const buildActionsQuery = ({ |
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.
Maybe name this buildResponseActionsQuery
.
response.rawResponse?.aggregations?.aggs.responses_by_action_id?.responses.buckets; | ||
const successful = aggsBuckets?.find((bucket) => bucket.key === 'success')?.doc_count ?? 0; | ||
|
||
const wasSuccessful = responded === successful; |
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.
Q: So here it looks like unless all responses are received it is not successful. Have we reached a consensus on showing a partially successful status based on a set of completed/successful responses 🤔
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.
Good question 👍 The change for partially successful has been abandoned. But in my opinion it would be nice to get back to it.
…o response-actions-common-tab
@@ -71,7 +71,7 @@ export const allowedExperimentalValues = Object.freeze({ | |||
/** | |||
* Enables the automated endpoint response action in rule + alerts | |||
*/ | |||
endpointResponseActionsEnabled: false, | |||
endpointResponseActionsEnabled: true, |
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 should be false
to start with. The only way to enable this would be via setting it on kibana.dev.yml
or with local running env vars
@@ -31,7 +31,7 @@ export const EndpointResponseActionResults = ({ action }: EndpointResponseAction | |||
const canReadEndpoint = canReadActionsLogManagement && canAccessEndpointActionsLogManagement; | |||
const { data: expandedAction } = useGetAutomatedActionResponseList( | |||
{ actionId, expiration, agent }, | |||
{ skip: !canReadEndpoint, action, isLive } | |||
{ enabled: canReadEndpoint, action, isLive } |
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 makes sense when you read it quickly! Thanks for changing this.
@@ -51,7 +51,7 @@ describe('useGetEndpointActionList hook', () => { | |||
pageSize: 20, | |||
startDate: 'now-5d', | |||
endDate: 'now', | |||
withAutomatedActions: true, | |||
withAutomatedActions: false, |
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.
🙇
@@ -25,12 +25,12 @@ import type { | |||
} from '../../../../common/endpoint/schema/automated_actions'; | |||
|
|||
interface GetAutomatedActionsListOptions { | |||
skip?: boolean; | |||
enabled: boolean; |
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.
🙇
// export interface SortField<Field = string> { | ||
// field: Field; | ||
// order: SortOrder; | ||
// } |
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.
Maybe remove this if not needed.
@@ -152,6 +153,7 @@ export const actionCreateService = ( | |||
command: payload.command, | |||
comment: payload.comment ?? undefined, | |||
...(payload.alert_ids ? { alert_id: payload.alert_ids } : {}), | |||
...(payload.hosts ? { hosts: payload.hosts } : {}), |
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.
🚀
@@ -29,6 +29,7 @@ import type { | |||
} from '../../../../common/endpoint/types'; | |||
import { ActivityLogItemTypes } from '../../../../common/endpoint/types'; | |||
import type { EndpointMetadataService } from '../metadata'; | |||
|
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 was unintentional I presume
export const useGetAutomatedActionList = ( | ||
query: EndpointAutomatedActionListRequestQuery, | ||
{ enabled }: GetAutomatedActionsListOptions | ||
) => { |
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.
Sorry I missed this earlier, but I'd encourage you to add return types to all these query hooks.
setUrlWithAutomatedActions(!withAutomatedActionsUrlParam); | ||
} | ||
}, [isFlyout, setUrlWithAutomatedActions, withAutomatedActionsUrlParam]); | ||
setUrlWithAutomatedActions(!withAutomatedActionsUrlParam); |
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.
So this would update the URL params on the flyout view as well. We don't want to do that. We want the response actions log query params to be updated on the URL only when in the page view.
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 see, therefore I would have to pass event handlers through the whole thing again ;p Could you elaborate on why we don't want to pass URL params in flyout?
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.
Not sure what you mean by passing even handlers through...?
We don't want to change the URL params on the flyout as we have a flyout on the endpoint details and responder view. We don't want to drive/control the response actions log or the endpoint details via URL params. Also, changing URL in this view will break bookmarks. Hope that helps.
@@ -182,7 +182,7 @@ export const ActionsLogExpandedTray = memo<{ | |||
}>(({ action, 'data-test-subj': dataTestSubj }) => { | |||
const getTestId = useTestIdGenerator(dataTestSubj); | |||
|
|||
const { startedAt, completedAt, command: _command, comment, parameters } = action; | |||
const { hosts, agents, startedAt, completedAt, command: _command, comment, parameters } = action; |
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 a note, this hosts
"name" is not the same as the one we've added in this PR under EndpointActions.data
for automated actions from alerts.
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 is hosts from ActionDetails
that could be created in two ways:
- For Response Actions Results tab: it comes from
EndpointActions.data
- For Normal Actions List it's calculated in
getActionDetailsList
Is this ok with you?
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.
That is right. But I don't see that change on the server side to handle that case for 1. That would go in mapToNormalizedActionRequest
util function. And then on the list handler for action list you need to handle the hosts
info being populated for each action based on whether it is automated or user initiated.
So here you're using the user-initiated action detail data and the hostname is populated via the agent metadata
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 talked offline about this, and this is okay here. This hostname is indeed the one from normal actions and the change here to have consistent host names in the details.
@@ -223,13 +223,17 @@ export const ActionsLogExpandedTray = memo<{ | |||
title: OUTPUT_MESSAGES.expandSection.comment, | |||
description: comment ? comment : emptyValue, | |||
}, | |||
{ | |||
title: OUTPUT_MESSAGES.expandSection.hostname, | |||
description: hosts?.[agents?.[0]]?.name || emptyValue, |
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.
hosts
and agents
are always present in action details.
description: hosts?.[agents?.[0]]?.name || emptyValue, | |
description: hosts[agents[0]]name, |
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.
Now to think of it, this should also handle showing multiple hostnames on the page view. Right?
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.
Hey, you're right the hosts and agents are always there 👍 However, sometimes name === '' so I would leave || emptyValue to display -
as with the rest. What do you think?
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.
Oh yeah the name could be '' for unenrolled agents
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.
Changed this to display multiple hostnames :)
const eventText = getCommentText(action.EndpointActions.data.command); | ||
|
||
const hostName = useMemo( | ||
() => expandedAction?.hosts?.[expandedAction.agents?.[0]]?.name, |
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 is correct where the hosts
is the one we need from the automated actions
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.
Yes, this is the host that we previously saved in Endpointdata.data
. However this const is just used for data-test-subj here.
} = useUserPrivileges(); | ||
|
||
const [isLive, setIsLive] = useState(true); | ||
const canReadEndpoint = canReadActionsLogManagement && canAccessEndpointActionsLogManagement; |
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.
Sorry, I missed this earlier as well. This condition boils down to having READ RBAC access to Actions Log management and having platinum and enterprise licenses. So this won't work if you're not superuser
.
We should decide if we want to give this to enterprise or platinum users. One can't have two licenses at the same time. For platinum
and upwards, canReadActionsLogManagement
will do, and for enterprise
canAccessEndpointActionsLogManagement
should be sufficient.
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.
Hmm thanks for raising this Ash 👍 I misread how withEndpointAuthz
works and assumed that these 2 are necessary. Please correct me if I am wrong, but you're saying that we should have one or another right? So it should be like this?
const canReadEndpoint = canReadActionsLogManagement || canAccessEndpointActionsLogManagement;
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, this is also okay.
But I understand that, if you gate this for platinum
users then it will work for both platinum and enterprise users. Enterprise includes everything platinum has. So in essence, just canReadActionsLogManagement
will do.
You can also add a test for these license checks if you like.
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.
One minor change is needed with multiple hostnames. I'm approving this now. Feel free to do this in a new PR along with missing tests. I can take another look if you happen to fix this in the current PR.
Thanks for your patience with the review. 🙇 And many thanks for doing this huge PR. 👏
@@ -225,15 +226,15 @@ export const ActionsLogExpandedTray = memo<{ | |||
}, | |||
{ | |||
title: OUTPUT_MESSAGES.expandSection.hostname, | |||
description: hosts?.[agents?.[0]]?.name || emptyValue, | |||
description: map(hosts, (host) => host.name).join(', ') || emptyValue, |
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 should be for non-flyout(page view). For flyout the previous version was okay. You would also need to pass the isFlyout as a prop to ActionsLogExpandedTray
now. Another thing is to filter out '' names. Something like,
description:
(!isFlyout
? Object.values(hosts)
.reduce<string[]>((acc, name) => {
if (name.name.length) {
acc.push(name.name);
}
return acc;
}, [])
.join(', ')
: hosts[agents[0]].name) || emptyValue,
We've also had the logic for this here if you want to pull it out into a new function in x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx
and then import and use it from there.
💚 Build Succeeded
Metrics [docs]Module Count
Public APIs missing comments
Async chunks
Public APIs missing exports
Page load bundle
Unknown metric groupsAPI count
async chunk count
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: cc @tomsonpl |
This PR combines 2 Tabs (Osquery + Endpoint) Response Actions in Event Details Flyout into one, and present results in a unified way.
AC:
osquery
tab ifendpointResponseActionsEnabled
is set to falseosquery + endpoint response actions
in one unified tab (only ifendpointResponseActionsEnabled
FF is set to true) and hideosquery
tabsecuritySolutionSearchStrategyProvider
so we can have aStrategyParseResponseType
Automated
filter in FlyoutWe are aware of the new Extended Flyout that is prepared to be released soon. We're going to migrate to it after this PR gets merged.
When analyst doesn't have RBAC permissions to see osquery / endpoint results.
When there is an agent that is not enrolled (we also display hostname from now on)