Skip to content

Commit

Permalink
[Osquery] Fix ECS mapping editor issues (#132307)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrykkopycinski authored May 18, 2022
1 parent d638b18 commit df38c6f
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 101 deletions.
3 changes: 2 additions & 1 deletion x-pack/plugins/osquery/cypress/cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"execTimeout": 120000,
"pageLoadTimeout": 12000,
"retries": {
"runMode": 0
"runMode": 1,
"openMode": 0
},
"screenshotsFolder": "../../../target/kibana-osquery/cypress/screenshots",
"trashAssetsBeforeRuns": false,
Expand Down
27 changes: 27 additions & 0 deletions x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver';
import { login } from '../../tasks/login';
import { navigateTo } from '../../tasks/navigation';
import {
Expand All @@ -24,11 +25,19 @@ import { getAdvancedButton } from '../../screens/integrations';
import { ROLES } from '../../test';

describe('ALL - Live Query', () => {
before(() => {
runKbnArchiverScript(ArchiverMethod.LOAD, 'ecs_mapping_1');
});

beforeEach(() => {
login(ROLES.soc_manager);
navigateTo('/app/osquery');
});

after(() => {
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'ecs_mapping_1');
});

it('should run query and enable ecs mapping', () => {
const cmd = Cypress.platform === 'darwin' ? '{meta}{enter}' : '{ctrl}{enter}';
cy.contains('New live query').click();
Expand Down Expand Up @@ -65,4 +74,22 @@ describe('ALL - Live Query', () => {
.react('EuiIconTip', { props: { type: 'indexMapping' } })
.should('exist');
});

it('should run customized saved query', () => {
cy.contains('New live query').click();
selectAllAgents();
cy.react('SavedQueriesDropdown').type('NOMAPPING{downArrow}{enter}');
cy.getReact('SavedQueriesDropdown').getCurrentState().should('have.length', 1);
inputQuery('{selectall}{backspace}{selectall}{backspace}select * from users');
cy.wait(1000);
submitQuery();
checkResults();
navigateTo('/app/osquery');
cy.react('EuiButtonIcon', { props: { iconType: 'play' } })
.eq(0)
.should('be.visible')
.click();

cy.react('ReactAce', { props: { value: 'select * from users' } }).should('exist');
});
});
33 changes: 18 additions & 15 deletions x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe('ALL - Packs', () => {
`pack_${PACK_NAME}_${SAVED_QUERY_ID}`
);
});
it('by clicking in Lens button', () => {
it.skip('by clicking in Lens button', () => {
let lensUrl = '';
cy.window().then((win) => {
cy.stub(win, 'open')
Expand Down Expand Up @@ -160,8 +160,8 @@ describe('ALL - Packs', () => {
.contains(/^Save and deploy changes$/)
.click();
cy.contains(`${PACK_NAME}`).click();
cy.contains(`${PACK_NAME} details`);
cy.contains(/^No items found/);
cy.contains(`${PACK_NAME} details`).should('exist');
cy.contains(/^No items found/).should('exist');
});

it('enable changing saved queries and ecs_mappings', () => {
Expand All @@ -171,12 +171,13 @@ describe('ALL - Packs', () => {
findAndClickButton('Add query');

getSavedQueriesDropdown().type('Multiple {downArrow} {enter}');
cy.contains('Custom key/value pairs');
cy.contains('Days of uptime');
cy.contains('List of keywords used to tag each');
cy.contains('Seconds of uptime');
cy.contains('Client network address.');
cy.contains('Total uptime seconds');
cy.contains('Custom key/value pairs').should('exist');
cy.contains('Days of uptime').should('exist');
cy.contains('List of keywords used to tag each').should('exist');
cy.contains('Seconds of uptime').should('exist');
cy.contains('Client network address.').should('exist');
cy.contains('Total uptime seconds').should('exist');
cy.getBySel('ECSMappingEditorForm').should('have.length', 4);

getSavedQueriesDropdown().type('NOMAPPING {downArrow} {enter}');
cy.contains('Custom key/value pairs').should('not.exist');
Expand All @@ -185,17 +186,19 @@ describe('ALL - Packs', () => {
cy.contains('Seconds of uptime').should('not.exist');
cy.contains('Client network address.').should('not.exist');
cy.contains('Total uptime seconds').should('not.exist');
cy.getBySel('ECSMappingEditorForm').should('have.length', 1);

getSavedQueriesDropdown().type('ONE_MAPPING {downArrow} {enter}');
cy.contains('Name of the continent');
cy.contains('Seconds of uptime');
cy.contains('Name of the continent').should('exist');
cy.contains('Seconds of uptime').should('exist');
cy.getBySel('ECSMappingEditorForm').should('have.length', 2);

findAndClickButton('Save');
cy.react('CustomItemAction', {
props: { index: 0, item: { id: 'ONE_MAPPING_CHANGED' } },
}).click();
cy.contains('Name of the continent');
cy.contains('Seconds of uptime');
cy.contains('Name of the continent').should('exist');
cy.contains('Seconds of uptime').should('exist');
});

it('to click delete button', () => {
Expand Down Expand Up @@ -231,7 +234,7 @@ describe('ALL - Packs', () => {

cy.getBySel('toastCloseButton').click();
cy.contains(REMOVING_PACK).click();
cy.contains(`${REMOVING_PACK} details`);
cy.contains(`${REMOVING_PACK} details`).should('exist');
findAndClickButton('Edit');
cy.react('EuiComboBoxInput', { props: { value: AGENT_NAME } }).should('exist');

Expand All @@ -246,7 +249,7 @@ describe('ALL - Packs', () => {
closeModalIfVisible();
navigateTo('app/osquery/packs');
cy.contains(REMOVING_PACK).click();
cy.contains(`${REMOVING_PACK} details`);
cy.contains(`${REMOVING_PACK} details`).should('exist');
cy.wait(1000);
findAndClickButton('Edit');
cy.react('EuiComboBoxInput', { props: { value: '' } }).should('exist');
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/osquery/cypress/tasks/live_query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export const selectAllAgents = () => {
cy.contains('1 agent selected.');
};

export const clearInputQuery = () =>
cy.get(LIVE_QUERY_EDITOR).click().type(`{selectall}{backspace}`);

export const inputQuery = (query: string) => cy.get(LIVE_QUERY_EDITOR).type(query);

export const submitQuery = () => cy.contains('Submit').click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export const useAgentPolicy = ({ policyId, skip, silent }: UseAgentPolicy) => {
defaultMessage: 'Error while fetching agent policy details',
}),
}),
refetchOnReconnect: false,
refetchOnWindowFocus: false,
}
);
};
64 changes: 13 additions & 51 deletions x-pack/plugins/osquery/public/live_queries/form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
EuiAccordion,
EuiAccordionProps,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useMutation } from 'react-query';
Expand All @@ -23,17 +22,16 @@ import styled from 'styled-components';

import { pickBy, isEmpty, map } from 'lodash';
import { convertECSMappingToObject } from '../../../common/schemas/common/utils';
import { UseField, Form, FormData, useForm, useFormData, FIELD_TYPES } from '../../shared_imports';
import { UseField, Form, FormData, useForm, useFormData } from '../../shared_imports';
import { AgentsTableField } from './agents_table_field';
import { LiveQueryQueryField } from './live_query_query_field';
import { useKibana } from '../../common/lib/kibana';
import { ResultTabs } from '../../routes/saved_queries/edit/tabs';
import { queryFieldValidation } from '../../common/validations';
import { fieldValidators } from '../../shared_imports';
import { SavedQueryFlyout } from '../../saved_queries';
import { useErrorToast } from '../../common/hooks/use_error_toast';
import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field';
import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown';
import { liveQueryFormSchema } from './schema';

const FORM_ID = 'liveQueryForm';

Expand All @@ -44,8 +42,6 @@ const StyledEuiAccordion = styled(EuiAccordion)`
}
`;

export const MAX_QUERY_LENGTH = 2000;

const GhostFormField = () => <></>;

type FormType = 'simple' | 'steps';
Expand Down Expand Up @@ -100,46 +96,9 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
}
);

const formSchema = {
agentSelection: {
defaultValue: {
agents: [],
allAgentsSelected: false,
platformsSelected: [],
policiesSelected: [],
},
type: FIELD_TYPES.JSON,
validations: [],
},
savedQueryId: {
type: FIELD_TYPES.TEXT,
validations: [],
},
query: {
type: FIELD_TYPES.TEXT,
validations: [
{
validator: fieldValidators.maxLengthField({
length: MAX_QUERY_LENGTH,
message: i18n.translate('xpack.osquery.liveQuery.queryForm.largeQueryError', {
defaultMessage: 'Query is too large (max {maxLength} characters)',
values: { maxLength: MAX_QUERY_LENGTH },
}),
}),
},
{ validator: queryFieldValidation },
],
},
ecs_mapping: {
defaultValue: [],
type: FIELD_TYPES.JSON,
validations: [],
},
};

const { form } = useForm({
id: FORM_ID,
schema: formSchema,
schema: liveQueryFormSchema,
onSubmit: async (formData, isValid) => {
if (isValid) {
try {
Expand Down Expand Up @@ -181,11 +140,14 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({

const actionId = useMemo(() => data?.actions[0].action_id, [data?.actions]);
const agentIds = useMemo(() => data?.actions[0].agents, [data?.actions]);
// eslint-disable-next-line @typescript-eslint/naming-convention
const [{ agentSelection, ecs_mapping, query, savedQueryId }] = useFormData({
form,
watch: ['agentSelection', 'ecs_mapping', 'query', 'savedQueryId'],
});
const [{ agentSelection, ecs_mapping: ecsMapping, query, savedQueryId }, formDataSerializer] =
useFormData({
form,
});

/* recalculate the form data when ecs_mapping changes */
// eslint-disable-next-line react-hooks/exhaustive-deps
const serializedFormData = useMemo(() => formDataSerializer(), [ecsMapping, formDataSerializer]);

const agentSelected = useMemo(
() =>
Expand Down Expand Up @@ -260,8 +222,8 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
);

const flyoutFormDefaultValue = useMemo(
() => ({ savedQueryId, query, ecs_mapping }),
[savedQueryId, ecs_mapping, query]
() => ({ savedQueryId, query, ecs_mapping: serializedFormData.ecs_mapping }),
[savedQueryId, serializedFormData.ecs_mapping, query]
);

const handleToggle = useCallback((isOpen) => {
Expand Down
49 changes: 49 additions & 0 deletions x-pack/plugins/osquery/public/live_queries/form/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const MAX_QUERY_LENGTH = 2000;

import { i18n } from '@kbn/i18n';
import { FIELD_TYPES } from '../../shared_imports';
import { queryFieldValidation } from '../../common/validations';
import { fieldValidators } from '../../shared_imports';

export const liveQueryFormSchema = {
agentSelection: {
defaultValue: {
agents: [],
allAgentsSelected: false,
platformsSelected: [],
policiesSelected: [],
},
type: FIELD_TYPES.JSON,
validations: [],
},
savedQueryId: {
type: FIELD_TYPES.TEXT,
validations: [],
},
query: {
type: FIELD_TYPES.TEXT,
validations: [
{
validator: fieldValidators.maxLengthField({
length: MAX_QUERY_LENGTH,
message: i18n.translate('xpack.osquery.liveQuery.queryForm.largeQueryError', {
defaultMessage: 'Query is too large (max {maxLength} characters)',
values: { maxLength: MAX_QUERY_LENGTH },
}),
}),
},
{ validator: queryFieldValidation },
],
},
ecs_mapping: {
defaultValue: [],
type: FIELD_TYPES.JSON,
},
};
Loading

0 comments on commit df38c6f

Please sign in to comment.