mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Osquery] Fix multiple minor issues (#122023)
This commit is contained in:
parent
41806bab77
commit
4ee667be4f
46 changed files with 795 additions and 494 deletions
|
@ -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/."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"}
|
|
@ -9,12 +9,6 @@
|
|||
"value": {
|
||||
"field": "hours"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "message",
|
||||
"value": {
|
||||
"field": "seconds"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "Saved-Query-Id",
|
||||
|
|
|
@ -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}`);
|
||||
// });
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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/);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>) => {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
});
|
|
@ -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',
|
||||
});
|
||||
|
|
|
@ -5,4 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export type ActionAgentStatus = 'success' | 'pending' | 'failed';
|
||||
export enum ActionAgentStatus {
|
||||
SUCCESS = 'success',
|
||||
PENDING = 'pending',
|
||||
FAILED = 'failed',
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
});
|
|
@ -62,6 +62,7 @@ export const useActionDetails = ({ actionId, filterQuery, skip = false }: UseAct
|
|||
defaultMessage: 'Error while fetching action details',
|
||||
}),
|
||||
}),
|
||||
refetchOnWindowFocus: false,
|
||||
retryDelay: 1000,
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
76
x-pack/plugins/osquery/public/components/main_navigation.tsx
Normal file
76
x-pack/plugins/osquery/public/components/main_navigation.tsx
Normal 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>
|
||||
);
|
||||
};
|
|
@ -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',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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} />;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 />;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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: [],
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
}>;
|
||||
|
|
|
@ -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,
|
||||
});
|
|
@ -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} />;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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} />;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
? {
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue