Skip to content
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

Conversation

paul-tavares
Copy link
Contributor

@paul-tavares paul-tavares commented May 1, 2023

Summary

  • New api to create upload response action request. API route mounted at: /api/endpoint/action/upload
  • New server config value - maxUploadResponseActionFileBytes - that controls how large uploaded files can be. Defaults to 25MB. Max is 100MB
  • New feature flag to control upload response action: responseActionUploadEnabled. Must be set to true in kibana yaml in order to access this new API

API Request body (MUST BE SENT AS multipart/form-data). JSON representation of it below:

{
  file: FileBlob
  endpoint_ids: Array<string>
  /* other optional action params */
}

File Created (in .fleet-files-endpoint index)

      {
        "_index": ".fleet-files-endpoint",
        "_id": "f4369611-09ce-4a4f-851c-49542b70fac1",
        "_score": 1,
        "_source": {
          "file": {
            "created": "2023-05-02T15:05:28.529Z",
            "Status": "READY",
            "Updated": "2023-05-02T15:05:28.529Z",
            "name": "olm-6308-endpoint-agent-status.gif",
            "mime_type": "image/gif",
            "Meta": {
              "target_agents": [
                "63e7a9cf-5945-417d-89b8-027294b7a4f0"
              ],
              "action_id": "3c8c3b3e-1f18-4ab0-96e4-dd0b6c176621"
            },
            "extension": "gif",
            "FileKind": "none",
            "size": 410104,
            "hash": {
              "sha256": "231b8d932da84849f8d16a96712d434aa21c72cbf1d83845e34a0b8a54883b5a"
            }
          }
        }
      }

Checklist

@paul-tavares paul-tavares added release_note:skip Skip the PR/issue when compiling release notes Team:Defend Workflows “EDR Workflows” sub-team of Security Solution v8.9.0 labels May 1, 2023
@paul-tavares paul-tavares self-assigned this May 1, 2023
@paul-tavares paul-tavares requested review from pzl and tomsonpl May 3, 2023 18:59
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-defend-workflows (Team:Defend Workflows)

@paul-tavares paul-tavares requested a review from xcrzx May 3, 2023 18:59
…d-response-action-api

# Conflicts:
#	x-pack/plugins/security_solution/server/endpoint/services/actions/create/index.ts
Copy link
Contributor

@xcrzx xcrzx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rules Area changes LGTM

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
securitySolution 9.1MB 9.1MB +278.0B

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
securitySolution 61.6KB 61.6KB +31.0B
Unknown metric groups

ESLint disabled line counts

id before after diff
enterpriseSearch 19 21 +2
securitySolution 398 402 +4
total +6

Total ESLint disabled count

id before after diff
enterpriseSearch 20 22 +2
securitySolution 478 482 +4
total +6

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @paul-tavares

Copy link
Contributor

@tomsonpl tomsonpl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, left a few questions 👍

@@ -56,7 +57,9 @@ export const createFileMock = (): DeeplyMockedKeys<File> => {
share: jest.fn(),
listShares: jest.fn(),
unshare: jest.fn(),
toJSON: jest.fn(),
toJSON: jest.fn(() => {
return clone(fileMock.data);
Copy link
Contributor

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?

Copy link
Contributor Author

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 the File class instance

@@ -239,6 +241,24 @@ export class EndpointActionGenerator extends BaseDataGenerator {
}
}

if (command === 'upload') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you prepare an enum with all command names?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't really like using Enums from TS. They are a bit strange to use and I often find my self casting the type of a string to match the Enum definition when attempting to look up a value in the Enum

if (command === 'upload') {
if (!details.parameters) {
(
details as ActionDetails<
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the cast?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes because in order to set the parameters below, the ActionDetails must be an upload action details, so the cast here using the output content and parameters type does that.

});

it('should NOT register route if feature flag is false', () => {
// @ts-expect-error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any specific error here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, assignment to a Readonly property. I'll have to adjust the test render mock at some point to perhaps not set these to readonly so that we don't have to do this.

await file.update({
meta: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...file.data.meta!,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we get rid of the bang somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see in the next PR is I can somehow narrow the type. The issue here is that meta is optional in the Files plugin interface, but required in our use case. Will look into it.

@@ -77,10 +78,13 @@ export const actionCreateService = (
return createAction({ ...payload }, { minimumLicenseRequired: 'enterprise' });
};

const createAction = async (
const createAction = async <
TOutputContent extends object = object,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TILT: Could you explain the syntax? what is this = object? A default type if the type was not provided or anything else? Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly. This is the definition of a generic where they generic types have default values, thus you are not required to set them if attempting to use the method, but could be set if you want to narrow down the ActionDetails type that is returned.

More about generics here.

Copy link
Contributor

@tomsonpl tomsonpl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, left a few questions 👍

Copy link
Member

@ashokaditya ashokaditya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've a few nits and questions, but otherwise it is looking good to 🚢

fileStream = createHapiReadableStreamMock();
});

it('should not error if `override` parameter is not defined', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: overwrite parameter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will correct in next PR. 👍

}).not.toThrow();
});

it('should allow `override` parameter', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: overwrite

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. Thanks for catching this

UploadActionRequestSchema.body.validate({
endpoint_ids: ['endpoint_id'],
parameters: {
override: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
override: true,
overwrite: true,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😱
Good catch. this was actually probably throw'ing an incorrect error for what the test is attempting to validate. Will correct it in next PR.

...BaseActionRequestSchema,

parameters: schema.object({
overwrite: schema.maybe(schema.boolean()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a default value of false for overwrite here. No?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I can set it a default in next PR

@@ -74,6 +77,7 @@ export const commandToRBACMap: Record<ConsoleResponseActionCommands, ResponseCon
processes: 'writeProcessOperations',
'get-file': 'writeFileOperations',
execute: 'writeExecuteOperations',
upload: 'writeFileOperations',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be right after get-file just so that all writeFileOperations are bunched together.

Comment on lines +477 to +479
* 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
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥

Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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


uploadParameters.file.file_id = createdFile.file.id;
uploadParameters.file.file_name = createdFile.file.name;
uploadParameters.file.sha256 = createdFile.file.hash?.sha256;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uploadParameters.file.sha256 = createdFile.file.hash?.sha256;
uploadParameters.file.sha256 = createdFile.file.hash.sha256;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file.hash is an optional attribute.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. I saw it typed as a required string in the PR so I suggested the change.

{ casesClient }
);

await setFileActionId(esClient, logger, data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: What happens if the setFileActionId fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we fail the API request. But - thats a good point that I will need to think through. At this point in the code, a failure is unlikely, but perhaps if it happens to fail here, we just catch it and log it, but allow the api call to succeed, since the action has already been "sent" to the Endpoint. Failing here as is right now will cause the file to be deleted (in the catch() below)` which would in turn cause the endpoint to fail to down the file.

Good catch @ashokaditya . Will correct it in next PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. Sounds good with later updates. 🚀

@paul-tavares paul-tavares merged commit cc781e6 into elastic:main May 9, 2023
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label May 9, 2023
@paul-tavares paul-tavares deleted the task/olm-5369-upload-response-action-api branch May 9, 2023 13:39
jbudz added a commit that referenced this pull request May 9, 2023
@jbudz
Copy link
Member

jbudz commented May 9, 2023

@paul-tavares
Copy link
Contributor Author

Trying again:

#157182

🤞

paul-tavares added a commit that referenced this pull request May 9, 2023
…ommit) (#157182)

## Summary

This is a re-commit of PR #156303 which was reverted due to a type error
that slipped passed the last PR.
paul-tavares added a commit to paul-tavares/kibana that referenced this pull request May 9, 2023
paul-tavares added a commit to paul-tavares/kibana that referenced this pull request May 9, 2023
paul-tavares added a commit to paul-tavares/kibana that referenced this pull request May 10, 2023
commit c899f1d
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Tue May 9 17:30:04 2023 -0400

    Fix execute mapped authz property

commit 4c460c5
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Tue May 9 16:07:39 2023 -0400

    fix sending `undefined` for `comment`

commit 02f9d07
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Tue May 9 11:47:03 2023 -0400

    Upload API: log failure to file's action ID and still allow API response to return success

commit 5d6990c
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Tue May 9 11:40:44 2023 -0400

    add default value to `overwrite` param of upload command (feedback from PR elastic#156303)

commit a44b08b
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Tue May 9 11:39:33 2023 -0400

    fix tests (feedback from PR elastic#156303)

commit c221c56
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Tue May 9 10:02:42 2023 -0400

    tests for upload result component

commit 955c706
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Mon May 8 17:44:33 2023 -0400

    tests for upload action handler component

commit f5a0bae
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Mon May 8 15:37:55 2023 -0400

    tests for argument `mustHaveValue` validators

commit b9d9525
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Mon May 8 09:43:31 2023 -0400

    test scaffold for `mustHaveValue` console argument value validators

commit e6df2cf
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 17:01:54 2023 -0400

    fix type error

commit fb574e1
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 16:46:53 2023 -0400

    Support upload output for actions sent to multiple agents

commit d578647
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 16:45:38 2023 -0400

    Fix style in Console to ensure that whitespace is preserved

commit 9c436d2
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 13:44:32 2023 -0400

    add command `upload` result to the UI

commit 2f9c07f
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 13:42:48 2023 -0400

    Added `upload` output data to endpint generator when generating repsonse

commit ca6596a
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 10:40:20 2023 -0400

    Add `truthy` validation to the `file` argument

commit 0f43c54
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 10:31:00 2023 -0400

    Console: add new `mustHaveValue` argument value validation of `truthy`

commit 0044d5c
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Fri May 5 09:51:58 2023 -0400

    UI: hook to build and send request + command sends action to server

commit ee91d9d
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Thu May 4 17:42:39 2023 -0400

    Add `upload` command to list of console commands

commit d8adbf1
Author: Paul Tavares <paul.tavares@elastic.co>
Date:   Thu May 4 16:55:52 2023 -0400

    Removed `commandToCapabilitiesPrivilegesMap` and started using const's
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting release_note:skip Skip the PR/issue when compiling release notes reverted Team:Defend Workflows “EDR Workflows” sub-team of Security Solution v8.9.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants