Skip to content

Commit

Permalink
[8.x] [EDR Workflows] CrowdStrike RunScript: Log Actions and UI Output (
Browse files Browse the repository at this point in the history
#204044) (#204693)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[EDR Workflows] CrowdStrike RunScript: Log Actions and UI Output
(#204044)](#204044)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Tomasz
Ciecierski","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-17T15:53:03Z","message":"[EDR
Workflows] CrowdStrike RunScript: Log Actions and UI Output
(#204044)","sha":"a7addbadd394b33fa3212e57f55be251c1a3a371","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["v9.0.0","Team:Defend
Workflows","release_note:feature","backport:version","v8.18.0"],"number":204044,"url":"https://github.com/elastic/kibana/pull/204044","mergeCommit":{"message":"[EDR
Workflows] CrowdStrike RunScript: Log Actions and UI Output
(#204044)","sha":"a7addbadd394b33fa3212e57f55be251c1a3a371"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/204044","number":204044,"mergeCommit":{"message":"[EDR
Workflows] CrowdStrike RunScript: Log Actions and UI Output
(#204044)","sha":"a7addbadd394b33fa3212e57f55be251c1a3a371"}},{"branch":"8.x","label":"v8.18.0","labelRegex":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
  • Loading branch information
tomsonpl authored Dec 18, 2024
1 parent 2735f3c commit 8e8fdee
Show file tree
Hide file tree
Showing 21 changed files with 546 additions and 65 deletions.
28 changes: 13 additions & 15 deletions x-pack/plugins/stack_connectors/common/crowdstrike/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,16 +318,16 @@ export const CrowdstrikeExecuteRTRResponseSchema = schema.object(
schema.string(),
schema.object(
{
session_id: schema.maybe(schema.string()),
task_id: schema.maybe(schema.string()),
complete: schema.maybe(schema.boolean()),
stdout: schema.maybe(schema.string()),
stderr: schema.maybe(schema.string()),
base_command: schema.maybe(schema.string()),
aid: schema.maybe(schema.string()),
errors: schema.maybe(schema.arrayOf(schema.any())),
query_time: schema.maybe(schema.number()),
offline_queued: schema.maybe(schema.boolean()),
session_id: schema.string(),
task_id: schema.string(),
complete: schema.boolean(),
stdout: schema.string(),
stderr: schema.string(),
base_command: schema.string(),
aid: schema.string(),
errors: schema.arrayOf(schema.any()),
query_time: schema.number(),
offline_queued: schema.boolean(),
},
{ unknowns: 'allow' }
)
Expand All @@ -337,9 +337,9 @@ export const CrowdstrikeExecuteRTRResponseSchema = schema.object(
),
meta: schema.object(
{
query_time: schema.maybe(schema.number()),
powered_by: schema.maybe(schema.string()),
trace_id: schema.maybe(schema.string()),
query_time: schema.number(),
powered_by: schema.string(),
trace_id: schema.string(),
},
{ unknowns: 'allow' }
),
Expand All @@ -348,7 +348,5 @@ export const CrowdstrikeExecuteRTRResponseSchema = schema.object(
{ unknowns: 'allow' }
);

export type CrowdStrikeExecuteRTRResponse = typeof CrowdstrikeExecuteRTRResponseSchema;

// TODO: will be part of a next PR
export const CrowdstrikeGetScriptsParamsSchema = schema.any({});
5 changes: 3 additions & 2 deletions x-pack/plugins/stack_connectors/common/crowdstrike/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
CrowdstrikeGetTokenResponseSchema,
CrowdstrikeGetAgentsResponseSchema,
RelaxedCrowdstrikeBaseApiResponseSchema,
CrowdstrikeInitRTRResponseSchema,
CrowdstrikeInitRTRParamsSchema,
CrowdstrikeExecuteRTRResponseSchema,
} from './schema';

export type CrowdstrikeConfig = TypeOf<typeof CrowdstrikeConfigSchema>;
Expand All @@ -35,9 +35,10 @@ export type CrowdstrikeGetAgentOnlineStatusResponse = TypeOf<
typeof CrowdstrikeGetAgentOnlineStatusResponseSchema
>;
export type CrowdstrikeGetTokenResponse = TypeOf<typeof CrowdstrikeGetTokenResponseSchema>;
export type CrowdstrikeInitRTRResponse = TypeOf<typeof CrowdstrikeInitRTRResponseSchema>;

export type CrowdstrikeHostActionsParams = TypeOf<typeof CrowdstrikeHostActionsParamsSchema>;

export type CrowdstrikeActionParams = TypeOf<typeof CrowdstrikeActionParamsSchema>;
export type CrowdstrikeInitRTRParams = TypeOf<typeof CrowdstrikeInitRTRParamsSchema>;

export type CrowdStrikeExecuteRTRResponse = TypeOf<typeof CrowdstrikeExecuteRTRResponseSchema>;
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
CrowdstrikeGetTokenResponse,
CrowdstrikeGetAgentOnlineStatusResponse,
RelaxedCrowdstrikeBaseApiResponse,
CrowdStrikeExecuteRTRResponse,
} from '../../../common/crowdstrike/types';
import {
CrowdstrikeHostActionsParamsSchema,
Expand All @@ -31,7 +32,6 @@ import {
CrowdstrikeRTRCommandParamsSchema,
CrowdstrikeExecuteRTRResponseSchema,
CrowdstrikeGetScriptsParamsSchema,
CrowdStrikeExecuteRTRResponse,
CrowdstrikeApiDoNotValidateResponsesSchema,
CrowdstrikeGetTokenResponseSchema,
} from '../../../common/crowdstrike/schema';
Expand Down Expand Up @@ -292,15 +292,9 @@ export class CrowdstrikeConnector extends SubActionConnector<
payload: {
command: string;
endpoint_ids: string[];
overwriteUrl?: 'batchExecuteRTR' | 'batchActiveResponderExecuteRTR' | 'batchAdminExecuteRTR';
},
connectorUsageCollector: ConnectorUsageCollector
): Promise<CrowdStrikeExecuteRTRResponse> => {
// Some commands are only available in specific API endpoints, however there's an additional requirement check for the command's argument
// Eg. runscript command is available with the batchExecuteRTR endpoint, but if it goes with --Raw parameter, it should go to batchAdminExecuteRTR endpoint
// This overwrite value will be coming from kibana response actions api
const csUrl = payload.overwriteUrl ? this.urls[payload.overwriteUrl] : url;

const batchId = await this.crowdStrikeSessionManager.initializeSession(
{ endpoint_ids: payload.endpoint_ids },
connectorUsageCollector
Expand All @@ -313,7 +307,7 @@ export class CrowdstrikeConnector extends SubActionConnector<
}
return await this.crowdstrikeApiRequest<CrowdStrikeExecuteRTRResponse>(
{
url: csUrl,
url,
method: 'post',
data: {
base_command: baseCommand,
Expand All @@ -335,7 +329,6 @@ export class CrowdstrikeConnector extends SubActionConnector<
payload: {
command: string;
endpoint_ids: string[];
overwriteUrl?: 'batchActiveResponderExecuteRTR' | 'batchAdminExecuteRTR';
},
connectorUsageCollector: ConnectorUsageCollector
): Promise<CrowdStrikeExecuteRTRResponse> {
Expand All @@ -351,7 +344,6 @@ export class CrowdstrikeConnector extends SubActionConnector<
payload: {
command: string;
endpoint_ids: string[];
overwriteUrl?: 'batchAdminExecuteRTR';
},
connectorUsageCollector: ConnectorUsageCollector
): Promise<CrowdStrikeExecuteRTRResponse> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { GetProcessesRouteRequestSchema } from '../response_actions/running_proc
import { KillProcessRouteRequestSchema } from '../response_actions/kill_process';
import { SuspendProcessRouteRequestSchema } from '../response_actions/suspend_process';
import { UploadActionRequestSchema } from '../response_actions/upload';
import { RunScriptActionRequestSchema } from '../response_actions/run_script';

export const ResponseActionBodySchema = schema.oneOf([
IsolateRouteRequestSchema.body,
Expand All @@ -28,6 +29,7 @@ export const ResponseActionBodySchema = schema.oneOf([
ExecuteActionRequestSchema.body,
UploadActionRequestSchema.body,
ScanActionRequestSchema.body,
RunScriptActionRequestSchema.body,
]);

export type ResponseActionsRequestBody = TypeOf<typeof ResponseActionBodySchema>;
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,27 @@ export const RunScriptActionRequestSchema = {
/**
* The script to run
*/
Raw: schema.maybe(NonEmptyString),
raw: schema.maybe(NonEmptyString),
/**
* The path to the script on the host to run
*/
HostPath: schema.maybe(NonEmptyString),
hostPath: schema.maybe(NonEmptyString),
/**
* The path to the script in the cloud to run
*/
CloudFile: schema.maybe(NonEmptyString),
cloudFile: schema.maybe(NonEmptyString),
/**
* The command line to run
*/
CommandLine: schema.maybe(NonEmptyString),
commandLine: schema.maybe(NonEmptyString),
/**
* The max timeout value before the command is killed. Number represents milliseconds
*/
Timeout: schema.maybe(schema.number({ min: 1 })),
timeout: schema.maybe(schema.number({ min: 1 })),
},
{
validate: (params) => {
if (!params.Raw && !params.HostPath && !params.CloudFile) {
if (!params.raw && !params.hostPath && !params.cloudFile) {
return 'At least one of Raw, HostPath, or CloudFile must be provided';
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import type {
ResponseActionUploadOutputContent,
ResponseActionUploadParameters,
GetProcessesActionOutputContent,
ResponseActionRunScriptOutputContent,
ResponseActionRunScriptParameters,
} from '../../types';
import { RESPONSE_ACTION_AGENT_TYPE, RESPONSE_ACTION_TYPE } from './constants';

Expand Down Expand Up @@ -47,6 +49,15 @@ export const isProcessesAction = (
return action.command === 'running-processes';
};

export const isRunScriptAction = (
action: MaybeImmutable<SomeObjectWithCommand>
): action is ActionDetails<
ResponseActionRunScriptOutputContent,
ResponseActionRunScriptParameters
> => {
return action.command === 'runscript';
};

// type guards to ensure only the matching string values are attached to the types filter type
export const isAgentType = (type: string): type is (typeof RESPONSE_ACTION_AGENT_TYPE)[number] =>
RESPONSE_ACTION_AGENT_TYPE.includes(type as (typeof RESPONSE_ACTION_AGENT_TYPE)[number]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ export interface ResponseActionScanOutputContent {
}

export interface ResponseActionRunScriptOutputContent {
output: string;
stdout: string;
stderr: string;
code: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ Command Examples for Running Scripts:
3. Executes a raw script provided entirely within the "--Raw" flag.
runscript --Raw="Get-ChildItem."
runscript --Raw=\`\`\`Get-ChildItem.\`\`\`
4. Executes a script located on the remote host at the specified path with the provided command-line arguments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type {
ActionDetails,
MaybeImmutable,
ResponseActionExecuteOutputContent,
ResponseActionRunScriptOutputContent,
ResponseActionRunScriptParameters,
ResponseActionsExecuteParameters,
} from '../../../../common/endpoint/types';
import { EXECUTE_FILE_LINK_TITLE } from '../endpoint_response_actions_list/translations';
Expand All @@ -18,21 +20,27 @@ import { ExecuteActionHostResponseOutput } from './execute_action_host_response_

export interface ExecuteActionHostResponseProps {
action: MaybeImmutable<
ActionDetails<ResponseActionExecuteOutputContent, ResponseActionsExecuteParameters>
| ActionDetails<ResponseActionExecuteOutputContent, ResponseActionsExecuteParameters>
| ActionDetails<ResponseActionRunScriptOutputContent, ResponseActionRunScriptParameters>
>;
agentId?: string;
canAccessFileDownloadLink: boolean;
'data-test-subj'?: string;
textSize?: 'xs' | 's';
hideFile?: boolean;
hideContext?: boolean;
}

// Note: also used for RunScript command
export const ExecuteActionHostResponse = memo<ExecuteActionHostResponseProps>(
({
action,
agentId = action.agents[0],
canAccessFileDownloadLink,
textSize = 's',
'data-test-subj': dataTestSubj,
hideFile,
hideContext,
}) => {
const outputContent = useMemo(
() =>
Expand All @@ -44,21 +52,24 @@ export const ExecuteActionHostResponse = memo<ExecuteActionHostResponseProps>(

return (
<>
<EuiFlexItem>
<ResponseActionFileDownloadLink
action={action}
buttonTitle={EXECUTE_FILE_LINK_TITLE}
canAccessFileDownloadLink={canAccessFileDownloadLink}
data-test-subj={`${dataTestSubj}-getExecuteLink`}
textSize={textSize}
/>
<EuiSpacer size="xxl" />
</EuiFlexItem>
{!hideFile && (
<EuiFlexItem>
<ResponseActionFileDownloadLink
action={action}
buttonTitle={EXECUTE_FILE_LINK_TITLE}
canAccessFileDownloadLink={canAccessFileDownloadLink}
data-test-subj={`${dataTestSubj}-getExecuteLink`}
textSize={textSize}
/>
<EuiSpacer size="xxl" />
</EuiFlexItem>
)}
{outputContent && (
<ExecuteActionHostResponseOutput
outputContent={outputContent}
data-test-subj={`${dataTestSubj}-executeResponseOutput`}
textSize={textSize}
hideContext={hideContext}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ interface ShellInfoContentProps {
textSize?: 's' | 'xs';
title: string;
}

const ShellInfoContent = memo<ShellInfoContentProps>(({ content, textSize, title }) => (
<StyledEuiText size={textSize}>
<strong>
Expand Down Expand Up @@ -178,10 +179,12 @@ export interface ExecuteActionHostResponseOutputProps {
outputContent: ResponseActionExecuteOutputContent;
'data-test-subj'?: string;
textSize?: 's' | 'xs';
hideContext?: boolean;
}

// Note: also used for RunScript command
export const ExecuteActionHostResponseOutput = memo<ExecuteActionHostResponseOutputProps>(
({ outputContent, 'data-test-subj': dataTestSubj, textSize = 'xs' }) => {
({ outputContent, 'data-test-subj': dataTestSubj, textSize = 'xs', hideContext }) => {
const contextContent = useMemo(
() => (
<>
Expand Down Expand Up @@ -216,14 +219,16 @@ export const ExecuteActionHostResponseOutput = memo<ExecuteActionHostResponseOut

return (
<>
<EuiFlexItem>
<ExecutionActionOutputAccordion
content={contextContent}
data-test-subj={`${dataTestSubj}-context`}
textSize={textSize}
type="context"
/>
</EuiFlexItem>
{!hideContext && (
<EuiFlexItem>
<ExecutionActionOutputAccordion
content={contextContent}
data-test-subj={`${dataTestSubj}-context`}
textSize={textSize}
type="context"
/>
</EuiFlexItem>
)}
<EuiFlexItem>
{outputContent.stderr.length > 0 && (
<>
Expand Down
Loading

0 comments on commit 8e8fdee

Please sign in to comment.