-
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
[Security Solution][Endpoint] Upload response action create API #156303
Changes from all commits
8cd5527
0dfb681
eb51dad
59f12ec
38420d7
a2887e9
2dfe9ba
3a02739
82859a7
ad57cf7
e0b154d
086d286
11e9590
d905916
6f1736b
8ffb88b
e35196d
7135355
25c353a
ad0e3dc
71118bf
e331bcc
fff1737
4d5bd65
a7586bb
5ed98c0
9969053
098ab0e
1f3bd9b
e32193f
3fb73fe
b15e022
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,8 @@ import type { | |
ResponseActionGetFileParameters, | ||
ResponseActionsExecuteParameters, | ||
ResponseActionExecuteOutputContent, | ||
ResponseActionUploadOutputContent, | ||
ResponseActionUploadParameters, | ||
} from '../types'; | ||
import { ActivityLogItemTypes } from '../types'; | ||
import { | ||
|
@@ -239,6 +241,24 @@ export class EndpointActionGenerator extends BaseDataGenerator { | |
} | ||
} | ||
|
||
if (command === 'upload') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't you prepare an enum with all command names? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't really like using |
||
if (!details.parameters) { | ||
( | ||
details as ActionDetails< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the cast? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes because in order to set the parameters below, the |
||
ResponseActionUploadOutputContent, | ||
ResponseActionUploadParameters | ||
> | ||
).parameters = { | ||
file: { | ||
file_id: 'file-x-y-z', | ||
file_name: 'foo.txt', | ||
size: 1234, | ||
sha256: 'file-hash-sha-256', | ||
}, | ||
}; | ||
} | ||
} | ||
|
||
return merge(details, overrides as ActionDetails) as unknown as ActionDetails< | ||
TOutputType, | ||
TParameters | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -13,7 +13,10 @@ import { | |||||
NoParametersRequestSchema, | ||||||
KillOrSuspendProcessRequestSchema, | ||||||
ExecuteActionRequestSchema, | ||||||
UploadActionRequestSchema, | ||||||
} from './actions'; | ||||||
import { createHapiReadableStreamMock } from '../../../server/endpoint/services/actions/mocks'; | ||||||
import type { HapiReadableStream } from '../../../server/types'; | ||||||
|
||||||
describe('actions schemas', () => { | ||||||
describe('Endpoint action list API Schema', () => { | ||||||
|
@@ -639,4 +642,56 @@ describe('actions schemas', () => { | |||||
}).not.toThrow(); | ||||||
}); | ||||||
}); | ||||||
|
||||||
describe(`UploadActionRequestSchema`, () => { | ||||||
let fileStream: HapiReadableStream; | ||||||
|
||||||
beforeEach(() => { | ||||||
fileStream = createHapiReadableStreamMock(); | ||||||
}); | ||||||
|
||||||
it('should not error if `override` parameter is not defined', () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will correct in next PR. 👍 |
||||||
expect(() => { | ||||||
UploadActionRequestSchema.body.validate({ | ||||||
endpoint_ids: ['endpoint_id'], | ||||||
file: fileStream, | ||||||
}); | ||||||
}).not.toThrow(); | ||||||
}); | ||||||
|
||||||
it('should allow `override` parameter', () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here. Thanks for catching this |
||||||
expect(() => { | ||||||
UploadActionRequestSchema.body.validate({ | ||||||
endpoint_ids: ['endpoint_id'], | ||||||
parameters: { | ||||||
overwrite: true, | ||||||
}, | ||||||
file: fileStream, | ||||||
}); | ||||||
}).not.toThrow(); | ||||||
}); | ||||||
|
||||||
it('should error if `file` is not defined', () => { | ||||||
expect(() => { | ||||||
UploadActionRequestSchema.body.validate({ | ||||||
endpoint_ids: ['endpoint_id'], | ||||||
parameters: { | ||||||
override: true, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😱 |
||||||
}, | ||||||
}); | ||||||
}).toThrow(); | ||||||
}); | ||||||
|
||||||
it('should error if `file` is not a Stream', () => { | ||||||
expect(() => { | ||||||
UploadActionRequestSchema.body.validate({ | ||||||
endpoint_ids: ['endpoint_id'], | ||||||
parameters: { | ||||||
overwrite: true, | ||||||
}, | ||||||
file: {}, | ||||||
}); | ||||||
}).toThrow(); | ||||||
}); | ||||||
}); | ||||||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -239,3 +239,17 @@ export const ResponseActionBodySchema = schema.oneOf([ | |
EndpointActionGetFileSchema.body, | ||
ExecuteActionRequestSchema.body, | ||
]); | ||
|
||
export const UploadActionRequestSchema = { | ||
body: schema.object({ | ||
...BaseActionRequestSchema, | ||
|
||
parameters: schema.object({ | ||
overwrite: schema.maybe(schema.boolean()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be a default value of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I can set it a default in next PR |
||
}), | ||
|
||
file: schema.stream(), | ||
}), | ||
}; | ||
|
||
export type UploadActionRequestBody = TypeOf<typeof UploadActionRequestSchema.body>; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ export const RESPONSE_ACTION_API_COMMANDS_NAMES = [ | |
'running-processes', | ||
'get-file', | ||
'execute', | ||
'upload', | ||
] as const; | ||
|
||
export type ResponseActionsApiCommandNames = typeof RESPONSE_ACTION_API_COMMANDS_NAMES[number]; | ||
|
@@ -36,6 +37,7 @@ export const ENDPOINT_CAPABILITIES = [ | |
'running_processes', | ||
'get_file', | ||
'execute', | ||
'upload_file', | ||
] as const; | ||
|
||
export type EndpointCapabilities = typeof ENDPOINT_CAPABILITIES[number]; | ||
|
@@ -52,6 +54,7 @@ export const CONSOLE_RESPONSE_ACTION_COMMANDS = [ | |
'processes', | ||
'get-file', | ||
'execute', | ||
'upload', | ||
] as const; | ||
|
||
export type ConsoleResponseActionCommands = typeof CONSOLE_RESPONSE_ACTION_COMMANDS[number]; | ||
|
@@ -74,6 +77,7 @@ export const commandToRBACMap: Record<ConsoleResponseActionCommands, ResponseCon | |
processes: 'writeProcessOperations', | ||
'get-file': 'writeFileOperations', | ||
execute: 'writeExecuteOperations', | ||
upload: 'writeFileOperations', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this should be right after |
||
}); | ||
|
||
export const RESPONSE_ACTION_API_COMMANDS_TO_CONSOLE_COMMAND_MAP = Object.freeze< | ||
|
@@ -86,6 +90,7 @@ export const RESPONSE_ACTION_API_COMMANDS_TO_CONSOLE_COMMAND_MAP = Object.freeze | |
'running-processes': 'processes', | ||
'kill-process': 'kill-process', | ||
'suspend-process': 'suspend-process', | ||
upload: 'upload', | ||
}); | ||
|
||
// 4 hrs in seconds | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ import type { | |
NoParametersRequestSchema, | ||
ResponseActionBodySchema, | ||
KillOrSuspendProcessRequestSchema, | ||
UploadActionRequestBody, | ||
} from '../schema/actions'; | ||
import type { | ||
ResponseActionStatus, | ||
|
@@ -176,7 +177,8 @@ export type EndpointActionDataParameterTypes = | |
| undefined | ||
| ResponseActionParametersWithPidOrEntityId | ||
| ResponseActionsExecuteParameters | ||
| ResponseActionGetFileParameters; | ||
| ResponseActionGetFileParameters | ||
| ResponseActionUploadParameters; | ||
|
||
export interface EndpointActionData< | ||
TParameters extends EndpointActionDataParameterTypes = EndpointActionDataParameterTypes, | ||
|
@@ -468,3 +470,25 @@ export type UploadedFileInfo = Pick< | |
export interface ActionFileInfoApiResponse { | ||
data: UploadedFileInfo; | ||
} | ||
|
||
/** | ||
* The parameters that are sent to the Endpoint. | ||
* | ||
* NOTE: Most of the parameters below are NOT accepted via the API. They are inserted into | ||
* the action's parameters via the API route handler | ||
*/ | ||
Comment on lines
+477
to
+479
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔥 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also mention in the note above, which parameters are provided in the API? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it is necessary. One can just go look at the API parameters type/schema |
||
export type ResponseActionUploadParameters = UploadActionRequestBody['parameters'] & { | ||
file: { | ||
sha256: string; | ||
size: number; | ||
file_name: string; | ||
file_id: string; | ||
}; | ||
}; | ||
|
||
export interface ResponseActionUploadOutputContent { | ||
/** Full path to the file on the host machine where it was saved */ | ||
path: string; | ||
/** The free space available (after saving the file) of the drive where the file was saved to, In Bytes */ | ||
disk_free_space: number; | ||
} |
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 out of curiosity: why clone?
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 used
clone()
so that the data being return by this mock is not a reference to the data stored in the instance. Thus, the data returned can be Mutated (in a tests) without impacting the state of theFile
class instance