[Osquery] Fix multiple minor issues (#122023)

This commit is contained in:
Tomasz Ciecierski 2022-01-14 13:20:28 +01:00 committed by GitHub
parent 41806bab77
commit 4ee667be4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 795 additions and 494 deletions

View file

@ -0,0 +1,124 @@
{
"queries": {
"acpi_tables": {
"query": "select * from acpi_tables;",
"interval": 86400,
"platform": "posix",
"version": "1.3.0",
"description": "General reporting and heuristics monitoring."
},
"cpuid": {
"query": "select feature, value, output_register, output_bit, input_eax from cpuid;",
"interval": 86400,
"version": "1.0.4",
"description": "General reporting and heuristics monitoring."
},
"smbios_tables": {
"query": "select * from smbios_tables;",
"interval": 86400,
"platform": "posix",
"version": "1.3.0",
"description": "General reporting and heuristics monitoring."
},
"nvram": {
"query": "select * from nvram where name not in ('backlight-level', 'SystemAudioVolumeDB', 'SystemAudioVolume');",
"interval": 7200,
"platform": "darwin",
"version": "1.0.2",
"description": "Report on crashes, alternate boots, and boot arguments."
},
"kernel_info": {
"query": "select * from kernel_info join hash using (path);",
"interval": 7200,
"version": "1.4.0",
"description": "Report the booted kernel, potential arguments, and the device."
},
"pci_devices": {
"query": "select * from pci_devices;",
"interval": 7200,
"platform": "posix",
"version": "1.0.4",
"description": "Report an inventory of PCI devices. Attaches and detaches will show up in hardware_events."
},
"fan_speeds": {
"query": "select * from fan_speed_sensors;",
"interval": 7200,
"platform": "darwin",
"version": "1.7.1",
"description": "Report current fan speeds in the target OSX system."
},
"temperatures": {
"query": "select * from temperature_sensors;",
"interval": 7200,
"platform": "darwin",
"version": "1.7.1",
"description": "Report current machine temperatures in the target OSX system."
},
"usb_devices": {
"query": "select * from usb_devices;",
"interval": 7200,
"platform": "posix",
"version": "1.2.0",
"description": "Report an inventory of USB devices. Attaches and detaches will show up in hardware_events."
},
"hardware_events": {
"query" : "select * from hardware_events where path <> '' or model <> '';",
"interval" : 7200,
"platform": "posix",
"removed": false,
"version" : "1.4.5",
"description" : "Retrieves all the hardware related events in the target OSX system.",
"value" : "Determine if a third party device was attached to the system."
},
"darwin_kernel_system_controls": {
"query": "select * from system_controls where subsystem = 'kern' and (name like '%boot%' or name like '%secure%' or name like '%single%');",
"interval": 7200,
"platform": "darwin",
"version": "1.4.3",
"description": "Double check the information reported in kernel_info and report the kernel signature."
},
"iokit_devicetree": {
"query": "select * from iokit_devicetree;",
"interval": 86400,
"platform": "darwin",
"version": "1.3.0",
"description": "General inventory of IOKit's devices on OS X."
},
"efi_file_hashes": {
"query": "select file.path, uid, gid, mode, 0 as atime, mtime, ctime, md5, sha1, sha256 from (select * from file where path like '/System/Library/CoreServices/%.efi' union select * from file where path like '/System/Library/LaunchDaemons/com.apple%efi%') file join hash using (path);",
"interval": 7200,
"removed": false,
"version": "1.6.1",
"platform": "darwin",
"description": "Hash files related to EFI platform updates and EFI bootloaders on primary boot partition. This does not hash bootloaders on the EFI/boot partition."
},
"kernel_extensions": {
"query" : "select * from kernel_extensions;",
"interval" : "7200",
"platform" : "darwin",
"version" : "1.4.5",
"description" : "Retrieves all the information about the current kernel extensions for the target OSX system."
},
"kernel_modules": {
"query" : "select * from kernel_modules;",
"interval" : "7200",
"platform" : "linux",
"version" : "1.4.5",
"description" : "Retrieves all the information for the current kernel modules in the target Linux system."
},
"windows_drivers": {
"query" : "select * from drivers;",
"interval" : "7200",
"platform" : "windows",
"version" : "2.2.0",
"description" : "Retrieves all the information for the current windows drivers in the target Windows system."
},
"device_nodes": {
"query": "select file.path, uid, gid, mode, 0 as atime, mtime, ctime, block_size, type from file where directory = '/dev/';",
"interval": "7200",
"platform": "posix",
"version": "1.6.0",
"description": "Inventory all 'device' nodes in /dev/."
}
}
}

View file

@ -0,0 +1 @@
{"attributes":{"created_at":"2021-12-29T09:23:21.137Z","created_by":"elastic","enabled":true,"name":"hardware-monitoring","queries":[{"id":"acpi_tables","interval":86400,"platform":"darwin,linux","query":"select * from acpi_tables;","version":"1.3.0"},{"id":"cpuid","interval":86400,"query":"select feature, value, output_register, output_bit, input_eax from cpuid;","version":"1.0.4"},{"id":"smbios_tables","interval":86400,"platform":"darwin,linux","query":"select * from smbios_tables;","version":"1.3.0"},{"id":"nvram","interval":7200,"platform":"darwin","query":"select * from nvram where name not in ('backlight-level', 'SystemAudioVolumeDB', 'SystemAudioVolume');","version":"1.0.2"},{"id":"kernel_info","interval":7200,"query":"select * from kernel_info join hash using (path);","version":"1.4.0"},{"id":"pci_devices","interval":7200,"platform":"darwin,linux","query":"select * from pci_devices;","version":"1.0.4"},{"id":"fan_speeds","interval":7200,"platform":"darwin","query":"select * from fan_speed_sensors;","version":"1.7.1"},{"id":"temperatures","interval":7200,"platform":"darwin","query":"select * from temperature_sensors;","version":"1.7.1"},{"id":"usb_devices","interval":7200,"platform":"darwin,linux","query":"select * from usb_devices;","version":"1.2.0"},{"id":"hardware_events","interval":7200,"platform":"darwin,linux","query":"select * from hardware_events where path <> '' or model <> '';","version":"1.4.5"},{"id":"darwin_kernel_system_controls","interval":7200,"platform":"darwin","query":"select * from system_controls where subsystem = 'kern' and (name like '%boot%' or name like '%secure%' or name like '%single%');","version":"1.4.3"},{"id":"iokit_devicetree","interval":86400,"platform":"darwin","query":"select * from iokit_devicetree;","version":"1.3.0"},{"id":"efi_file_hashes","interval":7200,"platform":"darwin","query":"select file.path, uid, gid, mode, 0 as atime, mtime, ctime, md5, sha1, sha256 from (select * from file where path like '/System/Library/CoreServices/%.efi' union select * from file where path like '/System/Library/LaunchDaemons/com.apple%efi%') file join hash using (path);","version":"1.6.1"},{"id":"kernel_extensions","interval":7200,"platform":"darwin","query":"select * from kernel_extensions;","version":"1.4.5"},{"id":"kernel_modules","interval":7200,"platform":"linux","query":"select * from kernel_modules;","version":"1.4.5"},{"id":"windows_drivers","interval":7200,"platform":"windows","query":"select * from drivers;","version":"2.2.0"},{"id":"device_nodes","interval":7200,"platform":"darwin,linux","query":"select file.path, uid, gid, mode, 0 as atime, mtime, ctime, block_size, type from file where directory = '/dev/';","version":"1.6.0"}],"updated_at":"2021-12-29T09:23:21.137Z","updated_by":"elastic"},"coreMigrationVersion":"8.1.0","id":"f70e1920-6888-11ec-9276-97ce5eb54433","references":[],"type":"osquery-pack","updated_at":"2021-12-29T09:23:21.147Z","version":"WzI4NDMxLDJd"}

View file

@ -9,12 +9,6 @@
"value": {
"field": "hours"
}
},
{
"key": "message",
"value": {
"field": "seconds"
}
}
],
"id": "Saved-Query-Id",

View file

@ -9,14 +9,61 @@ import { FLEET_AGENT_POLICIES } from '../../tasks/navigation';
import { addIntegration } from '../../tasks/integrations';
import { login } from '../../tasks/login';
// import { findAndClickButton, findFormFieldByRowsLabelAndType } from '../../tasks/live_query';
import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver';
describe('Super User - Add Integration', () => {
const integration = 'Osquery Manager';
before(() => {
runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query');
});
beforeEach(() => {
login();
});
it('should display Osquery integration in the Policies list once installed ', () => {
after(() => {
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query');
});
// it('should add the old integration and be able to upgrade it', () => {
// cy.visit(OLD_OSQUERY_MANAGER);
// cy.contains(integration).click();
// addIntegration();
// cy.contains('osquery_manager-1');
// cy.visit('app/fleet/policies');
// cy.contains(/^Default Fleet Server policy$/).click();
// cy.contains('Actions').click();
// cy.contains('View policy').click();
// cy.contains('name: osquery_manager-1');
// cy.contains(`version: 0.7.4`);
// cy.contains('Close').click();
// cy.contains(/^Osquery Manager$/).click();
// cy.contains(/^Settings$/).click();
// cy.contains(/^Upgrade to latest version$/).click();
// closeModalIfVisible();
// cy.contains('Updated Osquery Manager and upgraded policies', { timeout: 60000 });
// cy.visit('app/fleet/policies');
// cy.contains(/^Default Fleet Server policy$/).click();
// cy.contains('Actions').click();
// cy.contains('View policy').click();
// cy.contains('name: osquery_manager-1');
// cy.contains(`version: 0.8.1`);
// cy.visit('app/integrations/detail/osquery_manager/policies');
// cy.contains('Loading integration policies').should('exist');
// cy.contains('Loading integration policies').should('not.exist');
// cy.getBySel('integrationPolicyTable')
// .get('.euiTableRow', { timeout: 60000 })
// .should('have.lengthOf.above', 0);
// cy.get('.euiTableCellContent').get('.euiPopover__anchor').get(`[aria-label="Open"]`).click();
// cy.contains(/^Delete integration$/).click();
// closeModalIfVisible();
// cy.contains(/^Settings$/).click();
// cy.contains(/^Uninstall Osquery Manager$/).click();
// closeModalIfVisible();
// cy.contains(/^Successfully uninstalled Osquery Manager$/);
// });
it('add integration', () => {
cy.visit(FLEET_AGENT_POLICIES);
cy.contains('Default Fleet Server policy').click();
cy.contains('Add integration').click();
@ -24,4 +71,53 @@ describe('Super User - Add Integration', () => {
addIntegration();
cy.contains('osquery_manager-');
});
// it('should have integration and packs copied when upgrading integration', () => {
// const packageName = 'osquery_manager';
// const oldVersion = '0.7.4';
// const newVersion = '0.8.1';
//
// cy.visit(`app/integrations/detail/${packageName}-${oldVersion}/overview`);
// cy.contains('Add Osquery Manager').click();
// cy.contains('Save and continue').click();
// cy.contains('Add Elastic Agent later').click();
// cy.contains('Upgrade');
// cy.contains('Default policy').click();
// cy.get('tr')
// .should('contain', 'osquery_manager-2')
// .and('contain', 'Osquery Manager')
// .and('contain', `v${oldVersion}`);
// cy.contains('Actions').click();
// cy.contains('View policy').click();
// cy.contains('name: osquery_manager-2');
// cy.contains(`version: ${oldVersion}`);
// cy.contains('Close').click();
// navigateTo('app/osquery/packs');
// findAndClickButton('Add pack');
// findFormFieldByRowsLabelAndType('Name', 'Integration');
// findFormFieldByRowsLabelAndType('Scheduled agent policies (optional)', '{downArrow} {enter}');
// findAndClickButton('Add query');
// cy.react('EuiComboBox', { props: { placeholder: 'Search for saved queries' } })
// .click()
// .type('{downArrow} {enter}');
// cy.contains(/^Save$/).click();
// cy.contains(/^Save pack$/).click();
// cy.visit('app/fleet/policies');
// cy.contains('Default policy').click();
// cy.contains('Upgrade').click();
// cy.contains(/^Advanced$/).click();
// cy.contains('"Integration":');
// cy.contains(/^Upgrade integration$/).click();
// cy.contains(/^osquery_manager-2$/).click();
// cy.contains(/^Advanced$/).click();
// cy.contains('"Integration":');
// cy.contains('Cancel').click();
// cy.get('tr')
// .should('contain', 'osquery_manager-2')
// .and('contain', 'Osquery Manager')
// .and('contain', `v${newVersion}`);
// cy.contains('Actions').click();
// cy.contains('View policy').click();
// cy.contains('name: osquery_manager-2');
// cy.contains(`version: ${newVersion}`);
// });
});

View file

@ -0,0 +1,47 @@
/*
* 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.
*/
import { navigateTo } from '../../tasks/navigation';
import { login } from '../../tasks/login';
import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver';
describe('SuperUser - Delete ECS Mappings', () => {
const SAVED_QUERY_ID = 'Saved-Query-Id';
before(() => {
runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query');
});
beforeEach(() => {
login();
navigateTo('/app/osquery/saved_queries');
});
after(() => {
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query');
});
it('to click the edit button and edit pack', () => {
cy.react('CustomItemAction', {
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
cy.contains('Custom key/value pairs. e.g. {"application":"foo-bar","env":"production"}').should(
'exist'
);
cy.contains('Hours of uptime').should('exist');
cy.react('EuiButtonIcon', { props: { id: 'labels-trash' } }).click();
cy.react('EuiButton').contains('Update query').click();
cy.wait(1000);
cy.react('CustomItemAction', {
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
cy.contains('Custom key/value pairs. e.g. {"application":"foo-bar","env":"production"}').should(
'not.exist'
);
cy.contains('Hours of uptime').should('not.exist');
});
});

View file

@ -30,10 +30,10 @@ describe('Super User - Live Query', () => {
checkResults();
cy.react('EuiDataGridHeaderCellWrapper', {
props: { id: 'osquery.days', index: 1 },
props: { id: 'osquery.days.number', index: 1 },
});
cy.react('EuiDataGridHeaderCellWrapper', {
props: { id: 'osquery.hours', index: 2 },
props: { id: 'osquery.hours.number', index: 2 },
});
cy.react('EuiAccordion', { props: { buttonContent: 'Advanced' } }).click();
@ -46,7 +46,7 @@ describe('Super User - Live Query', () => {
props: { id: 'message', index: 1 },
});
cy.react('EuiDataGridHeaderCellWrapper', {
props: { id: 'osquery.days', index: 2 },
props: { id: 'osquery.days.number', index: 2 },
}).react('EuiIconIndexMapping');
});
});

View file

@ -23,12 +23,12 @@ describe('Super User - Metrics', () => {
});
it('should be able to run the query', () => {
cy.get('[data-test-subj="toggleNavButton"]').click();
cy.getBySel('toggleNavButton').click();
cy.contains('Metrics').click();
cy.wait(1000);
cy.get('[data-test-subj="nodeContainer"]').click();
cy.getBySel('nodeContainer').click();
cy.contains('Osquery').click();
inputQuery('select * from uptime;');
@ -36,17 +36,17 @@ describe('Super User - Metrics', () => {
checkResults();
});
it('should be able to run the previously saved query', () => {
cy.get('[data-test-subj="toggleNavButton"]').click();
cy.get('[data-test-subj="collapsibleNavAppLink"').contains('Metrics').click();
cy.getBySel('toggleNavButton').click();
cy.getBySel('collapsibleNavAppLink').contains('Metrics').click();
cy.wait(500);
cy.get('[data-test-subj="nodeContainer"]').click();
cy.getBySel('nodeContainer').click();
cy.contains('Osquery').click();
cy.get('[data-test-subj="comboBoxInput"]').first().click();
cy.getBySel('comboBoxInput').first().click();
cy.wait(500);
cy.get('div[role=listBox]').should('have.lengthOf.above', 0);
cy.get('[data-test-subj="comboBoxInput"]').first().type('{downArrow}{enter}');
cy.getBySel('comboBoxInput').first().type('{downArrow}{enter}');
submitQuery();
checkResults();

View file

@ -21,87 +21,150 @@ describe('SuperUser - Packs', () => {
const PACK_NAME = 'Pack-name';
const NEW_QUERY_NAME = 'new-query-name';
before(() => {
runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query');
});
beforeEach(() => {
login();
navigateTo('/app/osquery');
});
describe('Create and edit a pack', () => {
before(() => {
runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query');
});
beforeEach(() => {
login();
navigateTo('/app/osquery');
});
after(() => {
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query');
});
after(() => {
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query');
});
it('should add a pack from a saved query', () => {
cy.contains('Packs').click();
findAndClickButton('Add pack');
findFormFieldByRowsLabelAndType('Name', PACK_NAME);
findFormFieldByRowsLabelAndType('Description (optional)', 'Pack description');
findFormFieldByRowsLabelAndType(
'Scheduled agent policies (optional)',
'Default Fleet Server policy'
);
cy.react('List').first().click();
findAndClickButton('Add query');
cy.contains('Attach next query');
cy.react('EuiComboBox', { props: { placeholder: 'Search for saved queries' } })
.click()
.type(SAVED_QUERY_ID);
cy.react('List').first().click();
cy.react('EuiFormRow', { props: { label: 'Interval (s)' } })
.click()
.clear()
.type('10');
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
cy.react('EuiTableRow').contains(SAVED_QUERY_ID);
findAndClickButton('Save pack');
cy.contains('Save and deploy changes');
findAndClickButton('Save and deploy changes');
cy.contains(PACK_NAME);
});
it('should add a pack from a saved query', () => {
cy.contains('Packs').click();
findAndClickButton('Add pack');
findFormFieldByRowsLabelAndType('Name', PACK_NAME);
findFormFieldByRowsLabelAndType('Description (optional)', 'Pack description');
findFormFieldByRowsLabelAndType(
'Scheduled agent policies (optional)',
'Default Fleet Server policy'
);
cy.react('List').first().click();
findAndClickButton('Add query');
cy.contains('Attach next query');
cy.react('EuiComboBox', { props: { placeholder: 'Search for saved queries' } })
.click()
.type(SAVED_QUERY_ID);
cy.react('List').first().click();
cy.react('EuiFormRow', { props: { label: 'Interval (s)' } })
.click()
.clear()
.type('10');
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
cy.react('EuiTableRow').contains(SAVED_QUERY_ID);
findAndClickButton('Save pack');
cy.contains('Save and deploy changes');
findAndClickButton('Save and deploy changes');
cy.contains(PACK_NAME);
});
it('to click the edit button and edit pack', () => {
preparePack(PACK_NAME, SAVED_QUERY_ID);
findAndClickButton('Edit');
cy.contains(`Edit ${PACK_NAME}`);
findAndClickButton('Add query');
cy.contains('Attach next query');
inputQuery('select * from uptime');
findFormFieldByRowsLabelAndType('ID', NEW_QUERY_NAME);
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
cy.react('EuiTableRow').contains(NEW_QUERY_NAME);
findAndClickButton('Update pack');
cy.contains('Save and deploy changes');
findAndClickButton('Save and deploy changes');
it('to click the edit button and edit pack', () => {
preparePack(PACK_NAME, SAVED_QUERY_ID);
findAndClickButton('Edit');
cy.contains(`Edit ${PACK_NAME}`);
findAndClickButton('Add query');
cy.contains('Attach next query');
inputQuery('select * from uptime');
findFormFieldByRowsLabelAndType('ID', NEW_QUERY_NAME);
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
cy.react('EuiTableRow').contains(NEW_QUERY_NAME);
findAndClickButton('Update pack');
cy.contains('Save and deploy changes');
findAndClickButton('Save and deploy changes');
});
// THIS TESTS TAKES TOO LONG FOR NOW - LET ME THINK IT THROUGH
it('to click the icon and visit discover', () => {
preparePack(PACK_NAME, SAVED_QUERY_ID);
cy.react('CustomItemAction', {
props: { index: 0, item: { id: SAVED_QUERY_ID } },
}).click();
cy.getBySel('superDatePickerToggleQuickMenuButton').click();
cy.getBySel('superDatePickerToggleRefreshButton').click();
cy.getBySel('superDatePickerRefreshIntervalInput').clear().type('10');
cy.get('button').contains('Apply').click();
cy.getBySel('discoverDocTable', { timeout: 60000 }).contains(
`pack_${PACK_NAME}_${SAVED_QUERY_ID}`
);
});
it('by clicking in Lens button', () => {
let lensUrl = '';
cy.window().then((win) => {
cy.stub(win, 'open')
.as('windowOpen')
.callsFake((url) => {
lensUrl = url;
});
});
preparePack(PACK_NAME, SAVED_QUERY_ID);
cy.react('CustomItemAction', {
props: { index: 1, item: { id: SAVED_QUERY_ID } },
}).click();
cy.window()
.its('open')
.then(() => {
cy.visit(lensUrl);
});
cy.getBySel('lnsWorkspace');
cy.getBySel('breadcrumbs').contains(`Action pack_${PACK_NAME}_${SAVED_QUERY_ID} results`);
});
// strange behaviour with modal
it('activate and deactive pack', () => {
cy.contains('Packs').click();
cy.react('ActiveStateSwitchComponent', {
props: { item: { attributes: { name: PACK_NAME } } },
}).click();
cy.contains(`Successfully deactivated "${PACK_NAME}" pack`).should('not.exist');
cy.contains(`Successfully deactivated "${PACK_NAME}" pack`).should('exist');
cy.react('ActiveStateSwitchComponent', {
props: { item: { attributes: { name: PACK_NAME } } },
}).click();
cy.getBySel('confirmModalConfirmButton').click();
cy.contains(`Successfully activated "${PACK_NAME}" pack`).should('not.exist');
cy.contains(`Successfully activated "${PACK_NAME}" pack`).should('exist');
});
it('delete all queries in the pack', () => {
preparePack(PACK_NAME, SAVED_QUERY_ID);
cy.contains(/^Edit$/).click();
cy.getBySel('checkboxSelectAll').click();
cy.contains(/^Delete \d+ quer(y|ies)/).click();
cy.contains(/^Update pack$/).click();
cy.react('EuiButtonDisplay')
.contains(/^Save and deploy changes$/)
.click();
cy.contains(`${PACK_NAME}`).click();
cy.contains(`${PACK_NAME} details`);
cy.contains(/^No items found/);
});
it('to click delete button', () => {
preparePack(PACK_NAME, SAVED_QUERY_ID);
findAndClickButton('Edit');
deleteAndConfirm('pack');
});
});
// THIS TESTS TAKES TOO LONG FOR NOW - LET ME THINK IT THROUGH
// it('to click the icon and visit discover', () => {
// preparePack(PACK_NAME, SAVED_QUERY_ID);
// cy.react('CustomItemAction', {
// props: { index: 0, item: { id: SAVED_QUERY_ID } },
// }).click();
// cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"').click();
// cy.get('[data-test-subj="superDatePickerToggleRefreshButton"').click();
// cy.get('[data-test-subj="superDatePickerRefreshIntervalInput"').clear().type('10');
// cy.get('button').contains('Apply').click();
// cy.get('[data-test-subj="discoverDocTable"]', { timeout: 60000 }).contains(
// `pack_${PACK_NAME}_${SAVED_QUERY_ID}`
// );
// });
// it('by clicking in Lens button', () => {
// preparePack(PACK_NAME, SAVED_QUERY_ID);
// cy.react('CustomItemAction', {
// props: { index: 1, item: { id: SAVED_QUERY_ID } },
// }).click();
// cy.get('[data-test-subj="lnsWorkspace"]');
// cy.get('[data-test-subj="breadcrumbs"]').contains(
// `Action pack_${PACK_NAME}_${SAVED_QUERY_ID} results`
// );
// });
it('to click delete button', () => {
preparePack(PACK_NAME, SAVED_QUERY_ID);
findAndClickButton('Edit');
deleteAndConfirm('pack');
describe.skip('Remove queries from pack', () => {
const TEST_PACK = 'Test-pack';
before(() => {
runKbnArchiverScript(ArchiverMethod.LOAD, 'hardware_monitoring');
});
beforeEach(() => {
login();
navigateTo('/app/osquery');
});
after(() => {
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'hardware_monitoring');
});
it('should remove ALL queries', () => {
preparePack(TEST_PACK, SAVED_QUERY_ID);
});
});
});

View file

@ -26,59 +26,93 @@ describe('Super User - Saved queries', () => {
navigateTo('/app/osquery');
});
it('should save the query', () => {
cy.contains('New live query').click();
selectAllAgents();
inputQuery(DEFAULT_QUERY);
submitQuery();
checkResults();
cy.contains('Save for later').click();
cy.contains('Save query');
findFormFieldByRowsLabelAndType('ID', SAVED_QUERY_ID);
findFormFieldByRowsLabelAndType('Description', SAVED_QUERY_DESCRIPTION);
cy.react('EuiButtonDisplay').contains('Save').click();
});
it(
'should create a new query and verify: \n ' +
'- hidden columns, full screen and sorting \n' +
'- pagination \n' +
'- query can viewed (status), edited and deleted ',
() => {
cy.contains('New live query').click();
selectAllAgents();
inputQuery(DEFAULT_QUERY);
submitQuery();
checkResults();
// enter fullscreen
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
cy.contains(/Full screen$/).should('exist');
cy.contains('Exit full screen').should('not.exist');
cy.getBySel('dataGridFullScreenButton').click();
it('should view query details in status', () => {
cy.contains('New live query');
cy.react('ActionTableResultsButton').first().click();
cy.wait(1000);
cy.contains(DEFAULT_QUERY);
checkResults();
cy.react('EuiTab', { props: { id: 'status' } }).click();
cy.wait(1000);
cy.react('EuiTableRow').should('have.lengthOf', 1);
cy.contains('Successful').siblings().contains(1);
});
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
cy.contains(/Full screen$/).should('not.exist');
cy.contains('Exit full screen').should('exist');
it('should display a previously saved query and run it', () => {
cy.contains('Saved queries').click();
cy.contains(SAVED_QUERY_ID);
cy.react('PlayButtonComponent', {
props: { savedQuery: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
selectAllAgents();
submitQuery();
});
// hidden columns
cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.cmdline' } }).click();
cy.contains(/Hide column$/).click();
cy.react('EuiDataGridHeaderCellWrapper', {
props: { id: 'osquery.disk_bytes_written.number' },
}).click();
cy.contains(/Hide column$/).click();
cy.contains('2 columns hidden').should('exist');
// change pagination
cy.getBySel('pagination-button-next').click().wait(500).click();
cy.contains('2 columns hidden').should('exist');
it('should edit the saved query', () => {
cy.contains('Saved queries').click();
cy.contains(SAVED_QUERY_ID);
cy.react('CustomItemAction', {
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
findFormFieldByRowsLabelAndType('Description', ' Edited');
cy.react('EuiButton').contains('Update query').click();
cy.contains(`${SAVED_QUERY_DESCRIPTION} Edited`);
});
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
cy.contains(/Full screen$/).should('not.exist');
cy.contains('Exit full screen').should('exist');
cy.getBySel('dataGridFullScreenButton').click();
it('should delete the saved query', () => {
cy.contains('Saved queries').click();
cy.contains(SAVED_QUERY_ID);
cy.react('CustomItemAction', {
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
deleteAndConfirm('query');
cy.contains(SAVED_QUERY_ID);
});
// sorting
cy.react('EuiDataGridHeaderCellWrapper', {
props: { id: 'osquery.egid' },
}).click();
cy.contains(/Sort A-Z$/).click();
cy.contains('2 columns hidden').should('exist');
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
cy.contains(/Full screen$/).should('exist');
// save new query
cy.contains('Exit full screen').should('not.exist');
cy.contains('Save for later').click();
cy.contains('Save query');
findFormFieldByRowsLabelAndType('ID', SAVED_QUERY_ID);
findFormFieldByRowsLabelAndType('Description (optional)', SAVED_QUERY_DESCRIPTION);
cy.react('EuiButtonDisplay').contains('Save').click();
// visit Status results
cy.react('EuiTab', { props: { id: 'status' } }).click();
cy.react('EuiTableRow').should('have.lengthOf', 1);
cy.contains('Successful').siblings().contains(1);
// play saved query
cy.contains('Saved queries').click();
cy.contains(SAVED_QUERY_ID);
cy.react('PlayButtonComponent', {
props: { savedQuery: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
selectAllAgents();
submitQuery();
// edit saved query
cy.contains('Saved queries').click();
cy.contains(SAVED_QUERY_ID);
cy.react('CustomItemAction', {
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
findFormFieldByRowsLabelAndType('Description (optional)', ' Edited');
cy.react('EuiButton').contains('Update query').click();
cy.contains(`${SAVED_QUERY_DESCRIPTION} Edited`);
// delete saved query
cy.contains(SAVED_QUERY_ID);
cy.react('CustomItemAction', {
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
}).click();
deleteAndConfirm('query');
cy.contains(SAVED_QUERY_ID);
cy.contains(/^No items found/);
}
);
});

View file

@ -30,3 +30,7 @@
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
Cypress.Commands.add('getBySel', (selector, ...args) =>
cy.get(`[data-test-subj=${selector}]`, ...args)
);

View file

@ -16,14 +16,14 @@ import {
export const addIntegration = () => {
cy.getBySel(ADD_POLICY_BTN).click();
cy.getBySel(DATA_COLLECTION_SETUP_STEP).find('.euiLoadingSpinner').should('not.exist');
cy.getBySel('comboBoxInput').click().type('Default fleet {downArrow} {enter}');
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click();
// sometimes agent is assigned to default policy, sometimes not
closeModalIfVisible();
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN, { timeout: 60000 }).should('not.exist');
};
function closeModalIfVisible() {
export function closeModalIfVisible() {
cy.get('body').then(($body) => {
if ($body.find(CONFIRM_MODAL_BTN_SEL).length) {
cy.getBySel(CONFIRM_MODAL_BTN).click();

View file

@ -7,7 +7,7 @@
import { LIVE_QUERY_EDITOR } from '../screens/live_query';
export const DEFAULT_QUERY = 'select * from processes;';
export const DEFAULT_QUERY = 'select * from processes, users;';
export const selectAllAgents = () => {
cy.react('EuiComboBox', { props: { placeholder: 'Select agents or groups' } }).type('All agents');
@ -22,10 +22,10 @@ export const inputQuery = (query: string) => cy.get(LIVE_QUERY_EDITOR).type(quer
export const submitQuery = () => cy.contains('Submit').click();
export const checkResults = () =>
cy.get('[data-test-subj="dataGridRowCell"]', { timeout: 60000 }).should('have.lengthOf.above', 0);
cy.getBySel('dataGridRowCell', { timeout: 60000 }).should('have.lengthOf.above', 0);
export const typeInECSFieldInput = (text: string) =>
cy.get('[data-test-subj="ECS-field-input"]').click().type(text);
cy.getBySel('ECS-field-input').click().type(text);
export const typeInOsqueryFieldInput = (text: string) =>
cy.react('OsqueryColumnFieldComponent').first().react('ResultComboBox').click().type(text);

View file

@ -11,6 +11,7 @@ export const INTEGRATIONS = 'app/integrations#/';
export const FLEET = 'app/fleet/';
export const FLEET_AGENT_POLICIES = 'app/fleet/policies';
export const OSQUERY = 'app/osquery';
export const OLD_OSQUERY_MANAGER = 'app/integrations/detail/osquery_manager-0.7.4/settings';
export const NEW_LIVE_QUERY = 'app/osquery/live_queries/new';
export const OSQUERY_INTEGRATION_PAGE = '/app/fleet/integrations/osquery_manager/add-integration';
export const navigateTo = (page: string, opts?: Partial<Cypress.VisitOptions>) => {

View file

@ -9,6 +9,4 @@ export const preparePack = (packName: string, savedQueryId: string) => {
cy.contains('Packs').click();
const createdPack = cy.contains(packName);
createdPack.click();
cy.waitForReact(1000);
cy.react('EuiTableRow').contains(savedQueryId);
};

View file

@ -13,7 +13,7 @@ import {
getColorForAgentStatus,
getLabelForAgentStatus,
} from './services/agent_status';
import type { ActionAgentStatus } from './types';
import { ActionAgentStatus } from './types';
export const ActionAgentsStatusBadges = memo<{
agentStatus: { [k in ActionAgentStatus]: number };

View file

@ -10,7 +10,7 @@ import { EuiColorPaletteDisplay } from '@elastic/eui';
import React, { useMemo } from 'react';
import { AGENT_STATUSES, getColorForAgentStatus } from './services/agent_status';
import type { ActionAgentStatus } from './types';
import { ActionAgentStatus } from './types';
const StyledEuiColorPaletteDisplay = styled(EuiColorPaletteDisplay)`
&.osquery-action-agent-status-bar {

View file

@ -1,37 +0,0 @@
/*
* 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.
*/
import {
PaginationInputPaginated,
FactoryQueryTypes,
StrategyResponseType,
Inspect,
} from '../../common/search_strategy';
export type InspectResponse = Inspect & { response: string[] };
export const generateTablePaginationOptions = (
activePage: number,
limit: number
): PaginationInputPaginated => {
const cursorStart = activePage * limit;
return {
activePage,
cursorStart,
fakePossibleCount: 4 <= activePage && activePage > 0 ? limit * (activePage + 2) : limit * 5,
querySize: limit,
};
};
export const getInspectResponse = <T extends FactoryQueryTypes>(
response: StrategyResponseType<T>,
prevResponse: InspectResponse
): InspectResponse => ({
dsl: response?.inspect?.dsl ?? prevResponse?.dsl ?? [],
response:
response != null ? [JSON.stringify(response.rawResponse, null, 2)] : prevResponse?.response,
});

View file

@ -8,7 +8,7 @@
import { euiPaletteColorBlindBehindText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { ActionAgentStatus } from '../types';
import { ActionAgentStatus } from '../types';
const visColors = euiPaletteColorBlindBehindText();
const colorToHexMap = {
@ -20,15 +20,19 @@ const colorToHexMap = {
danger: visColors[9],
};
export const AGENT_STATUSES: ActionAgentStatus[] = ['success', 'pending', 'failed'];
export const AGENT_STATUSES: ActionAgentStatus[] = [
ActionAgentStatus.SUCCESS,
ActionAgentStatus.PENDING,
ActionAgentStatus.FAILED,
];
export function getColorForAgentStatus(agentStatus: ActionAgentStatus): string {
switch (agentStatus) {
case 'success':
case ActionAgentStatus.SUCCESS:
return colorToHexMap.success;
case 'pending':
case ActionAgentStatus.PENDING:
return colorToHexMap.default;
case 'failed':
case ActionAgentStatus.FAILED:
return colorToHexMap.danger;
default:
throw new Error(`Unsupported action agent status ${agentStatus}`);
@ -37,11 +41,11 @@ export function getColorForAgentStatus(agentStatus: ActionAgentStatus): string {
export function getLabelForAgentStatus(agentStatus: ActionAgentStatus, expired: boolean): string {
switch (agentStatus) {
case 'success':
case ActionAgentStatus.SUCCESS:
return i18n.translate('xpack.osquery.liveQueryActionResults.summary.successfulLabelText', {
defaultMessage: 'Successful',
});
case 'pending':
case ActionAgentStatus.PENDING:
return expired
? i18n.translate('xpack.osquery.liveQueryActionResults.summary.expiredLabelText', {
defaultMessage: 'Expired',
@ -49,7 +53,7 @@ export function getLabelForAgentStatus(agentStatus: ActionAgentStatus, expired:
: i18n.translate('xpack.osquery.liveQueryActionResults.summary.pendingLabelText', {
defaultMessage: 'Not yet responded',
});
case 'failed':
case ActionAgentStatus.FAILED:
return i18n.translate('xpack.osquery.liveQueryActionResults.summary.failedLabelText', {
defaultMessage: 'Failed',
});

View file

@ -5,4 +5,8 @@
* 2.0.
*/
export type ActionAgentStatus = 'success' | 'pending' | 'failed';
export enum ActionAgentStatus {
SUCCESS = 'success',
PENDING = 'pending',
FAILED = 'failed',
}

View file

@ -9,7 +9,12 @@ import { flatten, reverse, uniqBy } from 'lodash/fp';
import { useQuery } from 'react-query';
import { i18n } from '@kbn/i18n';
import { createFilter } from '../common/helpers';
import {
createFilter,
getInspectResponse,
InspectResponse,
generateTablePaginationOptions,
} from '../common/helpers';
import { useKibana } from '../common/lib/kibana';
import {
ResultEdges,
@ -22,7 +27,6 @@ import {
import { ESTermQuery } from '../../common/typed_json';
import { queryClient } from '../query_client';
import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers';
import { useErrorToast } from '../common/hooks/use_error_toast';
export interface ResultsArgs {

View file

@ -1,37 +0,0 @@
/*
* 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.
*/
import {
PaginationInputPaginated,
FactoryQueryTypes,
StrategyResponseType,
Inspect,
} from '../../common/search_strategy';
export type InspectResponse = Inspect & { response: string[] };
export const generateTablePaginationOptions = (
activePage: number,
limit: number
): PaginationInputPaginated => {
const cursorStart = activePage * limit;
return {
activePage,
cursorStart,
fakePossibleCount: 4 <= activePage && activePage > 0 ? limit * (activePage + 2) : limit * 5,
querySize: limit,
};
};
export const getInspectResponse = <T extends FactoryQueryTypes>(
response: StrategyResponseType<T>,
prevResponse: InspectResponse
): InspectResponse => ({
dsl: response?.inspect?.dsl ?? prevResponse?.dsl ?? [],
response:
response != null ? [JSON.stringify(response.rawResponse, null, 2)] : prevResponse?.response,
});

View file

@ -62,6 +62,7 @@ export const useActionDetails = ({ actionId, filterQuery, skip = false }: UseAct
defaultMessage: 'Error while fetching action details',
}),
}),
refetchOnWindowFocus: false,
retryDelay: 1000,
}
);

View file

@ -8,7 +8,12 @@
import { useQuery } from 'react-query';
import { i18n } from '@kbn/i18n';
import { createFilter } from '../common/helpers';
import {
createFilter,
generateTablePaginationOptions,
getInspectResponse,
InspectResponse,
} from '../common/helpers';
import { useKibana } from '../common/lib/kibana';
import {
ActionEdges,
@ -20,7 +25,6 @@ import {
} from '../../common/search_strategy';
import { ESTermQuery } from '../../common/typed_json';
import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers';
import { useErrorToast } from '../common/hooks/use_error_toast';
export interface ActionsArgs {

View file

@ -84,6 +84,22 @@ const AgentsTableComponent: React.FC<AgentsTableProps> = ({ agentSelection, onCh
const defaultValueInitialized = useRef(false);
useEffect(() => {
const handleSelectedOptions = (selection: string[], label: string) => {
const agentOptions = find(['label', label], options);
if (agentOptions) {
const defaultOptions = agentOptions.options?.filter((option) => {
if (option.key) {
return selection.includes(option.key);
}
});
if (defaultOptions?.length) {
setSelectedOptions(defaultOptions);
defaultValueInitialized.current = true;
}
}
};
if (agentSelection && !defaultValueInitialized.current && options.length) {
if (agentSelection.allAgentsSelected) {
const allAgentsOptions = find(['label', ALL_AGENTS_LABEL], options);
@ -95,35 +111,11 @@ const AgentsTableComponent: React.FC<AgentsTableProps> = ({ agentSelection, onCh
}
if (agentSelection.policiesSelected.length) {
const policyOptions = find(['label', AGENT_POLICY_LABEL], options);
if (policyOptions) {
const defaultOptions = policyOptions.options?.filter((option) =>
// @ts-expect-error update types
agentSelection.policiesSelected.includes(option.key)
);
if (defaultOptions?.length) {
setSelectedOptions(defaultOptions);
defaultValueInitialized.current = true;
}
}
handleSelectedOptions(agentSelection.policiesSelected, AGENT_POLICY_LABEL);
}
if (agentSelection.agents.length) {
const agentOptions = find(['label', AGENT_SELECTION_LABEL], options);
if (agentOptions) {
const defaultOptions = agentOptions.options?.filter((option) =>
// @ts-expect-error update types
agentSelection.agents.includes(option.key)
);
if (defaultOptions?.length) {
setSelectedOptions(defaultOptions);
defaultValueInitialized.current = true;
}
}
handleSelectedOptions(agentSelection.agents, AGENT_SELECTION_LABEL);
}
}
}, [agentSelection, options, selectedOptions]);

View file

@ -7,12 +7,6 @@
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { euiPaletteColorBlindBehindText } from '@elastic/eui';
import {
PaginationInputPaginated,
FactoryQueryTypes,
StrategyResponseType,
Inspect,
} from '../../common/search_strategy';
import {
AGENT_GROUP_KEY,
SelectedGroups,
@ -25,8 +19,6 @@ import {
GroupOption,
} from './types';
export type InspectResponse = Inspect & { response: string[] };
export const getNumOverlapped = (
{ policy = {}, platform = {} }: SelectedGroups,
overlap: Overlap
@ -158,26 +150,3 @@ export const generateAgentSelection = (selection: GroupOption[]) => {
}
return { newAgentSelection, selectedGroups, selectedAgents };
};
export const generateTablePaginationOptions = (
activePage: number,
limit: number
): PaginationInputPaginated => {
const cursorStart = activePage * limit;
return {
activePage,
cursorStart,
fakePossibleCount: 4 <= activePage && activePage > 0 ? limit * (activePage + 2) : limit * 5,
querySize: limit,
};
};
export const getInspectResponse = <T extends FactoryQueryTypes>(
response: StrategyResponseType<T>,
prevResponse?: InspectResponse
): InspectResponse => ({
dsl: response?.inspect?.dsl ?? prevResponse?.dsl ?? [],
// @ts-expect-error update types
response:
response != null ? [JSON.stringify(response.rawResponse, null, 2)] : prevResponse?.response,
});

View file

@ -16,7 +16,8 @@ import {
AgentsStrategyResponse,
} from '../../common/search_strategy';
import { generateTablePaginationOptions, processAggregations } from './helpers';
import { processAggregations } from './helpers';
import { generateTablePaginationOptions } from '../common/helpers';
import { Overlap, Group } from './types';
import { useErrorToast } from '../common/hooks/use_error_toast';

View file

@ -7,7 +7,38 @@
import { isString } from 'lodash/fp';
import {
PaginationInputPaginated,
FactoryQueryTypes,
StrategyResponseType,
Inspect,
} from '../../common/search_strategy';
import { ESQuery } from '../../common/typed_json';
export const createFilter = (filterQuery: ESQuery | string | undefined) =>
isString(filterQuery) ? filterQuery : JSON.stringify(filterQuery);
export type InspectResponse = Inspect & { response: string[] };
export const generateTablePaginationOptions = (
activePage: number,
limit: number
): PaginationInputPaginated => {
const cursorStart = activePage * limit;
return {
activePage,
cursorStart,
fakePossibleCount: 4 <= activePage && activePage > 0 ? limit * (activePage + 2) : limit * 5,
querySize: limit,
};
};
export const getInspectResponse = <T extends FactoryQueryTypes>(
response: StrategyResponseType<T>,
prevResponse: InspectResponse
): InspectResponse => ({
dsl: response?.inspect?.dsl ?? prevResponse?.dsl ?? [],
response:
response != null ? [JSON.stringify(response.rawResponse, null, 2)] : prevResponse?.response,
});

View file

@ -156,12 +156,27 @@ const breadcrumbGetters: {
};
export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) {
const { chrome, http } = useKibana().services;
const { chrome, http, application } = useKibana().services;
const breadcrumbs: ChromeBreadcrumb[] =
breadcrumbGetters[page]?.(values).map((breadcrumb) => ({
...breadcrumb,
href: breadcrumb.href ? http.basePath.prepend(`${BASE_PATH}${breadcrumb.href}`) : undefined,
})) || [];
breadcrumbGetters[page]?.(values).map((breadcrumb) => {
const href = breadcrumb.href
? http.basePath.prepend(`${BASE_PATH}${breadcrumb.href}`)
: undefined;
return {
...breadcrumb,
href,
onClick: href
? (ev: React.MouseEvent) => {
if (ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey) {
return;
}
ev.preventDefault();
application.navigateToUrl(href);
}
: undefined,
};
}) || [];
const docTitle: string[] = [...breadcrumbs]
.reverse()
.map((breadcrumb) => breadcrumb.text as string);

View file

@ -5,33 +5,16 @@
* 2.0.
*/
/* eslint-disable react-hooks/rules-of-hooks */
import React from 'react';
import { EuiLoadingElastic, EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui';
import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import {
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiTabs,
EuiTab,
EuiLoadingElastic,
EuiPage,
EuiPageBody,
EuiPageContent,
} from '@elastic/eui';
import { useLocation } from 'react-router-dom';
import { Container, Nav, Wrapper } from './layouts';
import { Container, Wrapper } from './layouts';
import { OsqueryAppRoutes } from '../routes';
import { useRouterNavigate } from '../common/lib/kibana';
import { ManageIntegrationLink } from './manage_integration_link';
import { useOsqueryIntegrationStatus } from '../common/hooks';
import { OsqueryAppEmptyState } from './empty_state';
import { MainNavigation } from './main_navigation';
const OsqueryAppComponent = () => {
const location = useLocation();
const section = useMemo(() => location.pathname.split('/')[1] ?? 'overview', [location.pathname]);
const { data: osqueryIntegration, isFetched } = useOsqueryIntegrationStatus();
if (!isFetched) {
@ -59,55 +42,7 @@ const OsqueryAppComponent = () => {
return (
<Container id="osquery-app">
<Wrapper>
<Nav>
<EuiFlexGroup gutterSize="l" alignItems="center">
<EuiFlexItem>
<EuiTabs display="condensed">
<EuiTab
isSelected={section === 'live_queries'}
{...useRouterNavigate('live_queries')}
>
<FormattedMessage
id="xpack.osquery.appNavigation.liveQueriesLinkText"
defaultMessage="Live queries"
/>
</EuiTab>
<EuiTab isSelected={section === 'packs'} {...useRouterNavigate('packs')}>
<FormattedMessage
id="xpack.osquery.appNavigation.packsLinkText"
defaultMessage="Packs"
/>
</EuiTab>
<EuiTab
isSelected={section === 'saved_queries'}
{...useRouterNavigate('saved_queries')}
>
<FormattedMessage
id="xpack.osquery.appNavigation.savedQueriesLinkText"
defaultMessage="Saved queries"
/>
</EuiTab>
</EuiTabs>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" direction="row">
<EuiFlexItem>
<EuiButtonEmpty
iconType="popout"
href="https://ela.st/osquery-feedback"
target="_blank"
>
<FormattedMessage
id="xpack.osquery.appNavigation.sendFeedbackButton"
defaultMessage="Send feedback"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<ManageIntegrationLink />
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</Nav>
<MainNavigation />
<OsqueryAppRoutes />
</Wrapper>
</Container>

View file

@ -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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs } from '@elastic/eui';
import { useLocation } from 'react-router-dom';
import { useRouterNavigate } from '../common/lib/kibana';
import { ManageIntegrationLink } from './manage_integration_link';
import { Nav } from './layouts';
enum Section {
LiveQueries = 'live_queries',
Packs = 'packs',
SavedQueries = 'saved_queries',
}
export const MainNavigation = () => {
const location = useLocation();
const section = useMemo(() => location.pathname.split('/')[1] ?? 'overview', [location.pathname]);
return (
<Nav>
<EuiFlexGroup gutterSize="l" alignItems="center">
<EuiFlexItem>
<EuiTabs display="condensed">
<EuiTab
isSelected={section === Section.LiveQueries}
{...useRouterNavigate(Section.LiveQueries)}
>
<FormattedMessage
id="xpack.osquery.appNavigation.liveQueriesLinkText"
defaultMessage="Live queries"
/>
</EuiTab>
<EuiTab isSelected={section === Section.Packs} {...useRouterNavigate(Section.Packs)}>
<FormattedMessage
id="xpack.osquery.appNavigation.packsLinkText"
defaultMessage="Packs"
/>
</EuiTab>
<EuiTab
isSelected={section === Section.SavedQueries}
{...useRouterNavigate(Section.SavedQueries)}
>
<FormattedMessage
id="xpack.osquery.appNavigation.savedQueriesLinkText"
defaultMessage="Saved queries"
/>
</EuiTab>
</EuiTabs>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" direction="row">
<EuiFlexItem>
<EuiButtonEmpty
iconType="popout"
href="https://ela.st/osquery-feedback"
target="_blank"
>
<FormattedMessage
id="xpack.osquery.appNavigation.sendFeedbackButton"
defaultMessage="Send feedback"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<ManageIntegrationLink />
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</Nav>
);
};

View file

@ -30,7 +30,7 @@ const NavigationButtonsComponent: React.FC<NavigationButtonsProps> = ({
getUrlForApp(PLUGIN_ID, {
path: agentPolicyId
? `/live_queries/new?agentPolicyId=${agentPolicyId}`
: ' `/live_queries/new',
: '/live_queries/new',
}),
[agentPolicyId, getUrlForApp]
);
@ -42,7 +42,7 @@ const NavigationButtonsComponent: React.FC<NavigationButtonsProps> = ({
navigateToApp(PLUGIN_ID, {
path: agentPolicyId
? `/live_queries/new?agentPolicyId=${agentPolicyId}`
: ' `/live_queries/new',
: '/live_queries/new',
});
}
},

View file

@ -311,25 +311,26 @@ export const OsqueryManagedPolicyCreateImportExtension = React.memo<
/* From 0.6.0 we don't provide an input template, so we have to set it here */
if (satisfies(newPolicy?.package?.version, '>=0.6.0')) {
const updatedPolicy = produce(newPolicy, (draft) => {
if (!draft.inputs.length) {
if (editMode && policy?.inputs.length) {
set(draft, 'inputs', policy.inputs);
} else {
set(draft, 'inputs[0]', {
type: 'osquery',
enabled: true,
streams: [],
policy_template: 'osquery_manager',
});
} else {
if (!draft.inputs[0].type) {
set(draft, 'inputs[0].type', 'osquery');
}
if (!draft.inputs[0].policy_template) {
set(draft, 'inputs[0].policy_template', 'osquery_manager');
}
if (!draft.inputs[0].enabled) {
set(draft, 'inputs[0].enabled', true);
}
}
return draft;
});
if (updatedPolicy?.inputs[0].config) {
setFieldValue(
'config',
JSON.stringify(updatedPolicy?.inputs[0].config.osquery.value, null, 2)
);
}
onChange({
isValid: true,
updatedPolicy,

View file

@ -45,7 +45,7 @@ const LiveQueryComponent: React.FC<LiveQueryProps> = ({
formType,
enabled,
}) => {
const { data: hasActionResultsPrivileges, isFetched } = useActionResultsPrivileges();
const { data: hasActionResultsPrivileges, isLoading } = useActionResultsPrivileges();
const defaultValue = useMemo(() => {
if (agentId || agentPolicyIds?.length || query?.length) {
@ -70,7 +70,7 @@ const LiveQueryComponent: React.FC<LiveQueryProps> = ({
return undefined;
}, [agentId, agentIds, agentPolicyIds, ecs_mapping, query, savedQueryId]);
if (!isFetched) {
if (isLoading) {
return <EuiLoadingContent lines={10} />;
}

View file

@ -391,13 +391,13 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
toggleErrors,
expanded,
}) => {
const { data: lastResultsData, isFetched } = usePackQueryLastResults({
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
actionId,
interval,
logsDataView,
});
const { data: errorsData, isFetched: errorsFetched } = usePackQueryErrors({
const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({
actionId,
interval,
logsDataView,
@ -408,7 +408,7 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
[queryId, interval, toggleErrors]
);
if (!isFetched || !errorsFetched) {
if (isLoading || errorsLoading) {
return <EuiLoadingSpinner />;
}

View file

@ -13,17 +13,18 @@ import {
EuiBasicTableColumn,
EuiLink,
EuiToolTip,
EuiLoadingContent,
} from '@elastic/eui';
import moment from 'moment-timezone';
import React, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { PackagePolicy } from '../../../fleet/common';
import { useRouterNavigate } from '../common/lib/kibana';
import { usePacks } from './use_packs';
import { ActiveStateSwitch } from './active_state_switch';
import { AgentsPolicyLink } from '../agent_policies/agents_policy_link';
import { PackSavedObject } from './types';
const UpdatedBy = styled.span`
white-space: nowrap;
@ -82,7 +83,7 @@ export const AgentPoliciesPopover = ({ agentPolicyIds }: { agentPolicyIds: strin
};
const PacksTableComponent = () => {
const { data } = usePacks({});
const { data, isLoading } = usePacks({});
const renderAgentPolicy = useCallback(
(agentPolicyIds) => <AgentPoliciesPopover agentPolicyIds={agentPolicyIds} />,
@ -112,15 +113,14 @@ const PacksTableComponent = () => {
);
}, []);
// @ts-expect-error update types
const columns: Array<EuiBasicTableColumn<PackagePolicy>> = useMemo(
const columns: Array<EuiBasicTableColumn<PackSavedObject>> = useMemo(
() => [
{
field: 'attributes.name',
name: i18n.translate('xpack.osquery.packs.table.nameColumnTitle', {
defaultMessage: 'Name',
}),
sortable: true,
sortable: (item) => item.attributes.name.toLowerCase(),
render: renderName,
},
{
@ -178,8 +178,12 @@ const PacksTableComponent = () => {
[]
);
if (isLoading) {
return <EuiLoadingContent lines={10} />;
}
return (
<EuiInMemoryTable<PackagePolicy>
<EuiInMemoryTable<PackSavedObject>
// eslint-disable-next-line react-perf/jsx-no-new-array-as-prop
items={data?.saved_objects ?? []}
columns={columns}

View file

@ -763,6 +763,7 @@ export const ECSMappingEditorForm = forwardRef<ECSMappingEditorFormRef, ECSMappi
defaultMessage: 'Delete ECS mapping row',
}
)}
id={`${defaultValue?.key}-trash`}
iconType="trash"
color="danger"
onClick={handleDeleteClick}

View file

@ -29,7 +29,7 @@ export const createFormSchema = (ids: Set<string>) => ({
description: {
type: FIELD_TYPES.TEXT,
label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.descriptionFieldLabel', {
defaultMessage: 'Description',
defaultMessage: 'Description (optional)',
}),
validations: [],
},

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { SavedObject } from 'kibana/server';
export interface IQueryPayload {
attributes?: {
@ -11,3 +12,14 @@ export interface IQueryPayload {
id: string;
};
}
export type PackSavedObject = SavedObject<{
name: string;
description: string | undefined;
queries: Array<Record<string, any>>;
enabled: boolean | undefined;
created_at: string;
created_by: string | undefined;
updated_at: string;
updated_by: string | undefined;
}>;

View file

@ -1,37 +0,0 @@
/*
* 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.
*/
import {
PaginationInputPaginated,
FactoryQueryTypes,
StrategyResponseType,
Inspect,
} from '../../common/search_strategy';
export type InspectResponse = Inspect & { response: string[] };
export const generateTablePaginationOptions = (
activePage: number,
limit: number
): PaginationInputPaginated => {
const cursorStart = activePage * limit;
return {
activePage,
cursorStart,
fakePossibleCount: 4 <= activePage && activePage > 0 ? limit * (activePage + 2) : limit * 5,
querySize: limit,
};
};
export const getInspectResponse = <T extends FactoryQueryTypes>(
response: StrategyResponseType<T>,
prevResponse: InspectResponse
): InspectResponse => ({
dsl: response?.inspect?.dsl ?? prevResponse?.dsl ?? [],
response:
response != null ? [JSON.stringify(response.rawResponse, null, 2)] : prevResponse?.response,
});

View file

@ -105,7 +105,11 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
]);
const [columns, setColumns] = useState<EuiDataGridColumn[]>([]);
const { data: allResultsData, isFetched } = useAllResults({
const {
data: allResultsData,
isFetched,
isLoading,
} = useAllResults({
actionId,
activePage: pagination.pageIndex,
limit: pagination.pageSize,
@ -232,15 +236,11 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
);
useEffect(() => {
if (!allResultsData?.edges?.length) {
if (!allResultsData?.columns.length) {
return;
}
const fields = [
'agent.name',
...ecsMappingColumns.sort(),
...keys(allResultsData?.edges[0]?.fields || {}).sort(),
];
const fields = ['agent.name', ...ecsMappingColumns.sort(), ...allResultsData?.columns];
const newColumns = fields.reduce(
(acc, fieldName) => {
@ -277,12 +277,15 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
if (fieldName.startsWith('osquery.')) {
const displayAsText = fieldName.split('.')[1];
const hasNumberType = fields.includes(`${fieldName}.number`);
if (!seen.has(displayAsText)) {
const id = hasNumberType ? fieldName + '.number' : fieldName;
data.push({
id: fieldName,
id,
displayAsText,
display: getHeaderDisplay(displayAsText),
defaultSortDirection: Direction.asc,
...(hasNumberType ? { schema: 'numeric' } : {}),
});
seen.add(displayAsText);
}
@ -298,7 +301,8 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
!isEqual(map('id', currentColumns), map('id', newColumns)) ? newColumns : currentColumns
);
setVisibleColumns(map('id', newColumns));
}, [allResultsData?.edges, ecsMappingColumns, getHeaderDisplay]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [allResultsData?.columns.length, ecsMappingColumns, getHeaderDisplay]);
const toolbarVisibility = useMemo(
() => ({
@ -347,7 +351,7 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
);
}
if (!isFetched) {
if (isLoading) {
return <EuiLoadingContent lines={5} />;
}

View file

@ -8,7 +8,12 @@
import { useQuery } from 'react-query';
import { i18n } from '@kbn/i18n';
import { createFilter } from '../common/helpers';
import {
createFilter,
generateTablePaginationOptions,
getInspectResponse,
InspectResponse,
} from '../common/helpers';
import { useKibana } from '../common/lib/kibana';
import {
ResultEdges,
@ -20,7 +25,6 @@ import {
} from '../../common/search_strategy';
import { ESTermQuery } from '../../common/typed_json';
import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers';
import { useErrorToast } from '../common/hooks/use_error_toast';
export interface ResultsArgs {
@ -78,10 +82,12 @@ export const useAllResults = ({
return {
...responseData,
columns: Object.keys(responseData.edges[0].fields || {}).sort(),
inspect: getInspectResponse(responseData, {} as InspectResponse),
};
},
{
keepPreviousData: true,
refetchInterval: isLive ? 5000 : false,
enabled: !skip,
onSuccess: () => setErrorToast(),

View file

@ -13,6 +13,7 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiText,
EuiBasicTableColumn,
} from '@elastic/eui';
import React, { useCallback, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
@ -28,10 +29,12 @@ import { useSavedQueries } from '../../../saved_queries/use_saved_queries';
type SavedQuerySO = SavedObject<{
name: string;
id: string;
query: string;
ecs_mapping: ECSMapping;
updated_at: string;
}>;
interface PlayButtonProps {
disabled: boolean;
savedQuery: SavedQuerySO;
@ -141,14 +144,14 @@ const SavedQueriesPageComponent = () => {
return updatedAt ? `${moment(updatedAt).fromNow()}${updatedBy}` : '-';
}, []);
const columns = useMemo(
const columns: Array<EuiBasicTableColumn<SavedQuerySO>> = useMemo(
() => [
{
field: 'attributes.id',
name: i18n.translate('xpack.osquery.savedQueries.table.queryIdColumnTitle', {
defaultMessage: 'Query ID',
}),
sortable: true,
sortable: (item) => item.attributes.id.toLowerCase(),
truncateText: true,
},
{
@ -156,7 +159,6 @@ const SavedQueriesPageComponent = () => {
name: i18n.translate('xpack.osquery.savedQueries.table.descriptionColumnTitle', {
defaultMessage: 'Description',
}),
sortable: true,
truncateText: true,
},
{
@ -172,7 +174,7 @@ const SavedQueriesPageComponent = () => {
name: i18n.translate('xpack.osquery.savedQueries.table.updatedAtColumnTitle', {
defaultMessage: 'Last updated at',
}),
sortable: (item: SavedQuerySO) =>
sortable: (item) =>
item.attributes.updated_at ? Date.parse(item.attributes.updated_at) : 0,
truncateText: true,
render: renderUpdatedAt,

View file

@ -54,7 +54,7 @@ export const useSavedQueryForm = ({
try {
await handleSubmit({
...formData,
...(isEmpty(ecsFieldValue) ? {} : { ecs_mapping: ecsFieldValue }),
ecs_mapping: ecsFieldValue,
});
// eslint-disable-next-line no-empty
} catch (e) {}

View file

@ -30,7 +30,11 @@ interface OsqueryActionProps {
const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
const permissions = useKibana().services.application.capabilities.osquery;
const agentId = metadata?.info?.agent?.id ?? undefined;
const { data: agentData, isFetched: agentFetched } = useAgentDetails({
const {
data: agentData,
isFetched: agentFetched,
isLoading,
} = useAgentDetails({
agentId,
silent: true,
skip: !agentId,
@ -72,7 +76,7 @@ const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
);
}
if (!agentFetched) {
if (isLoading) {
return <EuiLoadingContent lines={10} />;
}

View file

@ -6,19 +6,7 @@
*/
import moment from 'moment-timezone';
import {
isEmpty,
set,
unset,
has,
difference,
filter,
find,
map,
mapKeys,
pickBy,
uniq,
} from 'lodash';
import { set, unset, has, difference, filter, find, map, mapKeys, uniq } from 'lodash';
import { schema } from '@kbn/config-schema';
import { produce } from 'immer';
import {
@ -126,16 +114,11 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte
request.params.id,
{
enabled,
...pickBy(
{
name,
description,
queries: queries && convertPackQueriesToSO(queries),
updated_at: moment().toISOString(),
updated_by: currentUser,
},
(value) => !isEmpty(value)
),
name,
description: description || '',
queries: queries && convertPackQueriesToSO(queries),
updated_at: moment().toISOString(),
updated_by: currentUser,
},
policy_ids
? {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { isEmpty, filter, pickBy } from 'lodash';
import { filter } from 'lodash';
import { schema } from '@kbn/config-schema';
import { PLUGIN_ID } from '../../../common';
@ -77,20 +77,17 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp
const updatedSavedQuerySO = await savedObjectsClient.update(
savedQuerySavedObjectType,
request.params.id,
pickBy(
{
id,
description,
platform,
query,
version,
interval,
ecs_mapping: convertECSMappingToArray(ecs_mapping),
updated_by: currentUser,
updated_at: new Date().toISOString(),
},
(value) => !isEmpty(value)
),
{
id,
description: description || '',
platform,
query,
version,
interval,
ecs_mapping: convertECSMappingToArray(ecs_mapping),
updated_by: currentUser,
updated_at: new Date().toISOString(),
},
{
refresh: 'wait_for',
}