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] Invalid KQL Query Bug #99442

Merged
merged 20 commits into from
Jun 30, 2021

Conversation

dplumlee
Copy link
Contributor

@dplumlee dplumlee commented May 6, 2021

Summary

Addresses #98283

Currently, our method of converting KQL to Elasticsearch queries silently suppresses errors bubbled up by ES and returns an empty query string. This makes it so the entire query, including filters, etc. gets wiped out and potentially incorrect data is displayed.

This PR addresses that by bubbling up the errors and putting them in a toast component as well as cancelling any request that was made with the invalid query so that incorrect data is never fetched.

Screen Shot 2021-05-11 at 5 05 24 PM

Checklist

Delete any items that are not applicable to this PR.

For maintainers

@@ -70,6 +70,9 @@ const escapeSpecialCharacters = (val: string) => val.replace(/["]/g, '\\$&'); //

export const escapeKuery = flow(escapeSpecialCharacters, escapeWhitespace);

/**
* Deprecated in leiu of `convertToBuildEsQueryOrError`
Copy link
Contributor

@XavierM XavierM May 11, 2021

Choose a reason for hiding this comment

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

You are not lucky because only spellings that I can fix are the french ones ;)

Suggested change
* Deprecated in leiu of `convertToBuildEsQueryOrError`
* Deprecated in lieu of `convertToBuildEsQueryOrError`

@@ -80,7 +83,7 @@ export const convertToBuildEsQuery = ({
indexPattern: IIndexPattern;
queries: Query[];
filters: Filter[];
}) => {
}): string | undefined => {
Copy link
Contributor

@XavierM XavierM May 11, 2021

Choose a reason for hiding this comment

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

just thinking out loud here, instead of creating convertToBuildEsQueryOrError, I am wondering if convertToBuildEsQuery can return a type like that [string | undefined, Error | undefined] or we can even be stricter [string, undefined] | [undefined, Error]
Since we are changing the type, we can do a little more to avoid the deprecation and this will also avoid to call esQuery.buildEsQuery twice to get the error and it will simplify your new hook useInvalidFilterQuery to display the error. I am always scared of function that can return the valid answer or an error.

Copy link
Contributor Author

@dplumlee dplumlee May 11, 2021

Choose a reason for hiding this comment

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

We could do that, i think that's a good idea to condense the two and update inline instead of creating the new function. @spong @andrew-goldstein thoughts?

@dplumlee dplumlee force-pushed the invalid-kql-query-bug branch from 5a96e57 to c702648 Compare May 11, 2021 08:10
@dplumlee dplumlee marked this pull request as ready for review May 11, 2021 22:33
@dplumlee dplumlee requested a review from a team as a code owner May 11, 2021 22:33
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-detections-response (Team:Detections and Resp)

@dplumlee dplumlee force-pushed the invalid-kql-query-bug branch from a85c258 to 05a8f68 Compare May 11, 2021 22:35
@@ -199,7 +202,7 @@ const HostsComponent = () => {
deleteQuery={deleteQuery}
docValueFields={docValueFields}
to={to}
filterQuery={tabsFilterQuery}
filterQuery={tabsFilterQuery || ''}
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 the right approach here? Our plan was to skip the tab filter queries so this just continues the logic that was in place before, just further down the line

}
// This disable is required to only trigger the toast once per render
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filterQuery, addError]);
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 go for it and add addError as a dependency, because you won't have kqlError != null

@andrew-goldstein
Copy link
Contributor

Thanks for this thoughtful, surgical fix @dplumlee!

I'm seeing an unexpected result that doesn't appear to be introduced by this PR, but the fix makes the unexpected result more obvious:

  1. To ensure there's no existing URL state, open a new tab and visit http://localhost:5601

  2. Navigate to Security > Detections

  3. Enter the following KQL, which intentionally has an extraneous trailing double quote, in the search bar and press Enter:

not file.path : C:\elk\mimikatz.exe"

Expected result:

  • The new KQLSyntaxError toast appears
  1. Click the X button on the toast to dismiss it

  2. Remove the extra trailing quote from the search bar, such that the query is valid as shown in the example below and press Enter:

not file.path : C:\elk\mimikatz.exe

Expected result:

  • The now-valid query executes, and detections appear in the table

Actual result:

  • The new query appears to execute (the Refresh button changes state, and loading indicators are displayed)
  • The table doesn't update

table_does_not_update

Additional observations:

  • The unexpected behavior is still reproducible if, in step 5, the Refresh button is clicked (as opposed to pressing Enter) after fixing the query to make it valid
  • When the page is in the unexpected state described above, clicking the Refresh button a 2nd time does appear to re-execute the query, and the results are then displayed (as expected)
  • To determine whether or not this is a new or existing behavior, I applied the technique above (transitioning the query state from invalid to valid) on a rule details page (using [Security Solution]Incorrect detection alert list apear on searching event in search bar under individual Rule page #98283 as guidance), and I'm seeing similar behavior. Specifically, after transitioning the query from invalid to valid and then inspecting the query, it doesn't appear that the newly-valid KQL is being applied. Based on the above, I don't think a change in this PR introduced this unexpected behavior, but the fix makes it more obvious.

@XavierM
Copy link
Contributor

XavierM commented May 12, 2021

@andrew-goldstein good catch 🎣 as always. @dplumlee I think to fix this behavior without you going crazy, will be to override the refetch function to show the toaster again.

@andrew-goldstein
Copy link
Contributor

@andrew-goldstein good catch 🎣 as always. @dplumlee I think to fix this behavior without you going crazy, will be to override the refetch function to show the toaster again.

There appears to be a similar statefulness issue with the Inspect action:

  1. To ensure there's no existing URL state, open a new tab and visit http://localhost:5601

  2. Navigate to Security > Detections

  3. Enter the following KQL, which intentionally has an extraneous trailing double quote, in the search bar and press Enter:

not file.path : C:\elk\mimikatz.exe"

Expected result:

  • The new KQLSyntaxError toast appears
  1. Click the X button on the toast to dismiss it

  2. Hover over the Inspect button icon and click it

Expected result:

  • The Inspect dialog appears

Actual result:

  • The Inspect dialog does NOT appear
  1. Completely clear the KQL bar and press Enter

Expected results:

  • The table refreshes to display data
  • Nothing else happens

Actual results:

  • The table refreshes to display data
  • The Inspect dialog unexpectedly appears after the table displays the data

inspect

@andrew-goldstein
Copy link
Contributor

In the (specific) case of a KQL syntax error, the internal Kibana stack trace of the error that was thrown will not be useful to the user. All the relevant feedback is contained in the message property of the JSON error shown in the screenshot below:

error-with-stack-trace

Consider, if this suggestion doesn't introduce significant complexity, eliminating the stack trace in the screenshot above, by displaying only the message property of the error when, specifically, a KQL syntax error is thrown.

@MadameSheema
Copy link
Member

Hi team! Thanks for working on this :)

BC5 has been already shipped, last BC (BC6) is planned for next week. If you think that this fix is risky, we should probably wait for 7.13.1.

@andrew-goldstein
Copy link
Contributor

BC5 has been already shipped, last BC (BC6) is planned for next week. If you think that this fix is risky, we should probably wait for 7.13.1.

As a team, we've collaborated and iterated on the fix to minimize risk, and we are all still in agreement it should be included in the final BC. I'm also thoroughly desk testing it as part of the review.

@MadameSheema, would you be willing to coordinate with the team such that the fix is tested as soon as it's merged to master (ahead of the next BC)?

@MadameSheema
Copy link
Member

Thanks for the heads up @andrew-goldstein :)

I'll coordinate with the team to make sure this issue is tested on master as soon as is merged.

@dplumlee dplumlee force-pushed the invalid-kql-query-bug branch from ea839a8 to 9543753 Compare May 19, 2021 17:39
useEffect(() => {
if (filterQuery === undefined && kqlError != null) {
// Removes error stack from user view
delete kqlError.stack;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally we might avoid mutating the kqlError arg, because consumers of the hook are probably not expecting this side effect.

As an alternative to using delete to remove the stack, I would typically recommend using a utility function like omit from lodash/fp, because it's guaranteed not to mutate the original, and it's less verbose than the native TS alternative. Unfortunately when I tried that, the error toaster didn't behave as expected, because the implementation of addError uses IEsError in a TS type guard.

I experimented with a few different options for safely cloning and/or constructing a new error (that omits the stack), but each path I explored had some unexpected "gotchas".

To keep this PR moving forward, consider documenting the side effect of mutating kqlError if we can't reasonably eliminate it.

@dplumlee dplumlee force-pushed the invalid-kql-query-bug branch from 985e3cc to 02dd544 Compare May 24, 2021 22:55
@andrew-goldstein
Copy link
Contributor

andrew-goldstein commented May 25, 2021

It looks like the KPIs in Timeline are showing unexpected results when a KQLSyntaxError is thrown. To reproduce:

  1. To ensure there's no existing URL state, open a new tab and visit http://localhost:5601

  2. Navigate to the Security Solution

  3. Click Untitled timeline to open Timeline

  4. Enter the following KQL, which intentionally contains a file.path with a username, pr_99442, that won't match any documents:

file.path: "C:\Users\pr_99442\Downloads\mimikatz_trunk (1)\x64\mimikatz.exe"

Expected result:

  • The KPIs at the top of Timeline show all zeros, as shown in the screenshot below:

zero_results

  1. Next, remove both the leading and trailing quotes in the KQL, as shown in the query below, and press Enter:
file.path: C:\Users\pr_99442\Downloads\mimikatz_trunk (1)\x64\mimikatz.exe

Expected results:

  • The error toaster appears
  • The KPIs revert to their initial "all dashes" - state, shown in the screenshot below, because nothing matches an invalid query:

initial-state

Above: The KPIs are displayed with -s when Timeline is first opened, because there are no matching documents

Actual result:

  • The error toaster appears
  • The KPIs that previously displayed all zeros, (because the first query was valid, but didn't match any results) now display (unexpected) non-zero results, per the screenshot below:

with-syntax-error

@dplumlee dplumlee force-pushed the invalid-kql-query-bug branch from e273995 to d3805a6 Compare June 29, 2021 21:41
@dplumlee dplumlee added the bug Fixes for quality problems that affect the customer experience label Jun 29, 2021
Copy link
Contributor

@andrew-goldstein andrew-goldstein left a comment

Choose a reason for hiding this comment

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

I'm seeing two unexpected behaviors in Timeline:

  1. Clicking the Refresh button while the query is invalid sometimes (other times it hangs, per #2, below) refreshes the grid with invalid results:

refresh-shows-data-when-the-query-is-invalid

  1. Clicking the Refresh button when the query is invalid sometimes causes timeline to hang forever in the "Loading Events..." state:

hang_on_refresh

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
securitySolution 2186 2187 +1

Async chunks

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

id before after diff
securitySolution 6.3MB 6.3MB +7.8KB

History

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

cc @dplumlee

@andrew-goldstein andrew-goldstein added the auto-backport Deprecated - use backport:version if exact versions are needed label Jun 30, 2021
Copy link
Contributor

@andrew-goldstein andrew-goldstein left a comment

Choose a reason for hiding this comment

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

Thanks for all your effort on this fix @dplumlee!
Per a discussion with @peluja1012, let's merge this immediately ahead of the BC that's about to be built, and fix the last known issue in a follow-up PR
LGTM

@andrew-goldstein andrew-goldstein merged commit 36b21b4 into elastic:master Jun 30, 2021
@kibanamachine
Copy link
Contributor

💔 Backport failed

Status Branch Result
7.x Commit could not be cherrypicked due to conflicts

To backport manually run:
node scripts/backport --pr 99442

andrew-goldstein pushed a commit to andrew-goldstein/kibana that referenced this pull request Jun 30, 2021
## Summary

Addresses elastic#98283

Currently, our method of converting KQL to Elasticsearch queries silently suppresses errors bubbled up by ES and returns an empty query string. This makes it so the entire query, including filters, etc. gets wiped out and potentially incorrect data is displayed. 

This PR addresses that by bubbling up the errors and putting them in a toast component as well as cancelling any request that was made with the invalid query so that incorrect data is never fetched.

![Screen Shot 2021-05-11 at 5 05 24 PM](https://user-images.githubusercontent.com/56367316/117895214-e8bf9500-b28b-11eb-83a6-522deebecbe2.png)


### Checklist

Delete any items that are not applicable to this PR.

- [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials
- [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

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
andrew-goldstein pushed a commit to andrew-goldstein/kibana that referenced this pull request Jun 30, 2021
## Summary

Addresses elastic#98283

Currently, our method of converting KQL to Elasticsearch queries silently suppresses errors bubbled up by ES and returns an empty query string. This makes it so the entire query, including filters, etc. gets wiped out and potentially incorrect data is displayed. 

This PR addresses that by bubbling up the errors and putting them in a toast component as well as cancelling any request that was made with the invalid query so that incorrect data is never fetched.

![Screen Shot 2021-05-11 at 5 05 24 PM](https://user-images.githubusercontent.com/56367316/117895214-e8bf9500-b28b-11eb-83a6-522deebecbe2.png)


### Checklist

Delete any items that are not applicable to this PR.

- [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials
- [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

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
andrew-goldstein added a commit that referenced this pull request Jun 30, 2021
## Summary

Addresses #98283

Currently, our method of converting KQL to Elasticsearch queries silently suppresses errors bubbled up by ES and returns an empty query string. This makes it so the entire query, including filters, etc. gets wiped out and potentially incorrect data is displayed. 

This PR addresses that by bubbling up the errors and putting them in a toast component as well as cancelling any request that was made with the invalid query so that incorrect data is never fetched.

![Screen Shot 2021-05-11 at 5 05 24 PM](https://user-images.githubusercontent.com/56367316/117895214-e8bf9500-b28b-11eb-83a6-522deebecbe2.png)


### Checklist

Delete any items that are not applicable to this PR.

- [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials
- [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

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

Co-authored-by: Davis Plumlee <[email protected]>
andrew-goldstein added a commit that referenced this pull request Jun 30, 2021
## Summary

Addresses #98283

Currently, our method of converting KQL to Elasticsearch queries silently suppresses errors bubbled up by ES and returns an empty query string. This makes it so the entire query, including filters, etc. gets wiped out and potentially incorrect data is displayed. 

This PR addresses that by bubbling up the errors and putting them in a toast component as well as cancelling any request that was made with the invalid query so that incorrect data is never fetched.

![Screen Shot 2021-05-11 at 5 05 24 PM](https://user-images.githubusercontent.com/56367316/117895214-e8bf9500-b28b-11eb-83a6-522deebecbe2.png)


### Checklist

Delete any items that are not applicable to this PR.

- [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials
- [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

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

Co-authored-by: Davis Plumlee <[email protected]>
@dplumlee dplumlee deleted the invalid-kql-query-bug branch July 21, 2021 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-backport Deprecated - use backport:version if exact versions are needed bug Fixes for quality problems that affect the customer experience release_note:fix Team:Detections and Resp Security Detection Response Team v7.14.0 v7.15.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants