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 Solutions] Adds a default for indicator match custom query of *:* #81727

Merged

Conversation

FrankHassanabad
Copy link
Contributor

@FrankHassanabad FrankHassanabad commented Oct 27, 2020

Summary

Allows for Indicator matches to have a default of *:* for the query field when it is selected.

Before, indicator query is blank when first selecting the rule:
Screen Shot 2020-11-05 at 5 44 50 PM

After, indicator query is by default *:* unless the user has previously edited the query field:
Screen Shot 2020-11-05 at 5 45 38 PM

Adds a stable reference for threat matching to determine when the query field has been modified or not. This is keep the current behavior and the rules operate like this:

  • If you select an indicator match rule and nothing has been previously edited it will select *:* for the query
  • If you have modified your custom query and select indicator match rule, then *:* will be replaced with that custom query and *:* will not be used.
  • If you select EQL rule and then back to this rule type the *:* will be re-inserted and edit: true will flip back to false, due to the magic that is keys within React and how the EQL rule type relies on that.

Checklist

Delete any items that are not applicable to this PR.

@FrankHassanabad FrankHassanabad changed the title Add default for threat match [Security Solutions] Adds a default for threat matching query Oct 27, 2020
@MindyRS MindyRS added the Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. label Oct 27, 2020
@FrankHassanabad FrankHassanabad added the Team:Detections and Resp Security Detection Response Team label Nov 6, 2020
@FrankHassanabad FrankHassanabad self-assigned this Nov 6, 2020
@FrankHassanabad FrankHassanabad changed the title [Security Solutions] Adds a default for threat matching query [Security Solutions] Adds a default for indicator match custom query of *:* Nov 6, 2020
@FrankHassanabad FrankHassanabad marked this pull request as ready for review November 6, 2020 00:52
@FrankHassanabad FrankHassanabad requested review from a team as code owners November 6, 2020 00:52
@FrankHassanabad
Copy link
Contributor Author

@elasticmachine merge upstream

@yctercero
Copy link
Contributor

Just started reviewing :)

@@ -165,7 +166,20 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
// reset form when rule type changes
useEffect(() => {
reset({ resetValues: false });
}, [reset, ruleType]);
if (getFields().queryBar != null) {
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 you can use queryBar from line 140? Don't think you have to re-pull it here.

Copy link
Contributor

Choose a reason for hiding this comment

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

EDIT: Nvm, I see reset was called beforehand.

@yctercero
Copy link
Contributor

This might be more a UX question, but do we want KQL queries carried over into the EQL input?
Screen Shot 2020-11-10 at 2 27 48 PM

I had selected Threshold Rule, added a query, then selected EQL Rule.

@yctercero
Copy link
Contributor

It seems related to my last comment. But found this little weirdness.

frank

@yctercero
Copy link
Contributor

Just kidding! 🃏 After testing on master, it appears that the two issues I mentioned above are existing issues that may require some team/UX discussion. I'll go ahead and open an issue for this for the team to look at.

LGTM! Pulled down and tried running through various corner cases. New code works great!

Copy link
Contributor

@rylnd rylnd left a comment

Choose a reason for hiding this comment

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

Added some comments revolving around usage of the hook form lib; I think we can do away with the new edited property but it's possible I'm misunderstanding.

@@ -165,7 +166,20 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
// reset form when rule type changes
useEffect(() => {
reset({ resetValues: false });
}, [reset, ruleType]);
if (getFields().queryBar != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would isolate this logic to a separate useEffect hook. Shared dependencies can cause undesired effects, e.g. isUpdateView changing would now cause reset() to fire.

@@ -131,7 +132,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
options: { stripEmptyFields: false },
schema,
});
const { getFields, getFormData, reset, submit } = form;
const { getFields, getFormData, reset, submit, setFieldValue } = form;
Copy link
Contributor

Choose a reason for hiding this comment

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

You shouldn't need setFieldValue here, you can instead use [field].setValue as in handleResetIndices 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.

Thanks, I think this is a dead variable at this point and I don't use it below anymore so this will be removed.

}, [reset, ruleType]);
if (getFields().queryBar != null) {
const queryBar = getFields().queryBar.value as DefineStepRule['queryBar'];
if (queryBar.edited !== true && !isUpdateView) {
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI this edited field is mostly redundant with the built in isPristine property on each field, except that isPristine becomes false when the value changes below, while edited does not.

Perhaps we could use field.isPristine || field.value.query.query === '*:*' to accomplish the same logic, and do away with the edited property?

Copy link
Contributor

Choose a reason for hiding this comment

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

In talking with @FrankHassanabad I wanted to add this to our team discussion. It's becoming increasingly more difficult to make any changes without either 1) breaking another rule type's logic or 2) adding extra validations throughout. It's feeling like a bit of wack-a-mole. I almost want to just silo off each rule type. One example I ran into with preview was that each rule held the state of other rule types (if let's say I visited Threshold and set some state and then switched to custom query) and had to add logic to ensure that it was picking up and ignoring the right parts.

Copy link
Contributor

@marshallmain marshallmain Nov 11, 2020

Choose a reason for hiding this comment

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

@yctercero Would the schemas here help to silo off the rule types in the frontend code? I created them with a similar goal in mind for the backend of making it clear which fields are valid for each rule type, as well as defining which fields are shared across all rule types. They're usable in the frontend as well but I'm not sure if they fully implement the siloing you have in mind.

Copy link
Contributor

Choose a reason for hiding this comment

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

@marshallmain yea, I think they would. It might add more touchpoints in the front, but that may be worth the security of not having to worry that an edit to one rule will break another.

Copy link
Contributor Author

@FrankHassanabad FrankHassanabad Nov 11, 2020

Choose a reason for hiding this comment

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

@ryland, The isPristine field is something I want to use, but there are more odd side effects of how we use the forms where we do a reset({ resetValues: false }); which will reset the isPristine back to true. Even in that block of code if I were to do something like:

  // reset form when rule type changes
  useEffect(() => {
    reset({ resetValues: false });
    if (getFields().queryBar != null) {
      const queryBar = getFields().queryBar.value as DefineStepRule['queryBar'];
      if (
        isThreatMatchRule(ruleType) &&
        (getFields().queryBar.isPristine || queryBar.query.query === '')
      ) {
        setFieldValue('queryBar', {
          ...stepDefineDefaultValue.queryBar,
          query: { ...stepDefineDefaultValue.queryBar.query, query: '*:*' },
        });
      } else if (getFields().queryBar.isPristine || queryBar.query.query === '*:*') {
        setFieldValue('queryBar', stepDefineDefaultValue.queryBar);
      } else {
        setFieldValue('queryBar', queryBar);
      }
    }
  }, [reset, ruleType, setFieldValue, getFields, isUpdateView]);

It still won't work all the way because isPristine will reset back to true and stay true based on two things which is:

  • The reset will turn it back to true
  • Even if this block of code in the else tries to force pristine back to false:
else {
  setFieldValue('queryBar', queryBar);
}

The forms code if it sees the queryBar value is the same as before it will not trigger pristine back to false. So pristine becomes not useful here.

However, to remove the edit flag I added at a trade off of the forms having funny quirks if a user decides to use *:* in a different rule if they're really flipping around between rules a lot before going to the next step would be this:

  // reset form when rule type changes
  useEffect(() => {
    reset({ resetValues: false });
    const queryBar = getFields().queryBar.value as DefineStepRule['queryBar'];
    if (getFields().queryBar != null) {
      if (isThreatMatchRule(ruleType) && queryBar.query.query === '') {
        setFieldValue('queryBar', {
          ...stepDefineDefaultValue.queryBar,
          query: { ...stepDefineDefaultValue.queryBar.query, query: '*:*' },
        });
      } else if (queryBar.query.query === '*:*') {
        setFieldValue('queryBar', stepDefineDefaultValue.queryBar);
      }
    }
  }, [reset, ruleType, setFieldValue, getFields, isUpdateView]);

That would just hardcode the two conditions of either detecting an empty string or detecting a *:* but if a user decides to use *:* and then flip between rules such as threshold and custom query they are going to get the surprise that the *:* turns back into empty string. But the validation would turn on to warn them they have an empty string they need to deal with.

If that is deemed the "the lesser of evils" with what we already have in the forms code I can commit the code above.

Copy link
Contributor Author

@FrankHassanabad FrankHassanabad Nov 11, 2020

Choose a reason for hiding this comment

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

Actually after messing around with it @rylnd I was able to accomplish everything the edited flag could using a combination of the isPristine along with reset and then the setValue that is looking pretty good. I think this is what I am going to go with:

  // reset form when rule type changes
  useEffect(() => {
    reset({ resetValues: false });
    const { queryBar } = getFields();
    if (queryBar != null && !isUpdateView) {
      if (isThreatMatchRule(ruleType) && queryBar.isPristine) {
        queryBar.reset({
          defaultValue: {
            ...stepDefineDefaultValue.queryBar,
            query: { ...stepDefineDefaultValue.queryBar.query, query: '*:*' },
          },
        });
      } else if (queryBar.isPristine) {
        queryBar.reset({
          defaultValue: {
            ...stepDefineDefaultValue.queryBar,
            query: { ...stepDefineDefaultValue.queryBar.query, query: '' },
          },
        });
      } else {
        // This ensures the isPristine is still set to false after a user has modified it
        queryBar.setValue((prev: DefineStepRule['queryBar']) => ({ ...prev }));
      }
    }
  }, [reset, ruleType, getFields, isUpdateView]);

const queryBar = getFields().queryBar.value as DefineStepRule['queryBar'];
if (queryBar.edited !== true && !isUpdateView) {
if (isThreatMatchRule(ruleType)) {
setFieldValue('queryBar', {
Copy link
Contributor

Choose a reason for hiding this comment

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

field.setValue() has a format similar to setState where it accepts a function that receives the previous value; that might be more appropriate than merging in the original defaults, 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.

Thanks, see comments above, I can use this in combination with reset and I think we are going to get what we want out of this feature in a more straight forward way.

Copy link
Contributor

@rylnd rylnd left a comment

Choose a reason for hiding this comment

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

Looks great, Frank! Thanks for the followup!

queryBar.reset({
defaultValue: threatQueryBarDefaultValue,
});
} else if (queryBar.value === threatQueryBarDefaultValue) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I was suspicious of this clause being hit if the user manually enters *:* as a query, but it looks to be good since this is a comparison of object literals (which will only be equal in the use of reset() above) and not just values 👍

@kibanamachine
Copy link
Contributor

💚 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 7.8MB 7.8MB +698.0B

History

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

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.

Re-LGTM :) Tested out again and it looks great! Awesome how you were able to minimize the amount of code changes.

@FrankHassanabad FrankHassanabad merged commit 18cea00 into elastic:master Nov 12, 2020
@FrankHassanabad FrankHassanabad deleted the add-default-for-threat-match branch November 12, 2020 21:58
FrankHassanabad added a commit to FrankHassanabad/kibana that referenced this pull request Nov 12, 2020
…of *:* (elastic#81727)

## Summary

Allows for Indicator matches to have a default of `*:*` for the query field when it is selected.

Before, indicator query is blank when first selecting the rule:
<img width="1037" alt="Screen Shot 2020-11-05 at 5 44 50 PM" src="https://user-images.githubusercontent.com/1151048/98312312-afc9ff00-1f8e-11eb-822b-ad95104ca54e.png">

After, indicator query is by default `*:*` unless the user has previously edited the query field:
<img width="1038" alt="Screen Shot 2020-11-05 at 5 45 38 PM" src="https://user-images.githubusercontent.com/1151048/98312363-cb350a00-1f8e-11eb-9137-8da2f770ec7e.png">

Adds a stable reference for threat matching to determine when the query field has been modified or not. This is keep the current behavior and the rules operate like this:

* If you select an indicator match rule and nothing has been previously edited it will select `*:*` for the query
* If you have modified your custom query and select indicator match rule, then `*:*` will be replaced with that custom query and `*:*` will not be used.
* If you select EQL rule and then _back_ to this rule type the `*:*` will be re-inserted and `edit: true` will flip back to false, due to the magic that is keys within React and how the EQL rule type relies on that.
 
### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
FrankHassanabad added a commit that referenced this pull request Nov 13, 2020
…of *:* (#81727) (#83352)

## Summary

Allows for Indicator matches to have a default of `*:*` for the query field when it is selected.

Before, indicator query is blank when first selecting the rule:
<img width="1037" alt="Screen Shot 2020-11-05 at 5 44 50 PM" src="https://user-images.githubusercontent.com/1151048/98312312-afc9ff00-1f8e-11eb-822b-ad95104ca54e.png">

After, indicator query is by default `*:*` unless the user has previously edited the query field:
<img width="1038" alt="Screen Shot 2020-11-05 at 5 45 38 PM" src="https://user-images.githubusercontent.com/1151048/98312363-cb350a00-1f8e-11eb-9137-8da2f770ec7e.png">

Adds a stable reference for threat matching to determine when the query field has been modified or not. This is keep the current behavior and the rules operate like this:

* If you select an indicator match rule and nothing has been previously edited it will select `*:*` for the query
* If you have modified your custom query and select indicator match rule, then `*:*` will be replaced with that custom query and `*:*` will not be used.
* If you select EQL rule and then _back_ to this rule type the `*:*` will be re-inserted and `edit: true` will flip back to false, due to the magic that is keys within React and how the EQL rule type relies on that.
 
### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
gmmorris added a commit to gmmorris/kibana that referenced this pull request Nov 16, 2020
* master:
  [Security Solution][Detections] Adds framework for replacing API schemas (elastic#82462)
  [Enterprise Search] Share Loading component (elastic#83246)
  Consolidates Jest configuration files and scripts (elastic#82671)
  APM header changes (elastic#82870)
  [Security Solutions] Adds a default for indicator match custom query of *:* (elastic#81727)
  [Security Solution] Note 10k object paging limit on Endpoint list (elastic#82889)
  [packerCache] fix gulp usage, don't archive node_modules (elastic#83327)
  Upgrade Node.js to version 12 (elastic#61587)
  [Actions] Removing placeholders and updating validation messages on connector forms (elastic#82734)
  [Fleet] Rename ingest_manager_api_integration tests fleet_api_integration (elastic#83011)
  [DOCS] Updates Discover docs (elastic#82773)
  [ML] Data frame analytics: Adds map view (elastic#81666)
  enables actions scoped within the stack to register at Basic license (elastic#82931)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release_note:fix Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v7.11.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants