From 383bab02916cf4ef22a04051bd4020f5a59efa62 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Tue, 10 Sep 2019 17:21:55 -0600 Subject: [PATCH] [SIEM] Changes ML conditional links to use tabs, fixes a small bug with null filterQuery (#45218) ## Summary Changes the ML drill-downs to use the tabs and re-direct to the Anomalies table when drilled down. https://github.com/elastic/kibana/issues/45080 Tests for this were both by playing with the Anomalies as well as hand testing that these clickable links below do what I would expect them to do based on the conditional rules of: * Split comma separated values into OR clauses within KQL. * Redirect from multiple hosts/ips on the details page to the host over view/detail overview page with a new KQL added as a filter since comma separated values on details would just be errors. * Remove/Replace any $value$ dollar values that did not have a value as before. Manual testing is from either the test cases below or by using the ML Anomalies explorerand clicking on the drill down links using the action menu items from Host or IP jobs which look like this: Screen Shot 2019-09-06 at 4 17 05 PM URL manual test cases I used: Testing conditional ml-network links: ----- Single IP with a null for the KQL: http://localhost:5601/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z'))) Single IP with kqlQuery: http://localhost:5601/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z'))) Multiple IP's with a null for the filterQuery: http://localhost:5601/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z'))) Multiple IP's with a value for the filterQuery: http://localhost:5601/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z'))) Undefined/null IP and a null filterQuery: http://localhost:5601/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z'))) Undefined/null IP but a value for the filterQuery: http://localhost:5601/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z'))) Testing conditional host links: Single host name with a null for the KQL: http://localhost:5601/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z'))) Single host name with a variable left in the KQL http://localhost:5601/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z'))) Single host name with a value for filterQuery: http://localhost:5601/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z'))) Multiple host names with null for filterQuery http://localhost:5601/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z'))) Multiple host names with a value for filterQuery http://localhost:5601/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z'))) Undefined/null host name with a null for the KQL: http://localhost:5601/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z'))) Undefined/null host name but with a value for filterQuery http://localhost:5601/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z'))) ---- Extra misc tests: 3 host names http://localhost:5601/app/siem#/ml-hosts/suricata-iowa,siem-windows,siem-fake?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22snapd%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-09-09T18:00:00.000Z',kind:absolute,to:'2019-09-09T20:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-09-09T18:00:00.000Z',kind:absolute,to:'2019-09-09T20:59:59.999Z'))) 3 ips http://localhost:5601/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2,127.0.0.3?_g=()&kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T06:00:00.000Z',kind:absolute,to:'2019-08-29T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T06:00:00.000Z',kind:absolute,to:'2019-08-29T05:59:59.999Z'))) ### Checklist Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. - [x] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md) - [x] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios - [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist) ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process) - [x] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process) --- x-pack/legacy/plugins/siem/cypress/README.md | 2 +- .../lib/ml_conditional_links/index.ts | 76 +++++++ .../ml_conditional_links.spec.ts | 206 ++++++++++++++++++ .../add_entities_to_kql.test.ts | 6 + .../conditional_links/add_entities_to_kql.ts | 4 + .../ml_host_conditional_container.tsx | 22 +- .../ml_network_conditional_container.tsx | 16 +- .../plugins/siem/public/pages/home/index.tsx | 14 +- 8 files changed, 320 insertions(+), 26 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts create mode 100644 x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts diff --git a/x-pack/legacy/plugins/siem/cypress/README.md b/x-pack/legacy/plugins/siem/cypress/README.md index 996d23ab5fab2..d3313ef64e449 100644 --- a/x-pack/legacy/plugins/siem/cypress/README.md +++ b/x-pack/legacy/plugins/siem/cypress/README.md @@ -201,7 +201,7 @@ CYPRESS_baseUrl=http://localhost:5601 CYPRESS_ELASTICSEARCH_USERNAME=elastic CYP When Cypress tests are run on the command line via `yarn cypress:run`, reporting artifacts are generated under the `target` directory in the root -of the Kibana, as detailed for each artifact type in the sections bleow. +of the Kibana, as detailed for each artifact type in the sections below. ### HTML Reports diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts new file mode 100644 index 0000000000000..d261832a92f2a --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + * These links are for different test scenarios that try and capture different drill downs into + * ml-network and ml-hosts and are of the flavor of testing: + * A filter being null: (filterQuery:!n) + * A filter being set with single values: filterQuery:(expression:%27process.name%20:%20%22conhost.exe%22%27,kind:kuery) + * A filter being set with multiple values: filterQuery:(expression:%27process.name%20:%20%22conhost.exe,sc.exe%22%27,kind:kuery) + * A filter containing variables not replaced: filterQuery:(expression:%27process.name%20:%20%$process.name$%22%27,kind:kuery) + * + * In different combination with: + * network not being set: $ip$ + * host not being set: $host.name$ + * ...or... + * network being set normally: 127.0.0.1 + * host being set normally: suricata-iowa + * ...or... + * network having multiple values: 127.0.0.1,127.0.0.2 + * host having multiple values: suricata-iowa,siem-windows + */ + +// Single IP with a null for the filterQuery: +export const mlNetworkSingleIpNullFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Single IP with a value for the filterQuery: +export const mlNetworkSingleIpFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Multiple IPs with a null for the filterQuery: +export const mlNetworkMultipleIpNullFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Multiple IPs with a value for the filterQuery: +export const mlNetworkMultipleIpFilterQuery = + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// $ip$ with a null filterQuery: +export const mlNetworkNullFilterQuery = + "/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// $ip$ with a value for the filterQuery: +export const mlNetworkFilterQuery = + "/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + +// Single host name with a null for the filterQuery: +export const mlHostSingleHostNullFilterQuery = + "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Single host name with a variable in the filterQuery +export const mlHostSingleHostFilterQueryVariable = + "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Single host name with a value for filterQuery: +export const mlHostSingleHostFilterQuery = + "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Multiple host names with null for filterQuery +export const mlHostMultiHostNullFilterQuery = + "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Multiple host names with a value for filterQuery +export const mlHostMultiHostFilterQuery = + "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Undefined/null host name with a null for the KQL: +export const mlHostVariableHostNullFilterQuery = + "/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + +// Undefined/null host name but with a value for filterQuery +export const mlHostVariableHostFilterQuery = + "/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts new file mode 100644 index 0000000000000..a178f6364d54c --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts @@ -0,0 +1,206 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { logout } from '../../lib/logout'; +import { + mlNetworkSingleIpNullFilterQuery, + mlNetworkSingleIpFilterQuery, + mlNetworkMultipleIpNullFilterQuery, + mlNetworkMultipleIpFilterQuery, + mlNetworkNullFilterQuery, + mlNetworkFilterQuery, + mlHostSingleHostNullFilterQuery, + mlHostSingleHostFilterQueryVariable, + mlHostSingleHostFilterQuery, + mlHostMultiHostNullFilterQuery, + mlHostMultiHostFilterQuery, + mlHostVariableHostNullFilterQuery, + mlHostVariableHostFilterQuery, +} from '../../lib/ml_conditional_links'; +import { loginAndWaitForPage } from '../../lib/util/helpers'; +import { KQL_INPUT } from '../../lib/url_state'; + +describe('ml conditional links', () => { + afterEach(() => { + return logout(); + }); + + it('sets the KQL from a single IP with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkSingleIpFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('sets the KQL from a multiple IPs with a null for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpNullFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2"))' + ); + }); + + it('sets the KQL from a multiple IPs with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2")) and ((process.name: "conhost.exe" or process.name: "sc.exe"))' + ); + }); + + it('sets the KQL from a $ip$ with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('sets the KQL from a single host name with a value for filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('sets the KQL from a multiple host names with null for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostNullFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(host.name: "siem-windows" or host.name: "siem-suricata")' + ); + }); + + it('sets the KQL from a multiple host names with a value for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(host.name: "siem-windows" or host.name: "siem-suricata") and ((process.name: "conhost.exe" or process.name: "sc.exe"))' + ); + }); + + it('sets the KQL from a undefined/null host name but with a value for filterQuery', () => { + loginAndWaitForPage(mlHostVariableHostFilterQuery); + cy.get(KQL_INPUT, { timeout: 5000 }).should( + 'have.attr', + 'value', + '(process.name: "conhost.exe" or process.name: "sc.exe")' + ); + }); + + it('redirects from a single IP with a null for the filterQuery', () => { + loginAndWaitForPage(mlNetworkSingleIpNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/network/ip/127.0.0.1?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + ); + }); + + it('redirects from a single IP with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkSingleIpFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network/ip/127.0.0.1?kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:network.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a multiple IPs with a null for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpNullFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network?kqlQuery=(filterQuery:(expression:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))'),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a multiple IPs with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkMultipleIpFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network?kqlQuery=(filterQuery:(expression:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a $ip$ with a null filterQuery', () => { + loginAndWaitForPage(mlNetworkNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + ); + }); + + it('redirects from a $ip$ with a value for the filterQuery', () => { + loginAndWaitForPage(mlNetworkFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/network?kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + ); + }); + + it('redirects from a single host name with a null for the filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + ); + }); + + it('redirects from a host name with a variable in the filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostFilterQueryVariable); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + ); + }); + + it('redirects from a single host name with a value for filterQuery', () => { + loginAndWaitForPage(mlHostSingleHostFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/siem-windows/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:hosts.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); + + it('redirects from a multiple host names with null for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostNullFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)'),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); + + it('redirects from a multiple host names with a value for filterQuery', () => { + loginAndWaitForPage(mlHostMultiHostFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); + + it('redirects from a undefined/null host name with a null for the KQL', () => { + loginAndWaitForPage(mlHostVariableHostNullFilterQuery); + cy.url().should( + 'equal', + 'http://localhost:5601/app/siem#/hosts/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + ); + }); + + it('redirects from a undefined/null host name but with a value for filterQuery', () => { + loginAndWaitForPage(mlHostVariableHostFilterQuery); + cy.url().should( + 'equal', + "http://localhost:5601/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts index c039cc2f88646..bab39a437b0df 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts @@ -17,6 +17,7 @@ describe('add_entities_to_kql', () => { afterAll(() => { console.log = originalError; }); + describe('#entityToKql', () => { test('returns empty string with no entity names defined and an empty entity string', () => { const entity = entityToKql([], ''); @@ -165,5 +166,10 @@ describe('add_entities_to_kql', () => { '(filterQuery:(expression:\'(host.name: "host-name-1" or host.name: "host-name-2") and (process.name : "")\',kind:kuery))' ); }); + + test('returns kql expression with a null filterQuery', () => { + const entity = addEntitiesToKql(['host.name'], ['host-1'], '(filterQuery:!n)'); + expect(entity).toEqual('(filterQuery:(expression:\'(host.name: "host-1")\'))'); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts index c6c7704aa0999..490bb8fd324c3 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts @@ -52,6 +52,10 @@ export const addEntitiesToKql = ( } return encode(value); } + } else if (value.filterQuery == null) { + const entitiesKql = entitiesToKql(entityNames, entities); + value.filterQuery = { expression: `(${entitiesKql})` }; + return encode(value); } } return kqlQuery; diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index be0628452ea02..9921be26923b3 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -6,31 +6,27 @@ import React from 'react'; -import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom'; +import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { QueryString } from 'ui/utils/query_string'; -import { pure } from 'recompose'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; import { replaceKqlQueryLocationForHostPage } from './replace_kql_query_location_for_host_page'; -interface MlHostConditionalProps { - match: RouteMatch<{}>; - location: Location; -} - interface QueryStringType { '?_g': string; kqlQuery: string | null; timerange: string | null; } -export const MlHostConditionalContainer = pure(({ match }) => ( +type MlHostConditionalProps = Partial> & { url: string }; + +export const MlHostConditionalContainer = React.memo(({ url }) => ( { const queryStringDecoded: QueryStringType = QueryString.decode( location.search.substring(1) @@ -43,7 +39,7 @@ export const MlHostConditionalContainer = pure(({ match }} /> (({ match ); } const reEncoded = QueryString.encode(queryStringDecoded); - return ; + return ; } else if (multipleEntities(hostName)) { const hosts: string[] = getMultipleEntities(hostName); if (queryStringDecoded.kqlQuery != null) { @@ -77,10 +73,10 @@ export const MlHostConditionalContainer = pure(({ match ); } const reEncoded = QueryString.encode(queryStringDecoded); - return ; + return ; } else { const reEncoded = QueryString.encode(queryStringDecoded); - return ; + return ; } }} /> diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx index 3fcc60c655889..a24e1fc098238 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx @@ -6,31 +6,27 @@ import React from 'react'; -import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom'; +import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { QueryString } from 'ui/utils/query_string'; -import { pure } from 'recompose'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, getMultipleEntities, multipleEntities } from './entity_helpers'; import { replaceKqlQueryLocationForNetworkPage } from './replace_kql_query_location_for_network_page'; -interface MlNetworkConditionalProps { - match: RouteMatch<{}>; - location: Location; -} - interface QueryStringType { '?_g': string; kqlQuery: string | null; timerange: string | null; } -export const MlNetworkConditionalContainer = pure(({ match }) => ( +type MlNetworkConditionalProps = Partial> & { url: string }; + +export const MlNetworkConditionalContainer = React.memo(({ url }) => ( { const queryStringDecoded: QueryStringType = QueryString.decode( location.search.substring(1) @@ -43,7 +39,7 @@ export const MlNetworkConditionalContainer = pure(({ }} /> ( /> } /> - - + ( + + )} + /> + ( + + )} + />