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] New route for create an exception list and return the existing one if it already exists #139618

Conversation

dasansol92
Copy link
Contributor

@dasansol92 dasansol92 commented Aug 29, 2022

Summary

  • Adds new internal route for creating an exception list that skips the list creation if it already exists so we avoid 409 errors.
  • Common code has been moved into a handler that is used in both routes.
  • Exception list api client (management) has been updated to use the new route and avoid 409 errors in frontend when checking if the list has been created.

Solves #137577

For maintainers

@dasansol92 dasansol92 added release_note:skip Skip the PR/issue when compiling release notes backport:skip This commit does not require backporting Team:Defend Workflows “EDR Workflows” sub-team of Security Solution v8.5.0 labels Aug 29, 2022
@dasansol92
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Contributor

@paul-tavares paul-tavares left a comment

Choose a reason for hiding this comment

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

awesome job at extracting out the portion of code that actually creates the list.

My main comment is around avoid having to define the URL param for the internal route (no need for it in my mind) and maybe renaming the route to provide a clearer picture of what it does.


export const internalCreateExceptionListQuerySchema = t.exact(
t.partial({
ignore_existing: DefaultStringBooleanFalse,
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the new route is internal, why now just make the POST API call behave the way we want it by default and avoid this param all together?

I would say: remove this URL param and just have the internal handler NOT return a 409. YOu might need to add another param to the createExceptionListHandler() function you created that contains the extracted/reusable code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also - if you decide to keep this, can you add documentation to this prop that states what it does when defined in the API params

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, that make sense to me. My intention here was having something more generic but it does not make sense right now and it also adds an extra level of complexity we can get rid of.
Will remove the param and adjust the route naming to be more specific. Thanks!

/**
* Internal exception list routes
*/
export const INTERNAL_EXCEPTION_LIST_URL = `/internal${EXCEPTION_LIST_URL}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe name the route more specific so that it's clear to us that it differs from the normal "create"? Maybe:

/internal${EXCEPTION_LIST_URL}/_ensure_created;

??

Copy link
Contributor

Choose a reason for hiding this comment

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

Just noticed further below that the actual route has /_create on it. Can you instead define that here so that we don't have to hard coded everywhere we need it? (and still consider the name change if it makes sense)

Like:

/**
 * Ensure that a given list is created. If already created, then the existing list definition is returned (no 409 error). 
 * If not yet created, then it will be created.
 */
export const INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL = `${INTERNAL_EXCEPTION_LIST_URL}/_ensure_created`;

Copy link
Contributor

Choose a reason for hiding this comment

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

Would we consider not adding _ensure... if we foresee using this pattern for other routes - like an internal _update route that's for internally managed exception lists.

const siemResponse = buildSiemResponse(response);
try {
return await createExceptionListHandler(context, request, response, siemResponse);
} catch (err) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need the try {} catch ()?
createExceptionListHandler() seems to already do all fo this.

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, it will catch all errors inside the handler and will return a proper http error if something throws.

options: {
tags: ['access:lists-all'],
},
path: `${INTERNAL_EXCEPTION_LIST_URL}/_create`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of defining this actual route here, can you create a const for it next to INTERNAL_EXCEPTION_LIST_URL?

@dasansol92 dasansol92 marked this pull request as ready for review August 30, 2022 08:55
@dasansol92 dasansol92 requested review from a team as code owners August 30, 2022 08:55
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-onboarding-and-lifecycle-mgt (Team:Onboarding and Lifecycle Mgt)

@dasansol92
Copy link
Contributor Author

@elasticmachine merge upstream

path: INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL,
validate: {
body: buildRouteValidation<
typeof createExceptionListSchema,
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of the following:

  • Update the public create route to restrict the list types that can be used to only those that can be publicly managed (which I think right now is just detection?)
  • Update this path to accept only list types that are internally managed

Might make each routes intent very clear to devs and avoid someone accidentally using them interchangeably?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This sounds good to me. Will add it for the internal one.

EXCEPTION_LIST_ITEM_URL,
EXCEPTION_LIST_URL,
INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL,
} from '@kbn/securitysolution-list-constants';
import type { HttpStart } from '@kbn/core/public';
import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from '../../common/constants';

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any chance of updating this client name from ExceptionsListApiClient to something like ArtifactManagementExceptionsListApiClient - something indicating it's use for artifacts. At first glance I thought you were updating the main exceptions list client (an instance of which is also used throughout detections).

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! Let me discuss this with the rest of the team. I'm not 100% sure but this refactor could be big so I would suggest doing it in another pr soon instead of including this change here.

…-137577' of github.com:dasansol92/kibana into feat/olm-new_list_endpoint_for_creating_exception_lists-137577
@yctercero
Copy link
Contributor

From our team sync today:

  • What about using the GET to check in all these places instead of creating a new route?

cc @elastic/security-solution-platform

t.partial({
// TODO: Move the ALL_ENDPOINT_ARTIFACT_LIST_IDS inside the package and use it here instead
list_id: t.keyof({
endpoint_trusted_apps: null,
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we're limiting the use of this API to only these list_id's, correct?

if so, could we use the const that have the list id's defined here so that they remain in sync?

@dasansol92
Copy link
Contributor Author

@yctercero

  • What about using the GET to check in all these places instead of creating a new route?

We have already talked about this and using the existing GET to check if a list exists means having to do an extra call to create it if it does not exists yet.
That being said, this extra call would be done only the first time user visits an artifact page, once it is created, only the GET to ensure it exists would be done.
cc: @paul-tavares

@paul-tavares
Copy link
Contributor

@yctercero confused on what you mean by GET?

We're trying to avoid having the API send back an Error if the list already exists. If we do a GET against a list that does not exists, we get an API error.

So today, we do a POST to the create list and ignore 409 in our code - but this still sends back an API error, which if you look at the original issue that triggered this PR, that is what we are trying to avoid. Having that Error be return in the API is giving others in Kibana (when investigating issues) a false sense that perhaps whatever issue they are investigating is due to our API error.

@yctercero
Copy link
Contributor

@paul-tavares we were suggesting what @dasansol92 had mentioned in that you would only get an error once using a GET on the initial call to see if it exists and then every subsequent call to it would not return an error, just the list you're asking for. It seemed that core's issue with it was that you were seeing that 409 on every route change.

Just a suggestion as it avoids making any changes other than one additional call on your end. I'm not opposed to continuing with this PR but @nkhristinin had asked about just using a GET and I didn't have a good reason of why not so was curious what you all thought.

@paul-tavares
Copy link
Contributor

@yctercero It will not address the original problem - we don't want to get an API error for something that we feel is a work-around to limitations in the Lists functionality (more on this below). We want to: check if our internal use only list exists and if not, then created it. Doing a GET instead of a POST to create the list does not resolve the problem because it still returns an error if the list does not exist.

To clarify: our UI code has no issues with the error that currently comes back (409). But because we now have to do some checks upfront when kibana first loads in support of the global search and navigation bars, this API error is being seen in the browser developer tools console and network tabs by other apps in kibana and that leads folks to believe there is a problem (see my comment here)

We can revert these changes here if the team does not feel this is needed in Lists plugin and by its consumers. We can always just create our own API under /api/endpoint that can do this for us. But it really does does feel like it belongs here.


About the Root Cause

The root cause of this work-around is the fact that Lists does not allow/support "reserved" list IDs. We have to ensure these types of internal lists are first created before we can use them. Because of this limitation, we are forced to work-around this problem by ensuring that we create the List (and ignore 409 if it already exists) before we can start to use it.

Personally - I would have liked that List introduced support for "internal" list names. I suggested this in multiple exchanges in the past and issue 109 (internal) and issue 982 (internal) remain open with that suggestion.

Some history

When our team first started working with the Lists plugins (Trusted apps) we actually went down the path of ensuring the list is created as soon as Kibana comes up so that we were not forced to always first ensure the list is created when attempting to consume the data, but ended up reverting that based on this comment from Frank


export const internalCreateExceptionListSchema = t.intersection([
t.exact(
t.type({
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice - thanks for these updates!

Copy link
Contributor

@yctercero yctercero left a comment

Choose a reason for hiding this comment

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

Code LGTM! Thanks @paul-tavares for the back and forth - just wanted to make sure we'd explored using what we already have before adding more code. But changes look good and clean!

@dasansol92
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Contributor

@paul-tavares paul-tavares left a comment

Choose a reason for hiding this comment

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

Changes look goo to me.

Q. Did you test the original issue when the user has no permissions to endpoint? Wondering if the creation of the list will actually cause an Error due to Auth. And if so, maybe we need to make some additional changes on the UI code to not call the API if user has no Authz. to endpoint (or maybe that is already done?)

@dasansol92
Copy link
Contributor Author

@paul-tavares Did a fix here cf631c4 to avoid doing create call when it is not needed or does not has permissions to do it.

It will still being an API error when accessing security solutions (without permissions) because this hook as the instance is created and the API call done at the beginning. Let me know if that make sense.

@dasansol92
Copy link
Contributor Author

@elasticmachine merge upstream

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
lists 308 309 +1
securitySolution 3054 3055 +1
total +2

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/securitysolution-io-ts-list-types 449 452 +3
@kbn/securitysolution-list-constants 12 13 +1
total +4

Async chunks

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

id before after diff
lists 148.4KB 148.8KB +449.0B
securitySolution 6.4MB 6.4MB +347.0B
total +796.0B
Unknown metric groups

API count

id before after diff
@kbn/securitysolution-io-ts-list-types 462 465 +3
@kbn/securitysolution-list-constants 26 28 +2
total +5

History

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

Copy link
Contributor

@paul-tavares paul-tavares left a comment

Choose a reason for hiding this comment

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

🎉
🚢 it

core.http
);

if (!privileges.canAccessEndpointManagement) {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 🙏

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.

Tested it out and it works as it should. I only have a minor nit. Feel free to 🚢 it.

type,
version,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
Copy link
Member

Choose a reason for hiding this comment

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

nit: Better to name this exceptionListsClient.

@dasansol92 dasansol92 merged commit e459752 into elastic:main Sep 8, 2022
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 Team:Defend Workflows “EDR Workflows” sub-team of Security Solution v8.5.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants