mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[EDR Workflows] Add RunScript CS Command - UI (#202012)
This commit is contained in:
parent
2818a7cc5a
commit
9b27804a9b
24 changed files with 259 additions and 19 deletions
|
@ -96,6 +96,7 @@ export const GET_FILE_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/get_file`;
|
|||
export const EXECUTE_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/execute`;
|
||||
export const UPLOAD_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/upload`;
|
||||
export const SCAN_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/scan`;
|
||||
export const RUN_SCRIPT_ROUTE = `${BASE_ENDPOINT_ACTION_ROUTE}/run_script`;
|
||||
|
||||
/** Endpoint Actions Routes */
|
||||
export const ENDPOINT_ACTION_LOG_ROUTE = `${BASE_ENDPOINT_ROUTE}/action_log/{agent_id}`;
|
||||
|
|
|
@ -147,6 +147,17 @@ export class EndpointActionGenerator extends BaseDataGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
if (command === 'runscript') {
|
||||
if (!output) {
|
||||
output = {
|
||||
type: 'json',
|
||||
content: {
|
||||
code: '200',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (command === 'execute') {
|
||||
if (!output) {
|
||||
output = this.generateExecuteActionResponseOutput();
|
||||
|
|
|
@ -28,6 +28,7 @@ export const RESPONSE_ACTION_API_COMMANDS_NAMES = [
|
|||
'execute',
|
||||
'upload',
|
||||
'scan',
|
||||
'runscript',
|
||||
] as const;
|
||||
|
||||
export type ResponseActionsApiCommandNames = (typeof RESPONSE_ACTION_API_COMMANDS_NAMES)[number];
|
||||
|
@ -54,6 +55,7 @@ export const ENDPOINT_CAPABILITIES = [
|
|||
'execute',
|
||||
'upload_file',
|
||||
'scan',
|
||||
'runscript',
|
||||
] as const;
|
||||
|
||||
export type EndpointCapabilities = (typeof ENDPOINT_CAPABILITIES)[number];
|
||||
|
@ -72,6 +74,7 @@ export const CONSOLE_RESPONSE_ACTION_COMMANDS = [
|
|||
'execute',
|
||||
'upload',
|
||||
'scan',
|
||||
'runscript',
|
||||
] as const;
|
||||
|
||||
export type ConsoleResponseActionCommands = (typeof CONSOLE_RESPONSE_ACTION_COMMANDS)[number];
|
||||
|
@ -100,6 +103,7 @@ export const RESPONSE_CONSOLE_ACTION_COMMANDS_TO_RBAC_FEATURE_CONTROL: Record<
|
|||
execute: 'writeExecuteOperations',
|
||||
upload: 'writeFileOperations',
|
||||
scan: 'writeScanOperations',
|
||||
runscript: 'writeExecuteOperations',
|
||||
});
|
||||
|
||||
export const RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP = Object.freeze<
|
||||
|
@ -114,6 +118,7 @@ export const RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP = Object.freeze<
|
|||
'suspend-process': 'suspend-process',
|
||||
upload: 'upload',
|
||||
scan: 'scan',
|
||||
runscript: 'runscript',
|
||||
});
|
||||
|
||||
export const RESPONSE_CONSOLE_COMMAND_TO_API_COMMAND_MAP = Object.freeze<
|
||||
|
@ -128,6 +133,7 @@ export const RESPONSE_CONSOLE_COMMAND_TO_API_COMMAND_MAP = Object.freeze<
|
|||
'suspend-process': 'suspend-process',
|
||||
upload: 'upload',
|
||||
scan: 'scan',
|
||||
runscript: 'runscript',
|
||||
});
|
||||
|
||||
export const RESPONSE_CONSOLE_ACTION_COMMANDS_TO_ENDPOINT_CAPABILITY = Object.freeze<
|
||||
|
@ -142,6 +148,7 @@ export const RESPONSE_CONSOLE_ACTION_COMMANDS_TO_ENDPOINT_CAPABILITY = Object.fr
|
|||
'suspend-process': 'suspend_process',
|
||||
upload: 'upload_file',
|
||||
scan: 'scan',
|
||||
runscript: 'runscript',
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -159,6 +166,7 @@ export const RESPONSE_CONSOLE_ACTION_COMMANDS_TO_REQUIRED_AUTHZ = Object.freeze<
|
|||
'kill-process': 'canKillProcess',
|
||||
'suspend-process': 'canSuspendProcess',
|
||||
scan: 'canWriteScanOperations',
|
||||
runscript: 'canWriteExecuteOperations',
|
||||
});
|
||||
|
||||
// 4 hrs in seconds
|
||||
|
|
|
@ -126,6 +126,18 @@ const RESPONSE_ACTIONS_SUPPORT_MAP: SupportMap = {
|
|||
crowdstrike: false,
|
||||
},
|
||||
},
|
||||
runscript: {
|
||||
automated: {
|
||||
endpoint: false,
|
||||
sentinel_one: false,
|
||||
crowdstrike: false,
|
||||
},
|
||||
manual: {
|
||||
endpoint: false,
|
||||
sentinel_one: false,
|
||||
crowdstrike: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -257,6 +257,12 @@ export const allowedExperimentalValues = Object.freeze({
|
|||
* Enables the Defend Insights feature
|
||||
*/
|
||||
defendInsights: false,
|
||||
|
||||
/**
|
||||
* Enables CrowdStrike's RunScript RTR command
|
||||
*/
|
||||
|
||||
crowdstrikeRunScriptEnabled: false,
|
||||
});
|
||||
|
||||
type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;
|
||||
|
|
|
@ -215,6 +215,88 @@ export const CONSOLE_COMMANDS = {
|
|||
},
|
||||
};
|
||||
|
||||
export const CROWDSTRIKE_CONSOLE_COMMANDS = {
|
||||
runscript: {
|
||||
args: {
|
||||
raw: {
|
||||
about: i18n.translate(
|
||||
'xpack.securitySolution.crowdStrikeConsoleCommands.runscript.args.raw.about',
|
||||
{
|
||||
defaultMessage: 'Raw script content',
|
||||
}
|
||||
),
|
||||
},
|
||||
cloudFile: {
|
||||
about: i18n.translate(
|
||||
'xpack.securitySolution.crowdStrikeConsoleCommands.runscript.args.cloudFile.about',
|
||||
{
|
||||
defaultMessage: 'Script name in cloud storage',
|
||||
}
|
||||
),
|
||||
},
|
||||
commandLine: {
|
||||
about: i18n.translate(
|
||||
'xpack.securitySolution.crowdStrikeConsoleCommands.runscript.args.commandLine.about',
|
||||
{
|
||||
defaultMessage: 'Command line arguments',
|
||||
}
|
||||
),
|
||||
},
|
||||
hostPath: {
|
||||
about: i18n.translate(
|
||||
'xpack.securitySolution.crowdStrikeConsoleCommands.runscript.args.hostPath.about',
|
||||
{
|
||||
defaultMessage: 'Absolute or relative path of script on host machine',
|
||||
}
|
||||
),
|
||||
},
|
||||
timeout: {
|
||||
about: i18n.translate(
|
||||
'xpack.securitySolution.crowdStrikeConsoleCommands.runscript.args.timeout.about',
|
||||
{
|
||||
defaultMessage: 'Timeout in seconds',
|
||||
}
|
||||
),
|
||||
},
|
||||
},
|
||||
title: i18n.translate('xpack.securitySolution.crowdStrikeConsoleCommands.runscript.title', {
|
||||
defaultMessage: 'Isolate',
|
||||
}),
|
||||
about: i18n.translate('xpack.securitySolution.crowdStrikeConsoleCommands.runscript.about', {
|
||||
defaultMessage: 'Run a script on the host',
|
||||
}),
|
||||
helpUsage: i18n.translate('xpack.securitySolution.crowdStrikeConsoleCommands.runscript.about', {
|
||||
defaultMessage: `
|
||||
Command Examples for Running Scripts:
|
||||
|
||||
1. Executes a script saved in the CrowdStrike cloud with the specified command-line arguments.
|
||||
|
||||
runscript --CloudFile="CloudScript1.ps1" --CommandLine="-Verbose true"
|
||||
|
||||
2. Executes a script saved in the CrowdStrike cloud with the specified command-line arguments and a 180-second timeout.
|
||||
|
||||
runscript --CloudFile="CloudScript1.ps1" --CommandLine="-Verbose true" -Timeout=180
|
||||
|
||||
3. Executes a raw script provided entirely within the "--Raw" flag.
|
||||
|
||||
runscript --Raw="Get-ChildItem."
|
||||
|
||||
4. Executes a script located on the remote host at the specified path with the provided command-line arguments.
|
||||
|
||||
runscript --HostPath="C:\\temp\\LocalScript.ps1" --CommandLine="-Verbose true"
|
||||
|
||||
`,
|
||||
}),
|
||||
privileges: i18n.translate(
|
||||
'xpack.securitySolution.crowdStrikeConsoleCommands.runscript.privileges',
|
||||
{
|
||||
defaultMessage:
|
||||
'Insufficient privileges to run script. Contact your Kibana administrator if you think you should have this permission.',
|
||||
}
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const CONFIRM_WARNING_MODAL_LABELS = (entryType: string) => {
|
||||
return {
|
||||
title: i18n.translate('xpack.securitySolution.artifacts.confirmWarningModal.title', {
|
||||
|
|
|
@ -33,6 +33,13 @@ export const CommandInputUsage = memo<Pick<CommandUsageProps, 'commandDef'>>(({
|
|||
});
|
||||
}, [commandDef]);
|
||||
|
||||
const helpExample = useMemo(() => {
|
||||
if (commandDef.helpUsage) {
|
||||
return commandDef.helpUsage;
|
||||
}
|
||||
return commandDef.exampleUsage;
|
||||
}, [commandDef]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiDescriptionList
|
||||
|
@ -55,7 +62,7 @@ export const CommandInputUsage = memo<Pick<CommandUsageProps, 'commandDef'>>(({
|
|||
titleProps={additionalProps}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
{commandDef.exampleUsage && (
|
||||
{helpExample && (
|
||||
<EuiDescriptionList
|
||||
compressed
|
||||
type="column"
|
||||
|
@ -69,7 +76,7 @@ export const CommandInputUsage = memo<Pick<CommandUsageProps, 'commandDef'>>(({
|
|||
})}
|
||||
</ConsoleCodeBlock>
|
||||
),
|
||||
description: <ConsoleCodeBlock>{commandDef.exampleUsage}</ConsoleCodeBlock>,
|
||||
description: <ConsoleCodeBlock>{helpExample}</ConsoleCodeBlock>,
|
||||
},
|
||||
]}
|
||||
descriptionProps={additionalProps}
|
||||
|
|
|
@ -49,7 +49,12 @@ export interface CommandArgDefinition {
|
|||
* - `truthy`: The argument must have a value and the values must be "truthy" (evaluate to `Boolean` true)
|
||||
*/
|
||||
mustHaveValue?: boolean | 'non-empty-string' | 'number' | 'number-greater-than-zero' | 'truthy';
|
||||
|
||||
/**
|
||||
* Specifies that one or more arguments might be required, but only one of them can be used at a time.
|
||||
*/
|
||||
exclusiveOr?: boolean;
|
||||
|
||||
/**
|
||||
* Validate the individual values given to this argument.
|
||||
* Should return `true` if valid or a string with the error message
|
||||
|
@ -124,10 +129,17 @@ export interface CommandDefinition<TMeta = any> {
|
|||
/**
|
||||
* Displayed in the input hint area when the user types the command as well as in the output of
|
||||
* this command's `--help`. This value will override the command usage generated by the console
|
||||
* from the Command Definition.
|
||||
* from the Command Definition. It's value displayed in `--help` would overriden by `helpUsage` if defined.
|
||||
*/
|
||||
exampleUsage?: string;
|
||||
|
||||
/**
|
||||
* Displayed in the output of this command's `--help`.
|
||||
* This value will override the command usage generated by the console
|
||||
* from the Command Definition.
|
||||
*/
|
||||
helpUsage?: string;
|
||||
|
||||
/**
|
||||
* Validate the command entered by the user. This is called only after the Console has ran
|
||||
* through all of its builtin validations (based on `CommandDefinition`).
|
||||
|
|
|
@ -42,7 +42,7 @@ import {
|
|||
import { getCommandAboutInfo } from './get_command_about_info';
|
||||
|
||||
import { validateUnitOfTime } from './utils';
|
||||
import { CONSOLE_COMMANDS } from '../../../common/translations';
|
||||
import { CONSOLE_COMMANDS, CROWDSTRIKE_CONSOLE_COMMANDS } from '../../../common/translations';
|
||||
import { ScanActionResult } from '../command_render_components/scan_action';
|
||||
|
||||
const emptyArgumentValidator = (argData: ParsedArgData): true | string => {
|
||||
|
@ -167,6 +167,7 @@ export const getEndpointConsoleCommands = ({
|
|||
const featureFlags = ExperimentalFeaturesService.get();
|
||||
|
||||
const isUploadEnabled = featureFlags.responseActionUploadEnabled;
|
||||
const crowdstrikeRunScriptEnabled = featureFlags.crowdstrikeRunScriptEnabled;
|
||||
|
||||
const doesEndpointSupportCommand = (commandName: ConsoleResponseActionCommands) => {
|
||||
// Agent capabilities is only validated for Endpoint agent types
|
||||
|
@ -523,6 +524,71 @@ export const getEndpointConsoleCommands = ({
|
|||
privileges: endpointPrivileges,
|
||||
}),
|
||||
});
|
||||
if (crowdstrikeRunScriptEnabled) {
|
||||
consoleCommands.push({
|
||||
name: 'runscript',
|
||||
about: getCommandAboutInfo({
|
||||
aboutInfo: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.about,
|
||||
isSupported: doesEndpointSupportCommand('runscript'),
|
||||
}),
|
||||
RenderComponent: () => null,
|
||||
meta: {
|
||||
agentType,
|
||||
endpointId: endpointAgentId,
|
||||
capabilities: endpointCapabilities,
|
||||
privileges: endpointPrivileges,
|
||||
},
|
||||
exampleUsage: `runscript --Raw=\`\`\`Get-ChildItem .\`\`\` -CommandLine=""`,
|
||||
helpUsage: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.helpUsage,
|
||||
exampleInstruction: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.about,
|
||||
validate: capabilitiesAndPrivilegesValidator(agentType),
|
||||
mustHaveArgs: true,
|
||||
args: {
|
||||
Raw: {
|
||||
required: false,
|
||||
allowMultiples: false,
|
||||
about: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.args.raw.about,
|
||||
mustHaveValue: 'non-empty-string',
|
||||
exclusiveOr: true,
|
||||
},
|
||||
CloudFile: {
|
||||
required: false,
|
||||
allowMultiples: false,
|
||||
about: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.args.cloudFile.about,
|
||||
mustHaveValue: 'non-empty-string',
|
||||
exclusiveOr: true,
|
||||
},
|
||||
CommandLine: {
|
||||
required: false,
|
||||
allowMultiples: false,
|
||||
about: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.args.commandLine.about,
|
||||
mustHaveValue: 'non-empty-string',
|
||||
},
|
||||
HostPath: {
|
||||
required: false,
|
||||
allowMultiples: false,
|
||||
about: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.args.hostPath.about,
|
||||
mustHaveValue: 'non-empty-string',
|
||||
exclusiveOr: true,
|
||||
},
|
||||
Timeout: {
|
||||
required: false,
|
||||
allowMultiples: false,
|
||||
about: CROWDSTRIKE_CONSOLE_COMMANDS.runscript.args.timeout.about,
|
||||
mustHaveValue: 'number-greater-than-zero',
|
||||
},
|
||||
...commandCommentArgument(),
|
||||
},
|
||||
helpGroupLabel: HELP_GROUPS.responseActions.label,
|
||||
helpGroupPosition: HELP_GROUPS.responseActions.position,
|
||||
helpCommandPosition: 9,
|
||||
helpDisabled: !doesEndpointSupportCommand('runscript'),
|
||||
helpHidden: !getRbacControl({
|
||||
commandName: 'runscript',
|
||||
privileges: endpointPrivileges,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
switch (agentType) {
|
||||
case 'sentinel_one':
|
||||
|
|
|
@ -73,7 +73,10 @@ describe('When displaying Endpoint Response Actions', () => {
|
|||
HELP_GROUPS.responseActions.label
|
||||
);
|
||||
|
||||
const expectedCommands: string[] = [...CONSOLE_RESPONSE_ACTION_COMMANDS];
|
||||
const endpointCommands = CONSOLE_RESPONSE_ACTION_COMMANDS.filter(
|
||||
(command) => command !== 'runscript'
|
||||
);
|
||||
const expectedCommands: string[] = [...endpointCommands];
|
||||
// add status to the list of expected commands in that order
|
||||
expectedCommands.splice(2, 0, 'status');
|
||||
|
||||
|
@ -149,6 +152,7 @@ describe('When displaying Endpoint Response Actions', () => {
|
|||
beforeEach(() => {
|
||||
(ExperimentalFeaturesService.get as jest.Mock).mockReturnValue({
|
||||
responseActionsCrowdstrikeManualHostIsolationEnabled: true,
|
||||
crowdstrikeRunScriptEnabled: true,
|
||||
});
|
||||
commands = getEndpointConsoleCommands({
|
||||
agentType: 'crowdstrike',
|
||||
|
@ -176,7 +180,7 @@ describe('When displaying Endpoint Response Actions', () => {
|
|||
HELP_GROUPS.responseActions.label
|
||||
);
|
||||
|
||||
expect(commandsInPanel).toEqual(['isolate', 'release']);
|
||||
expect(commandsInPanel).toEqual(['isolate', 'release', 'runscript --Raw']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,12 +35,14 @@ export const ActionsLogFilter = memo(
|
|||
isFlyout,
|
||||
onChangeFilterOptions,
|
||||
'data-test-subj': dataTestSubj,
|
||||
'data-test-height': dataTestHeight,
|
||||
}: {
|
||||
filterName: ActionsLogPopupFilters;
|
||||
typesFilters?: TypesFilters;
|
||||
isFlyout: boolean;
|
||||
onChangeFilterOptions?: (selectedOptions: string[]) => void;
|
||||
'data-test-subj'?: string;
|
||||
'data-test-height'?: number;
|
||||
}) => {
|
||||
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||
|
||||
|
@ -263,6 +265,7 @@ export const ActionsLogFilter = memo(
|
|||
data-test-subj={dataTestSubj}
|
||||
>
|
||||
<EuiSelectable
|
||||
height={dataTestHeight}
|
||||
aria-label={`${filterName}`}
|
||||
emptyMessage={UX_MESSAGES.filterEmptyMessage(filterName)}
|
||||
isLoading={isLoading}
|
||||
|
|
|
@ -37,6 +37,7 @@ export const ActionsLogFilters = memo(
|
|||
onTimeChange,
|
||||
showHostsFilter,
|
||||
'data-test-subj': dataTestSubj,
|
||||
'data-test-height': dataTestHeight,
|
||||
}: {
|
||||
dateRangePickerState: DateRangePickerValues;
|
||||
isDataLoading: boolean;
|
||||
|
@ -53,6 +54,7 @@ export const ActionsLogFilters = memo(
|
|||
onClick: ReturnType<typeof useGetEndpointActionList>['refetch'];
|
||||
showHostsFilter: boolean;
|
||||
'data-test-subj'?: string;
|
||||
'data-test-height'?: number;
|
||||
}) => {
|
||||
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||
|
||||
|
@ -76,6 +78,7 @@ export const ActionsLogFilters = memo(
|
|||
isFlyout={isFlyout}
|
||||
onChangeFilterOptions={onChangeCommandsFilter}
|
||||
data-test-subj={dataTestSubj}
|
||||
data-test-height={dataTestHeight}
|
||||
/>
|
||||
<ActionsLogFilter
|
||||
filterName={'statuses'}
|
||||
|
@ -109,6 +112,7 @@ export const ActionsLogFilters = memo(
|
|||
isSentinelOneV1Enabled,
|
||||
onChangeHostsFilter,
|
||||
dataTestSubj,
|
||||
dataTestHeight,
|
||||
onChangeCommandsFilter,
|
||||
onChangeStatusesFilter,
|
||||
onChangeAgentTypesFilter,
|
||||
|
|
|
@ -334,6 +334,10 @@ export const useActionsLogFilter = ({
|
|||
return false;
|
||||
}
|
||||
|
||||
if (commandName === 'runscript' && !featureFlags.crowdstrikeRunScriptEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).map((commandName) => ({
|
||||
key: commandName,
|
||||
|
|
|
@ -1518,6 +1518,7 @@ describe('Response actions history', () => {
|
|||
beforeEach(() => {
|
||||
featureFlags = {
|
||||
responseActionUploadEnabled: true,
|
||||
crowdstrikeRunScriptEnabled: true,
|
||||
};
|
||||
|
||||
mockedContext.setExperimentalFlag(featureFlags);
|
||||
|
@ -1537,8 +1538,9 @@ describe('Response actions history', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should show a list of actions (with `scan`) when opened', async () => {
|
||||
render();
|
||||
it('should show a list of actions (with `runscript`) when opened', async () => {
|
||||
// Note: when we enable new commands, it might be needed to increase the height
|
||||
render({ 'data-test-height': 350 });
|
||||
const { getByTestId, getAllByTestId } = renderResult;
|
||||
|
||||
await user.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
|
||||
|
@ -1557,6 +1559,7 @@ describe('Response actions history', () => {
|
|||
'execute. To check this option, press Enter.',
|
||||
'upload. To check this option, press Enter.',
|
||||
'scan. To check this option, press Enter.',
|
||||
'runscript. To check this option, press Enter.',
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ export const ResponseActionsLog = memo<
|
|||
isFlyout?: boolean;
|
||||
setIsDataInResponse?: (isData: boolean) => void;
|
||||
'data-test-subj'?: string;
|
||||
'data-test-height'?: number;
|
||||
}
|
||||
>(
|
||||
({
|
||||
|
@ -46,6 +47,7 @@ export const ResponseActionsLog = memo<
|
|||
isFlyout = true,
|
||||
setIsDataInResponse,
|
||||
'data-test-subj': dataTestSubj = 'response-actions-list',
|
||||
'data-test-height': dataTestHeight,
|
||||
}) => {
|
||||
const { pagination: paginationFromUrlParams, setPagination: setPaginationOnUrlParams } =
|
||||
useUrlPagination();
|
||||
|
@ -295,6 +297,7 @@ export const ResponseActionsLog = memo<
|
|||
onTimeChange={onTimeChange}
|
||||
showHostsFilter={showHostNames}
|
||||
data-test-subj={dataTestSubj}
|
||||
data-test-height={dataTestHeight}
|
||||
/>
|
||||
{isFetched && !totalItemCount ? (
|
||||
<ManagementEmptyStateWrapper>
|
||||
|
|
|
@ -63,7 +63,7 @@ describe(
|
|||
|
||||
// No access to response actions (except `unisolate`)
|
||||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter(
|
||||
(apiName) => apiName !== 'unisolate'
|
||||
(apiName) => apiName !== 'unisolate' && apiName !== 'runscript'
|
||||
)) {
|
||||
it(`should not allow access to Response Action: ${actionName}`, () => {
|
||||
ensureResponseActionAuthzAccess('none', actionName, username, password);
|
||||
|
@ -88,7 +88,7 @@ describe(
|
|||
|
||||
// No access to response actions (except `unisolate`)
|
||||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter(
|
||||
(apiName) => apiName !== 'unisolate'
|
||||
(apiName) => apiName !== 'unisolate' && apiName !== 'runscript'
|
||||
)) {
|
||||
it(`should not allow access to Response Action: ${actionName}`, () => {
|
||||
ensureResponseActionAuthzAccess('none', actionName, username, password);
|
||||
|
|
|
@ -63,7 +63,7 @@ describe(
|
|||
|
||||
// No access to response actions (except `unisolate`)
|
||||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter(
|
||||
(apiName) => apiName !== 'unisolate'
|
||||
(apiName) => apiName !== 'unisolate' && apiName !== 'runscript'
|
||||
)) {
|
||||
it(`should not allow access to Response Action: ${actionName}`, () => {
|
||||
ensureResponseActionAuthzAccess('none', actionName, username, password);
|
||||
|
@ -88,7 +88,7 @@ describe(
|
|||
|
||||
// No access to response actions (except `unisolate`)
|
||||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter(
|
||||
(apiName) => apiName !== 'unisolate'
|
||||
(apiName) => apiName !== 'unisolate' && apiName !== 'runscript'
|
||||
)) {
|
||||
it(`should not allow access to Response Action: ${actionName}`, () => {
|
||||
ensureResponseActionAuthzAccess('none', actionName, username, password);
|
||||
|
|
|
@ -70,7 +70,7 @@ describe(
|
|||
}
|
||||
|
||||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter(
|
||||
(apiName) => apiName !== 'unisolate'
|
||||
(apiName) => apiName !== 'unisolate' && apiName !== 'runscript'
|
||||
)) {
|
||||
it(`should not allow access to Response Action: ${actionName}`, () => {
|
||||
ensureResponseActionAuthzAccess('none', actionName, username, password);
|
||||
|
@ -99,7 +99,7 @@ describe(
|
|||
});
|
||||
|
||||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter(
|
||||
(apiName) => apiName !== 'unisolate'
|
||||
(apiName) => apiName !== 'unisolate' && apiName !== 'runscript'
|
||||
)) {
|
||||
it(`should not allow access to Response Action: ${actionName}`, () => {
|
||||
ensureResponseActionAuthzAccess('none', actionName, username, password);
|
||||
|
|
|
@ -43,9 +43,7 @@ describe(
|
|||
// This is not needed for this test, but it's a good example of
|
||||
// how to enable experimental features in the Cypress tests.
|
||||
// kbnServerArgs: [
|
||||
// `--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
// 'featureFlagName',
|
||||
// ])}`,
|
||||
// `--xpack.securitySolution.enableExperimental=${JSON.stringify(['featureFlagName'])}`,
|
||||
// ],
|
||||
},
|
||||
},
|
||||
|
@ -127,6 +125,8 @@ describe(
|
|||
'get-file',
|
||||
'upload',
|
||||
'scan'
|
||||
// TODO: currently not implemented for Endpoint
|
||||
// 'runscript'
|
||||
);
|
||||
|
||||
const deniedResponseActions = pick(consoleHelpPanelResponseActionsTestSubj, 'execute');
|
||||
|
|
|
@ -15,7 +15,8 @@ const TEST_SUBJ = Object.freeze({
|
|||
});
|
||||
|
||||
export const getConsoleHelpPanelResponseActionTestSubj = (): Record<
|
||||
ConsoleResponseActionCommands,
|
||||
// TODO: currently runscript is not supported in Endpoint
|
||||
Exclude<ConsoleResponseActionCommands, 'runscript'>,
|
||||
string
|
||||
> => {
|
||||
return {
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
GET_PROCESSES_ROUTE,
|
||||
ISOLATE_HOST_ROUTE_V2,
|
||||
KILL_PROCESS_ROUTE,
|
||||
RUN_SCRIPT_ROUTE,
|
||||
SCAN_ROUTE,
|
||||
SUSPEND_PROCESS_ROUTE,
|
||||
UNISOLATE_HOST_ROUTE_V2,
|
||||
|
@ -274,6 +275,11 @@ export const ensureResponseActionAuthzAccess = (
|
|||
Object.assign(apiPayload, { parameters: { path: 'scan/two' } });
|
||||
break;
|
||||
|
||||
case 'runscript':
|
||||
url = RUN_SCRIPT_ROUTE;
|
||||
Object.assign(apiPayload, { parameters: { Raw: 'ls' } });
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Response action [${responseAction}] has no API payload defined`);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,11 @@ const COMMANDS_WITH_ACCESS_TO_FILES: CommandsWithFileAccess = deepFreeze<Command
|
|||
sentinel_one: false,
|
||||
crowdstrike: false,
|
||||
},
|
||||
runscript: {
|
||||
endpoint: false,
|
||||
sentinel_one: false,
|
||||
crowdstrike: false,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -319,7 +319,7 @@ describe('When using `getActionList()', () => {
|
|||
outputs: {
|
||||
'agent-a': {
|
||||
content: {
|
||||
code: 'ra_scan_success_done',
|
||||
code: '200',
|
||||
},
|
||||
type: 'json',
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@ export const FEATURE_KEYS = {
|
|||
SCAN: 'Scan files',
|
||||
ALERTS_BY_PROCESS_ANCESTRY: 'Get related alerts by process ancestry',
|
||||
ENDPOINT_EXCEPTIONS: 'Endpoint exceptions',
|
||||
RUN_SCRIPT: 'Run script',
|
||||
} as const;
|
||||
|
||||
export type FeatureKeys = keyof typeof FEATURE_KEYS;
|
||||
|
@ -41,6 +42,7 @@ const RESPONSE_ACTIONS_FEATURE_KEY: Readonly<Record<ResponseActionsApiCommandNam
|
|||
execute: 'EXECUTE',
|
||||
upload: 'UPLOAD',
|
||||
scan: 'SCAN',
|
||||
runscript: 'RUN_SCRIPT',
|
||||
};
|
||||
|
||||
export const getResponseActionFeatureKey = (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue