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] Creates generic policy tab artifact component to be used for all of our artifacts #126685

Conversation

dasansol92
Copy link
Contributor

@dasansol92 dasansol92 commented Mar 2, 2022

Summary

  • Pending to add all unit tests for generic components.
  • Pending to remove old code (maybe will move this to a subsequent PR).
  • Pending to add host isolation exceptions privileges checks.
  • Pending to use url hook to get url params instead of redux.

It includes a generic component that renders policy artifact tab depending on artifact type. This component will allow us add features easily and faster across all tabs and add new artifact types.

Usage within PolicyTabs component:

          <PolicyArtifactsLayout
              // The policy item 
              policyItem={policyItem}
              // A function that returns the desired api client instance. Must be a function since tabs are rendered when component mounts and we want to get a new instance when focusing the desired tab.
              getExceptionsListApiClient={() => TrustedAppsApiClient.getInstance(http)}
              // A set of required and optional labels. [See this for reference](https://github.com/elastic/kibana/pull/126685/files#diff-c9deb5f70603c5052d77835dc4307e15f97fae44724d9b3270bf78acbc1688c5)
              labels={POLICY_ARTIFACT_TRUSTED_APPS_LABELS}
              // An array of fields exceptions are searchable on
              searcheableFields={TRUSTED_APPS_SEARCHABLE_FIELDS}
              // A function to get the artifact list url path
              getArtifactPath={getTrustedAppsListPath}
              // A function to get the policy artifact tab url path
              getPolicyArtifactsPath={getPolicyDetailsArtifactsListPath}
              // An optional boolean to define extra privileges (for host isolation exceptions for example)
              externalPrivileges={aBooleanToCheckExternalPrivileges}
            />

For maintainers

@dasansol92 dasansol92 added release_note:skip Skip the PR/issue when compiling release notes Team:Defend Workflows “EDR Workflows” sub-team of Security Solution v8.2.0 labels Mar 2, 2022
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.

some initial feedback. Did not get very far (stoped at policy_artifacts_layout.tsx). will pick it up again tomorrow

@dasansol92 dasansol92 marked this pull request as ready for review March 7, 2022 15:58
@dasansol92 dasansol92 requested a review from a team as a code owner March 7, 2022 15:58
@dasansol92 dasansol92 requested review from pzl and parkiino March 7, 2022 15:58
@elasticmachine
Copy link
Contributor

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

Comment on lines 46 to 49
onSettled: () => {
queryClient.invalidateQueries(['list', apiClient]);
onCancel();
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Just want to make sure this code is correct. As I understand it, onSettled is called everytime - regardless of success or error. So even if it errors, you are invalidating queries and you are calling onCancel() (why call onCancel on a failure?).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, we should keep the query cache and don't close the modal if something has failed. Thanks!

policyName: string;
apiClient: ExceptionsListApiClient;
exception: ExceptionListItemSchema;
onCancel: () => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the code below, I wonder if this should be named onClose. It seems to be used in both success and failure cases and when user cancels the modal

const handleModalConfirm = useCallback(() => {
const modifiedException = {
...exception,
tags: exception.tags.filter((tag) => tag !== `policy:${policyId}`),
Copy link
Contributor

Choose a reason for hiding this comment

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

can you use the const for the by policy prefix instead of hardcoding policy:

},
onSettled: () => {
queryClient.invalidateQueries(['list', apiClient]);
onClose();
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

let mockedApi: ReturnType<typeof eventFiltersListQueryHttpMock>;
let history: AppContextTestRender['history'];

const eventFiltersLabels = {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you convert this to a function that returns the object, so that we avoid sharing state between tests

[navigateCallback]
);

const handleOnExpandCollapse: ArtifactCardGridProps['onExpandCollapse'] = ({
Copy link
Contributor

Choose a reason for hiding this comment

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

for this one and more below - should wrap them in useCallback()

}, [artifacts?.data.length, labels]);

const artifactCardPolicies = useEndpointPoliciesToArtifactPolicies(policiesRequest.data?.items);
const provideCardProps: ArtifactCardGridProps['cardComponentProps'] = (artifact) => {
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
Contributor Author

Choose a reason for hiding this comment

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

Is this one useArtifactCardPropsProvider? I think we should do a bit of refactor then to add the privilege checks conditions.

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, that one. Let's hold off on using them, since some refactoring is needed.

defaultMessage: 'Error while attempt to remove event filter',
}
),
flyoutWarningCalloutMessage: i18n.translate(
Copy link
Contributor

Choose a reason for hiding this comment

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

Wondering if this one should be a function that accepts on input the max number of items to display - since you created a const. then use that in the message

Comment on lines 150 to 151
getExceptionsListApiClient={() => TrustedAppsApiClient.getInstance(http)}
searcheableFields={[...TRUSTED_APPS_SEARCHABLE_FIELDS]}
Copy link
Contributor

Choose a reason for hiding this comment

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

should these be memoized in order to avoid unnecessary re-renders?

(same comment below)

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 so, this is from a static import. I'm destructuring it because it's a readonly type. I can just define it as readonly on the component itself.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok. Just realize that because you are passing a new function everytime to getExceptionsListApiClient and a new Array to searcheableFields, the component will always trigger a re-render (props strict equality ( === ) will report false)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh yes, we can use useCallback for the getExceptionsListApiClient function

Copy link
Contributor

Choose a reason for hiding this comment

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

and maybe use instead of creating a new array and spreading the values, just cast the type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the searchableFields destructured array is fixed in this commit d99ba65

Copy link
Contributor Author

Choose a reason for hiding this comment

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

About the getExceptionsListApiClient, all of this code is inside a useMemo, do you think it's necessary to have functions outside using useCallback?

if (!ExceptionsListApiClient.instance.has(listId)) {
if (
!ExceptionsListApiClient.instance.has(listId) ||
ExceptionsListApiClient.instance.get(listId)?.http !== http
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is working. http is not public on the instance.
Maybe (instead of exposing http on the class, we add a method - something like: isHttp(coreHtttp): boolean { return this.http === coreHttp}

Also, can you add a test for it.

deleteModalErrorMessage: i18n.translate(
'xpack.securitySolution.endpoint.policy.artifacts.list.removeDialog.errorToastTitle',
{
defaultMessage: 'Error while attempt to remove artifact',
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this and all the other translations with attempt should be attempting

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.

Left a few comments, but this looks good to go from my perspective.

🚢 it

page: number;
perPage: number;
policies: string[];
filter?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

Sooooo... I changed this hook in my PR as well and:

  • Moved the order of the input props around to: apiClient, options, searchableFields and then query options.
  • made searchableFields optional -defaults to the DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS
  • made options be a Partial<> instead of forcing the user to define every attribute. Looks like this in mine:
export function useListArtifact(
  exceptionListApiClient: ExceptionsListApiClient,
  options: Partial<{
    filter: string;
    page: number;
    perPage: number;
    policies: string[];
  }> = {},
  searchableFields: MaybeImmutable<string[]> = DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS,
  customQueryOptions: UseQueryOptions<FoundExceptionListItemSchema, HttpFetchError> = {}
): QueryObserverResult<FoundExceptionListItemSchema, HttpFetchError> {
  const {
    filter = '',
    page = MANAGEMENT_DEFAULT_PAGE + 1,
    perPage = MANAGEMENT_DEFAULT_PAGE_SIZE,
    policies,
  } = options;

I made these changes in order to keep the hook's primary responsibility (to get a list of data out) simple and down to a minimal set of input args needed to use it.

Ref: https://github.com/elastic/kibana/pull/126400/files#diff-01c4c157be58054f3b6dc1977f5bba98738cc8461f4f6d15b97710b3f4b12759R17

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done: 63f8f9a

filter?: string;
page?: number;
perPage?: number;
policies?: string[];
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if we should also support a string for policies so that it is easier to just pass in the URL param value. that being said, once I work on a better URL param parser hook, this might be a non-issue as that "parser" would likely take care of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At this hook level I think is better to have an array and not a string. This can avoid us having wrong strings wit wrong structures coming from url. Agree this can change once we have a policy hook for parsing

FoundExceptionListItemSchema,
HttpFetchError
> = DEFAULT_OPTIONS,
customQueryIds: string[] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

You are introducing this just so that you can invalidate queries?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exactly!

if (!ExceptionsListApiClient.instance.has(listId)) {
if (
!ExceptionsListApiClient.instance.has(listId) ||
!ExceptionsListApiClient.instance.get(listId)?.isHttp(http)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you need the ? here now that you added the method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ExceptionsListApiClient.instance.get(listId) can be undefined at this point

Copy link
Contributor

Choose a reason for hiding this comment

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

Ohhh. I see now. TS is not smart enough to know that .has() being true will cause .get() to return the instance.

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.

Looks good to 🚢 . I have a suggestion for file renames and some minor typos.

import { APP_UI_ID } from '../../../../../../../common/constants';
import { EventFiltersPageLocation } from '../../../../event_filters/types';
import { TrustedAppsListPageLocation } from '../../../../trusted_apps/state';
Copy link
Member

Choose a reason for hiding this comment

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

For consistency, I would suggest we remove this folder state and move the files out and rename them like the other artifacts.

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 do this in a subsequent pr if you don't mind 🙂

if (!artifacts) {
return;
}
const artifactssToUpdate: ExceptionListItemSchema[] = [];
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: typo

Suggested change
const artifactssToUpdate: ExceptionListItemSchema[] = [];
const artifactsToUpdate: ExceptionListItemSchema[] = [];

return null;
}

// there are no artifacts assignable to this policy
Copy link
Member

Choose a reason for hiding this comment

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

🔥

);
}

// there are no results for the current search
Copy link
Member

Choose a reason for hiding this comment

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

🔥

interface PolicyArtifactsFlyoutProps {
policyItem: ImmutableObject<PolicyData>;
apiClient: ExceptionsListApiClient;
searcheableFields: string[];
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: typo

Suggested change
searcheableFields: string[];
searchableFields: string[];

Comment on lines +67 to +68
isLoading: isLoadingArtifacts,
isRefetching: isRefetchingArtifacts,
Copy link
Member

Choose a reason for hiding this comment

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

🔥

values: { policyName },
}),
flyoutSearchPlaceholder: i18n.translate(
'xpack.securitySolution.endpoint.policy.artifacts.layout.searh.label',
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
'xpack.securitySolution.endpoint.policy.artifacts.layout.searh.label',
'xpack.securitySolution.endpoint.policy.artifacts.layout.search.label',

values: { policyName },
}),
flyoutSearchPlaceholder: i18n.translate(
'xpack.securitySolution.endpoint.policy.eventFilters.layout.searh.label',
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
'xpack.securitySolution.endpoint.policy.eventFilters.layout.searh.label',
'xpack.securitySolution.endpoint.policy.eventFilters.layout.search.label',

},
customQueryOptions: UseQueryOptions<FoundExceptionListItemSchema, HttpFetchError>
searcheableFields: MaybeImmutable<string[]> = DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS,
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: typo

Suggested change
searcheableFields: MaybeImmutable<string[]> = DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS,
searchableFields: MaybeImmutable<string[]> = DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS,

@dasansol92
Copy link
Contributor Author

@ashokaditya Thanks for all the suggestions and typo's. Everything is in this commit: c5e2545

@dasansol92
Copy link
Contributor Author

@elasticmachine merge upstream

…component_to_be_used_for_all_of_our_artifacts-3173
…o_be_used_for_all_of_our_artifacts-3173' of github.com:dasansol92/kibana into feat/olm-create_generic_policy_tab_artifact_component_to_be_used_for_all_of_our_artifacts-3173
@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 4.7MB 4.7MB -36.1KB

Page load bundle

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

id before after diff
securitySolution 245.1KB 245.1KB +29.0B
Unknown metric groups

async chunk count

id before after diff
securitySolution 21 22 +1

ESLint disabled line counts

id before after diff
securitySolution 447 440 -7

Total ESLint disabled count

id before after diff
securitySolution 514 507 -7

History

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

@dasansol92 dasansol92 merged commit 776fe46 into elastic:main Mar 10, 2022
jloleysens added a commit to jloleysens/kibana that referenced this pull request Mar 10, 2022
…move-pdf-generation-to-screenshotting

* 'main' of github.com:elastic/kibana: (62 commits)
  [Lens] Drop partial buckets option (elastic#127153)
  chore(NA): remove unused translation xpack.ml.management.jobsSpacesList.objectNoun from fr-FR (elastic#127457)
  Add data to user details page (elastic#127019)
  [Fleet] Make upload and registry package info consistent (elastic#126915)
  [Reporting] Capture browser errors (elastic#127135)
  Initial readme commit with some stub articles (elastic#127420)
  skip flaky suite (elastic#121482)
  skip flaky suite (elastic#127416)
  Tests to ensure Kibana is handling multi-space import of saved objects correctly (elastic#127229)
  [Aggs] remove toAngularJson (elastic#127267)
  [i18n] Integrate 8.2.0 Translations (elastic#127309)
  [Security Solution] [Endpoint] Creates generic policy tab artifact component to be used for all of our artifacts (elastic#126685)
  [Kibana React] Fix Page Template `solutionNav` propagation (elastic#127140)
  [Cases] Export getRelatedCases API from cases client (elastic#127065)
  [Cloud Posture]add support for sorting benchmark page (elastic#126983)
  [User experience] Fix filters for the app (elastic#127295)
  [Fleet] Fix timeserie dimension mapping (elastic#127328)
  [data view mgmt] fix data view name wrap (elastic#127319)
  [kbn/optimizer] extract string diffing logic (elastic#127394)
  [ResponseOps] Add pagination and sorting to the alerts search strategy (elastic#126813)
  ...

# Conflicts:
#	x-pack/plugins/screenshotting/common/errors.ts
#	x-pack/plugins/screenshotting/common/index.ts
#	x-pack/plugins/screenshotting/server/screenshots/observable.ts
@kibanamachine kibanamachine added the backport missing Added to PRs automatically when the are determined to be missing a backport. label Mar 14, 2022
@kibanamachine
Copy link
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create backports run node scripts/backport --pr 126685 or prevent reminders by adding the backport:skip label.

1 similar comment
@kibanamachine
Copy link
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create backports run node scripts/backport --pr 126685 or prevent reminders by adding the backport:skip label.

@dasansol92 dasansol92 added the backport:skip This commit does not require backporting label Mar 15, 2022
@kibanamachine kibanamachine removed the backport missing Added to PRs automatically when the are determined to be missing a backport. label Mar 15, 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.2.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants