mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -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": {
|
"value": {
|
||||||
"field": "hours"
|
"field": "hours"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "message",
|
|
||||||
"value": {
|
|
||||||
"field": "seconds"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "Saved-Query-Id",
|
"id": "Saved-Query-Id",
|
||||||
|
|
|
@ -9,14 +9,61 @@ import { FLEET_AGENT_POLICIES } from '../../tasks/navigation';
|
||||||
import { addIntegration } from '../../tasks/integrations';
|
import { addIntegration } from '../../tasks/integrations';
|
||||||
|
|
||||||
import { login } from '../../tasks/login';
|
import { login } from '../../tasks/login';
|
||||||
|
// import { findAndClickButton, findFormFieldByRowsLabelAndType } from '../../tasks/live_query';
|
||||||
|
import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver';
|
||||||
|
|
||||||
describe('Super User - Add Integration', () => {
|
describe('Super User - Add Integration', () => {
|
||||||
const integration = 'Osquery Manager';
|
const integration = 'Osquery Manager';
|
||||||
|
before(() => {
|
||||||
|
runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query');
|
||||||
|
});
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
login();
|
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.visit(FLEET_AGENT_POLICIES);
|
||||||
cy.contains('Default Fleet Server policy').click();
|
cy.contains('Default Fleet Server policy').click();
|
||||||
cy.contains('Add integration').click();
|
cy.contains('Add integration').click();
|
||||||
|
@ -24,4 +71,53 @@ describe('Super User - Add Integration', () => {
|
||||||
addIntegration();
|
addIntegration();
|
||||||
cy.contains('osquery_manager-');
|
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();
|
checkResults();
|
||||||
cy.react('EuiDataGridHeaderCellWrapper', {
|
cy.react('EuiDataGridHeaderCellWrapper', {
|
||||||
props: { id: 'osquery.days', index: 1 },
|
props: { id: 'osquery.days.number', index: 1 },
|
||||||
});
|
});
|
||||||
cy.react('EuiDataGridHeaderCellWrapper', {
|
cy.react('EuiDataGridHeaderCellWrapper', {
|
||||||
props: { id: 'osquery.hours', index: 2 },
|
props: { id: 'osquery.hours.number', index: 2 },
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.react('EuiAccordion', { props: { buttonContent: 'Advanced' } }).click();
|
cy.react('EuiAccordion', { props: { buttonContent: 'Advanced' } }).click();
|
||||||
|
@ -46,7 +46,7 @@ describe('Super User - Live Query', () => {
|
||||||
props: { id: 'message', index: 1 },
|
props: { id: 'message', index: 1 },
|
||||||
});
|
});
|
||||||
cy.react('EuiDataGridHeaderCellWrapper', {
|
cy.react('EuiDataGridHeaderCellWrapper', {
|
||||||
props: { id: 'osquery.days', index: 2 },
|
props: { id: 'osquery.days.number', index: 2 },
|
||||||
}).react('EuiIconIndexMapping');
|
}).react('EuiIconIndexMapping');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,12 +23,12 @@ describe('Super User - Metrics', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to run the query', () => {
|
it('should be able to run the query', () => {
|
||||||
cy.get('[data-test-subj="toggleNavButton"]').click();
|
cy.getBySel('toggleNavButton').click();
|
||||||
cy.contains('Metrics').click();
|
cy.contains('Metrics').click();
|
||||||
|
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
|
|
||||||
cy.get('[data-test-subj="nodeContainer"]').click();
|
cy.getBySel('nodeContainer').click();
|
||||||
cy.contains('Osquery').click();
|
cy.contains('Osquery').click();
|
||||||
inputQuery('select * from uptime;');
|
inputQuery('select * from uptime;');
|
||||||
|
|
||||||
|
@ -36,17 +36,17 @@ describe('Super User - Metrics', () => {
|
||||||
checkResults();
|
checkResults();
|
||||||
});
|
});
|
||||||
it('should be able to run the previously saved query', () => {
|
it('should be able to run the previously saved query', () => {
|
||||||
cy.get('[data-test-subj="toggleNavButton"]').click();
|
cy.getBySel('toggleNavButton').click();
|
||||||
cy.get('[data-test-subj="collapsibleNavAppLink"').contains('Metrics').click();
|
cy.getBySel('collapsibleNavAppLink').contains('Metrics').click();
|
||||||
|
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get('[data-test-subj="nodeContainer"]').click();
|
cy.getBySel('nodeContainer').click();
|
||||||
cy.contains('Osquery').click();
|
cy.contains('Osquery').click();
|
||||||
|
|
||||||
cy.get('[data-test-subj="comboBoxInput"]').first().click();
|
cy.getBySel('comboBoxInput').first().click();
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get('div[role=listBox]').should('have.lengthOf.above', 0);
|
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();
|
submitQuery();
|
||||||
checkResults();
|
checkResults();
|
||||||
|
|
|
@ -21,87 +21,150 @@ describe('SuperUser - Packs', () => {
|
||||||
const PACK_NAME = 'Pack-name';
|
const PACK_NAME = 'Pack-name';
|
||||||
const NEW_QUERY_NAME = 'new-query-name';
|
const NEW_QUERY_NAME = 'new-query-name';
|
||||||
|
|
||||||
before(() => {
|
describe('Create and edit a pack', () => {
|
||||||
runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query');
|
before(() => {
|
||||||
});
|
runKbnArchiverScript(ArchiverMethod.LOAD, 'saved_query');
|
||||||
beforeEach(() => {
|
});
|
||||||
login();
|
beforeEach(() => {
|
||||||
navigateTo('/app/osquery');
|
login();
|
||||||
});
|
navigateTo('/app/osquery');
|
||||||
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query');
|
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'saved_query');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a pack from a saved query', () => {
|
it('should add a pack from a saved query', () => {
|
||||||
cy.contains('Packs').click();
|
cy.contains('Packs').click();
|
||||||
findAndClickButton('Add pack');
|
findAndClickButton('Add pack');
|
||||||
findFormFieldByRowsLabelAndType('Name', PACK_NAME);
|
findFormFieldByRowsLabelAndType('Name', PACK_NAME);
|
||||||
findFormFieldByRowsLabelAndType('Description (optional)', 'Pack description');
|
findFormFieldByRowsLabelAndType('Description (optional)', 'Pack description');
|
||||||
findFormFieldByRowsLabelAndType(
|
findFormFieldByRowsLabelAndType(
|
||||||
'Scheduled agent policies (optional)',
|
'Scheduled agent policies (optional)',
|
||||||
'Default Fleet Server policy'
|
'Default Fleet Server policy'
|
||||||
);
|
);
|
||||||
cy.react('List').first().click();
|
cy.react('List').first().click();
|
||||||
findAndClickButton('Add query');
|
findAndClickButton('Add query');
|
||||||
cy.contains('Attach next query');
|
cy.contains('Attach next query');
|
||||||
cy.react('EuiComboBox', { props: { placeholder: 'Search for saved queries' } })
|
cy.react('EuiComboBox', { props: { placeholder: 'Search for saved queries' } })
|
||||||
.click()
|
.click()
|
||||||
.type(SAVED_QUERY_ID);
|
.type(SAVED_QUERY_ID);
|
||||||
cy.react('List').first().click();
|
cy.react('List').first().click();
|
||||||
cy.react('EuiFormRow', { props: { label: 'Interval (s)' } })
|
cy.react('EuiFormRow', { props: { label: 'Interval (s)' } })
|
||||||
.click()
|
.click()
|
||||||
.clear()
|
.clear()
|
||||||
.type('10');
|
.type('10');
|
||||||
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
|
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
|
||||||
cy.react('EuiTableRow').contains(SAVED_QUERY_ID);
|
cy.react('EuiTableRow').contains(SAVED_QUERY_ID);
|
||||||
findAndClickButton('Save pack');
|
findAndClickButton('Save pack');
|
||||||
cy.contains('Save and deploy changes');
|
cy.contains('Save and deploy changes');
|
||||||
findAndClickButton('Save and deploy changes');
|
findAndClickButton('Save and deploy changes');
|
||||||
cy.contains(PACK_NAME);
|
cy.contains(PACK_NAME);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('to click the edit button and edit pack', () => {
|
it('to click the edit button and edit pack', () => {
|
||||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||||
findAndClickButton('Edit');
|
findAndClickButton('Edit');
|
||||||
cy.contains(`Edit ${PACK_NAME}`);
|
cy.contains(`Edit ${PACK_NAME}`);
|
||||||
findAndClickButton('Add query');
|
findAndClickButton('Add query');
|
||||||
cy.contains('Attach next query');
|
cy.contains('Attach next query');
|
||||||
inputQuery('select * from uptime');
|
inputQuery('select * from uptime');
|
||||||
findFormFieldByRowsLabelAndType('ID', NEW_QUERY_NAME);
|
findFormFieldByRowsLabelAndType('ID', NEW_QUERY_NAME);
|
||||||
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
|
cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click();
|
||||||
cy.react('EuiTableRow').contains(NEW_QUERY_NAME);
|
cy.react('EuiTableRow').contains(NEW_QUERY_NAME);
|
||||||
findAndClickButton('Update pack');
|
findAndClickButton('Update pack');
|
||||||
cy.contains('Save and deploy changes');
|
cy.contains('Save and deploy changes');
|
||||||
findAndClickButton('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
|
describe.skip('Remove queries from pack', () => {
|
||||||
// it('to click the icon and visit discover', () => {
|
const TEST_PACK = 'Test-pack';
|
||||||
// preparePack(PACK_NAME, SAVED_QUERY_ID);
|
before(() => {
|
||||||
// cy.react('CustomItemAction', {
|
runKbnArchiverScript(ArchiverMethod.LOAD, 'hardware_monitoring');
|
||||||
// props: { index: 0, item: { id: SAVED_QUERY_ID } },
|
});
|
||||||
// }).click();
|
beforeEach(() => {
|
||||||
// cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"').click();
|
login();
|
||||||
// cy.get('[data-test-subj="superDatePickerToggleRefreshButton"').click();
|
navigateTo('/app/osquery');
|
||||||
// cy.get('[data-test-subj="superDatePickerRefreshIntervalInput"').clear().type('10');
|
});
|
||||||
// cy.get('button').contains('Apply').click();
|
after(() => {
|
||||||
// cy.get('[data-test-subj="discoverDocTable"]', { timeout: 60000 }).contains(
|
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'hardware_monitoring');
|
||||||
// `pack_${PACK_NAME}_${SAVED_QUERY_ID}`
|
});
|
||||||
// );
|
|
||||||
// });
|
it('should remove ALL queries', () => {
|
||||||
// it('by clicking in Lens button', () => {
|
preparePack(TEST_PACK, SAVED_QUERY_ID);
|
||||||
// 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');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,59 +26,93 @@ describe('Super User - Saved queries', () => {
|
||||||
navigateTo('/app/osquery');
|
navigateTo('/app/osquery');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should save the query', () => {
|
it(
|
||||||
cy.contains('New live query').click();
|
'should create a new query and verify: \n ' +
|
||||||
selectAllAgents();
|
'- hidden columns, full screen and sorting \n' +
|
||||||
inputQuery(DEFAULT_QUERY);
|
'- pagination \n' +
|
||||||
submitQuery();
|
'- query can viewed (status), edited and deleted ',
|
||||||
checkResults();
|
() => {
|
||||||
cy.contains('Save for later').click();
|
cy.contains('New live query').click();
|
||||||
cy.contains('Save query');
|
selectAllAgents();
|
||||||
findFormFieldByRowsLabelAndType('ID', SAVED_QUERY_ID);
|
inputQuery(DEFAULT_QUERY);
|
||||||
findFormFieldByRowsLabelAndType('Description', SAVED_QUERY_DESCRIPTION);
|
submitQuery();
|
||||||
cy.react('EuiButtonDisplay').contains('Save').click();
|
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.getBySel('dataGridFullScreenButton').trigger('mouseover');
|
||||||
cy.contains('New live query');
|
cy.contains(/Full screen$/).should('not.exist');
|
||||||
cy.react('ActionTableResultsButton').first().click();
|
cy.contains('Exit full screen').should('exist');
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display a previously saved query and run it', () => {
|
// hidden columns
|
||||||
cy.contains('Saved queries').click();
|
cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.cmdline' } }).click();
|
||||||
cy.contains(SAVED_QUERY_ID);
|
cy.contains(/Hide column$/).click();
|
||||||
cy.react('PlayButtonComponent', {
|
cy.react('EuiDataGridHeaderCellWrapper', {
|
||||||
props: { savedQuery: { attributes: { id: SAVED_QUERY_ID } } },
|
props: { id: 'osquery.disk_bytes_written.number' },
|
||||||
}).click();
|
}).click();
|
||||||
selectAllAgents();
|
cy.contains(/Hide column$/).click();
|
||||||
submitQuery();
|
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.getBySel('dataGridFullScreenButton').trigger('mouseover');
|
||||||
cy.contains('Saved queries').click();
|
cy.contains(/Full screen$/).should('not.exist');
|
||||||
cy.contains(SAVED_QUERY_ID);
|
cy.contains('Exit full screen').should('exist');
|
||||||
cy.react('CustomItemAction', {
|
cy.getBySel('dataGridFullScreenButton').click();
|
||||||
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`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should delete the saved query', () => {
|
// sorting
|
||||||
cy.contains('Saved queries').click();
|
cy.react('EuiDataGridHeaderCellWrapper', {
|
||||||
cy.contains(SAVED_QUERY_ID);
|
props: { id: 'osquery.egid' },
|
||||||
cy.react('CustomItemAction', {
|
}).click();
|
||||||
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
|
cy.contains(/Sort A-Z$/).click();
|
||||||
}).click();
|
cy.contains('2 columns hidden').should('exist');
|
||||||
deleteAndConfirm('query');
|
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
|
||||||
cy.contains(SAVED_QUERY_ID);
|
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 --
|
// -- This will overwrite an existing command --
|
||||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
// 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 = () => {
|
export const addIntegration = () => {
|
||||||
cy.getBySel(ADD_POLICY_BTN).click();
|
cy.getBySel(ADD_POLICY_BTN).click();
|
||||||
cy.getBySel(DATA_COLLECTION_SETUP_STEP).find('.euiLoadingSpinner').should('not.exist');
|
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();
|
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click();
|
||||||
// sometimes agent is assigned to default policy, sometimes not
|
// sometimes agent is assigned to default policy, sometimes not
|
||||||
closeModalIfVisible();
|
closeModalIfVisible();
|
||||||
|
|
||||||
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN, { timeout: 60000 }).should('not.exist');
|
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN, { timeout: 60000 }).should('not.exist');
|
||||||
};
|
};
|
||||||
|
|
||||||
function closeModalIfVisible() {
|
export function closeModalIfVisible() {
|
||||||
cy.get('body').then(($body) => {
|
cy.get('body').then(($body) => {
|
||||||
if ($body.find(CONFIRM_MODAL_BTN_SEL).length) {
|
if ($body.find(CONFIRM_MODAL_BTN_SEL).length) {
|
||||||
cy.getBySel(CONFIRM_MODAL_BTN).click();
|
cy.getBySel(CONFIRM_MODAL_BTN).click();
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import { LIVE_QUERY_EDITOR } from '../screens/live_query';
|
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 = () => {
|
export const selectAllAgents = () => {
|
||||||
cy.react('EuiComboBox', { props: { placeholder: 'Select agents or groups' } }).type('All agents');
|
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 submitQuery = () => cy.contains('Submit').click();
|
||||||
|
|
||||||
export const checkResults = () =>
|
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) =>
|
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) =>
|
export const typeInOsqueryFieldInput = (text: string) =>
|
||||||
cy.react('OsqueryColumnFieldComponent').first().react('ResultComboBox').click().type(text);
|
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 = 'app/fleet/';
|
||||||
export const FLEET_AGENT_POLICIES = 'app/fleet/policies';
|
export const FLEET_AGENT_POLICIES = 'app/fleet/policies';
|
||||||
export const OSQUERY = 'app/osquery';
|
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 NEW_LIVE_QUERY = 'app/osquery/live_queries/new';
|
||||||
export const OSQUERY_INTEGRATION_PAGE = '/app/fleet/integrations/osquery_manager/add-integration';
|
export const OSQUERY_INTEGRATION_PAGE = '/app/fleet/integrations/osquery_manager/add-integration';
|
||||||
export const navigateTo = (page: string, opts?: Partial<Cypress.VisitOptions>) => {
|
export const navigateTo = (page: string, opts?: Partial<Cypress.VisitOptions>) => {
|
||||||
|
|
|
@ -9,6 +9,4 @@ export const preparePack = (packName: string, savedQueryId: string) => {
|
||||||
cy.contains('Packs').click();
|
cy.contains('Packs').click();
|
||||||
const createdPack = cy.contains(packName);
|
const createdPack = cy.contains(packName);
|
||||||
createdPack.click();
|
createdPack.click();
|
||||||
cy.waitForReact(1000);
|
|
||||||
cy.react('EuiTableRow').contains(savedQueryId);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
getColorForAgentStatus,
|
getColorForAgentStatus,
|
||||||
getLabelForAgentStatus,
|
getLabelForAgentStatus,
|
||||||
} from './services/agent_status';
|
} from './services/agent_status';
|
||||||
import type { ActionAgentStatus } from './types';
|
import { ActionAgentStatus } from './types';
|
||||||
|
|
||||||
export const ActionAgentsStatusBadges = memo<{
|
export const ActionAgentsStatusBadges = memo<{
|
||||||
agentStatus: { [k in ActionAgentStatus]: number };
|
agentStatus: { [k in ActionAgentStatus]: number };
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { EuiColorPaletteDisplay } from '@elastic/eui';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { AGENT_STATUSES, getColorForAgentStatus } from './services/agent_status';
|
import { AGENT_STATUSES, getColorForAgentStatus } from './services/agent_status';
|
||||||
import type { ActionAgentStatus } from './types';
|
import { ActionAgentStatus } from './types';
|
||||||
|
|
||||||
const StyledEuiColorPaletteDisplay = styled(EuiColorPaletteDisplay)`
|
const StyledEuiColorPaletteDisplay = styled(EuiColorPaletteDisplay)`
|
||||||
&.osquery-action-agent-status-bar {
|
&.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 { euiPaletteColorBlindBehindText } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import type { ActionAgentStatus } from '../types';
|
import { ActionAgentStatus } from '../types';
|
||||||
|
|
||||||
const visColors = euiPaletteColorBlindBehindText();
|
const visColors = euiPaletteColorBlindBehindText();
|
||||||
const colorToHexMap = {
|
const colorToHexMap = {
|
||||||
|
@ -20,15 +20,19 @@ const colorToHexMap = {
|
||||||
danger: visColors[9],
|
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 {
|
export function getColorForAgentStatus(agentStatus: ActionAgentStatus): string {
|
||||||
switch (agentStatus) {
|
switch (agentStatus) {
|
||||||
case 'success':
|
case ActionAgentStatus.SUCCESS:
|
||||||
return colorToHexMap.success;
|
return colorToHexMap.success;
|
||||||
case 'pending':
|
case ActionAgentStatus.PENDING:
|
||||||
return colorToHexMap.default;
|
return colorToHexMap.default;
|
||||||
case 'failed':
|
case ActionAgentStatus.FAILED:
|
||||||
return colorToHexMap.danger;
|
return colorToHexMap.danger;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported action agent status ${agentStatus}`);
|
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 {
|
export function getLabelForAgentStatus(agentStatus: ActionAgentStatus, expired: boolean): string {
|
||||||
switch (agentStatus) {
|
switch (agentStatus) {
|
||||||
case 'success':
|
case ActionAgentStatus.SUCCESS:
|
||||||
return i18n.translate('xpack.osquery.liveQueryActionResults.summary.successfulLabelText', {
|
return i18n.translate('xpack.osquery.liveQueryActionResults.summary.successfulLabelText', {
|
||||||
defaultMessage: 'Successful',
|
defaultMessage: 'Successful',
|
||||||
});
|
});
|
||||||
case 'pending':
|
case ActionAgentStatus.PENDING:
|
||||||
return expired
|
return expired
|
||||||
? i18n.translate('xpack.osquery.liveQueryActionResults.summary.expiredLabelText', {
|
? i18n.translate('xpack.osquery.liveQueryActionResults.summary.expiredLabelText', {
|
||||||
defaultMessage: 'Expired',
|
defaultMessage: 'Expired',
|
||||||
|
@ -49,7 +53,7 @@ export function getLabelForAgentStatus(agentStatus: ActionAgentStatus, expired:
|
||||||
: i18n.translate('xpack.osquery.liveQueryActionResults.summary.pendingLabelText', {
|
: i18n.translate('xpack.osquery.liveQueryActionResults.summary.pendingLabelText', {
|
||||||
defaultMessage: 'Not yet responded',
|
defaultMessage: 'Not yet responded',
|
||||||
});
|
});
|
||||||
case 'failed':
|
case ActionAgentStatus.FAILED:
|
||||||
return i18n.translate('xpack.osquery.liveQueryActionResults.summary.failedLabelText', {
|
return i18n.translate('xpack.osquery.liveQueryActionResults.summary.failedLabelText', {
|
||||||
defaultMessage: 'Failed',
|
defaultMessage: 'Failed',
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,4 +5,8 @@
|
||||||
* 2.0.
|
* 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 { useQuery } from 'react-query';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
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 { useKibana } from '../common/lib/kibana';
|
||||||
import {
|
import {
|
||||||
ResultEdges,
|
ResultEdges,
|
||||||
|
@ -22,7 +27,6 @@ import {
|
||||||
import { ESTermQuery } from '../../common/typed_json';
|
import { ESTermQuery } from '../../common/typed_json';
|
||||||
import { queryClient } from '../query_client';
|
import { queryClient } from '../query_client';
|
||||||
|
|
||||||
import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers';
|
|
||||||
import { useErrorToast } from '../common/hooks/use_error_toast';
|
import { useErrorToast } from '../common/hooks/use_error_toast';
|
||||||
|
|
||||||
export interface ResultsArgs {
|
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',
|
defaultMessage: 'Error while fetching action details',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
retryDelay: 1000,
|
retryDelay: 1000,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,12 @@
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
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 { useKibana } from '../common/lib/kibana';
|
||||||
import {
|
import {
|
||||||
ActionEdges,
|
ActionEdges,
|
||||||
|
@ -20,7 +25,6 @@ import {
|
||||||
} from '../../common/search_strategy';
|
} from '../../common/search_strategy';
|
||||||
import { ESTermQuery } from '../../common/typed_json';
|
import { ESTermQuery } from '../../common/typed_json';
|
||||||
|
|
||||||
import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers';
|
|
||||||
import { useErrorToast } from '../common/hooks/use_error_toast';
|
import { useErrorToast } from '../common/hooks/use_error_toast';
|
||||||
|
|
||||||
export interface ActionsArgs {
|
export interface ActionsArgs {
|
||||||
|
|
|
@ -84,6 +84,22 @@ const AgentsTableComponent: React.FC<AgentsTableProps> = ({ agentSelection, onCh
|
||||||
const defaultValueInitialized = useRef(false);
|
const defaultValueInitialized = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
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 && !defaultValueInitialized.current && options.length) {
|
||||||
if (agentSelection.allAgentsSelected) {
|
if (agentSelection.allAgentsSelected) {
|
||||||
const allAgentsOptions = find(['label', ALL_AGENTS_LABEL], options);
|
const allAgentsOptions = find(['label', ALL_AGENTS_LABEL], options);
|
||||||
|
@ -95,35 +111,11 @@ const AgentsTableComponent: React.FC<AgentsTableProps> = ({ agentSelection, onCh
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agentSelection.policiesSelected.length) {
|
if (agentSelection.policiesSelected.length) {
|
||||||
const policyOptions = find(['label', AGENT_POLICY_LABEL], options);
|
handleSelectedOptions(agentSelection.policiesSelected, AGENT_POLICY_LABEL);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agentSelection.agents.length) {
|
if (agentSelection.agents.length) {
|
||||||
const agentOptions = find(['label', AGENT_SELECTION_LABEL], options);
|
handleSelectedOptions(agentSelection.agents, AGENT_SELECTION_LABEL);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [agentSelection, options, selectedOptions]);
|
}, [agentSelection, options, selectedOptions]);
|
||||||
|
|
|
@ -7,12 +7,6 @@
|
||||||
|
|
||||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||||
import { euiPaletteColorBlindBehindText } from '@elastic/eui';
|
import { euiPaletteColorBlindBehindText } from '@elastic/eui';
|
||||||
import {
|
|
||||||
PaginationInputPaginated,
|
|
||||||
FactoryQueryTypes,
|
|
||||||
StrategyResponseType,
|
|
||||||
Inspect,
|
|
||||||
} from '../../common/search_strategy';
|
|
||||||
import {
|
import {
|
||||||
AGENT_GROUP_KEY,
|
AGENT_GROUP_KEY,
|
||||||
SelectedGroups,
|
SelectedGroups,
|
||||||
|
@ -25,8 +19,6 @@ import {
|
||||||
GroupOption,
|
GroupOption,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export type InspectResponse = Inspect & { response: string[] };
|
|
||||||
|
|
||||||
export const getNumOverlapped = (
|
export const getNumOverlapped = (
|
||||||
{ policy = {}, platform = {} }: SelectedGroups,
|
{ policy = {}, platform = {} }: SelectedGroups,
|
||||||
overlap: Overlap
|
overlap: Overlap
|
||||||
|
@ -158,26 +150,3 @@ export const generateAgentSelection = (selection: GroupOption[]) => {
|
||||||
}
|
}
|
||||||
return { newAgentSelection, selectedGroups, selectedAgents };
|
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,
|
AgentsStrategyResponse,
|
||||||
} from '../../common/search_strategy';
|
} from '../../common/search_strategy';
|
||||||
|
|
||||||
import { generateTablePaginationOptions, processAggregations } from './helpers';
|
import { processAggregations } from './helpers';
|
||||||
|
import { generateTablePaginationOptions } from '../common/helpers';
|
||||||
import { Overlap, Group } from './types';
|
import { Overlap, Group } from './types';
|
||||||
import { useErrorToast } from '../common/hooks/use_error_toast';
|
import { useErrorToast } from '../common/hooks/use_error_toast';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,38 @@
|
||||||
|
|
||||||
import { isString } from 'lodash/fp';
|
import { isString } from 'lodash/fp';
|
||||||
|
|
||||||
|
import {
|
||||||
|
PaginationInputPaginated,
|
||||||
|
FactoryQueryTypes,
|
||||||
|
StrategyResponseType,
|
||||||
|
Inspect,
|
||||||
|
} from '../../common/search_strategy';
|
||||||
|
|
||||||
import { ESQuery } from '../../common/typed_json';
|
import { ESQuery } from '../../common/typed_json';
|
||||||
|
|
||||||
export const createFilter = (filterQuery: ESQuery | string | undefined) =>
|
export const createFilter = (filterQuery: ESQuery | string | undefined) =>
|
||||||
isString(filterQuery) ? filterQuery : JSON.stringify(filterQuery);
|
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 = {}) {
|
export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) {
|
||||||
const { chrome, http } = useKibana().services;
|
const { chrome, http, application } = useKibana().services;
|
||||||
|
|
||||||
const breadcrumbs: ChromeBreadcrumb[] =
|
const breadcrumbs: ChromeBreadcrumb[] =
|
||||||
breadcrumbGetters[page]?.(values).map((breadcrumb) => ({
|
breadcrumbGetters[page]?.(values).map((breadcrumb) => {
|
||||||
...breadcrumb,
|
const href = breadcrumb.href
|
||||||
href: breadcrumb.href ? http.basePath.prepend(`${BASE_PATH}${breadcrumb.href}`) : undefined,
|
? 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]
|
const docTitle: string[] = [...breadcrumbs]
|
||||||
.reverse()
|
.reverse()
|
||||||
.map((breadcrumb) => breadcrumb.text as string);
|
.map((breadcrumb) => breadcrumb.text as string);
|
||||||
|
|
|
@ -5,33 +5,16 @@
|
||||||
* 2.0.
|
* 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 { Container, Wrapper } from './layouts';
|
||||||
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 { OsqueryAppRoutes } from '../routes';
|
import { OsqueryAppRoutes } from '../routes';
|
||||||
import { useRouterNavigate } from '../common/lib/kibana';
|
|
||||||
import { ManageIntegrationLink } from './manage_integration_link';
|
|
||||||
import { useOsqueryIntegrationStatus } from '../common/hooks';
|
import { useOsqueryIntegrationStatus } from '../common/hooks';
|
||||||
import { OsqueryAppEmptyState } from './empty_state';
|
import { OsqueryAppEmptyState } from './empty_state';
|
||||||
|
import { MainNavigation } from './main_navigation';
|
||||||
|
|
||||||
const OsqueryAppComponent = () => {
|
const OsqueryAppComponent = () => {
|
||||||
const location = useLocation();
|
|
||||||
const section = useMemo(() => location.pathname.split('/')[1] ?? 'overview', [location.pathname]);
|
|
||||||
const { data: osqueryIntegration, isFetched } = useOsqueryIntegrationStatus();
|
const { data: osqueryIntegration, isFetched } = useOsqueryIntegrationStatus();
|
||||||
|
|
||||||
if (!isFetched) {
|
if (!isFetched) {
|
||||||
|
@ -59,55 +42,7 @@ const OsqueryAppComponent = () => {
|
||||||
return (
|
return (
|
||||||
<Container id="osquery-app">
|
<Container id="osquery-app">
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Nav>
|
<MainNavigation />
|
||||||
<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>
|
|
||||||
<OsqueryAppRoutes />
|
<OsqueryAppRoutes />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</Container>
|
</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, {
|
getUrlForApp(PLUGIN_ID, {
|
||||||
path: agentPolicyId
|
path: agentPolicyId
|
||||||
? `/live_queries/new?agentPolicyId=${agentPolicyId}`
|
? `/live_queries/new?agentPolicyId=${agentPolicyId}`
|
||||||
: ' `/live_queries/new',
|
: '/live_queries/new',
|
||||||
}),
|
}),
|
||||||
[agentPolicyId, getUrlForApp]
|
[agentPolicyId, getUrlForApp]
|
||||||
);
|
);
|
||||||
|
@ -42,7 +42,7 @@ const NavigationButtonsComponent: React.FC<NavigationButtonsProps> = ({
|
||||||
navigateToApp(PLUGIN_ID, {
|
navigateToApp(PLUGIN_ID, {
|
||||||
path: agentPolicyId
|
path: agentPolicyId
|
||||||
? `/live_queries/new?agentPolicyId=${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 */
|
/* 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')) {
|
if (satisfies(newPolicy?.package?.version, '>=0.6.0')) {
|
||||||
const updatedPolicy = produce(newPolicy, (draft) => {
|
const updatedPolicy = produce(newPolicy, (draft) => {
|
||||||
if (!draft.inputs.length) {
|
if (editMode && policy?.inputs.length) {
|
||||||
|
set(draft, 'inputs', policy.inputs);
|
||||||
|
} else {
|
||||||
set(draft, 'inputs[0]', {
|
set(draft, 'inputs[0]', {
|
||||||
type: 'osquery',
|
type: 'osquery',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
streams: [],
|
streams: [],
|
||||||
policy_template: 'osquery_manager',
|
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({
|
onChange({
|
||||||
isValid: true,
|
isValid: true,
|
||||||
updatedPolicy,
|
updatedPolicy,
|
||||||
|
|
|
@ -45,7 +45,7 @@ const LiveQueryComponent: React.FC<LiveQueryProps> = ({
|
||||||
formType,
|
formType,
|
||||||
enabled,
|
enabled,
|
||||||
}) => {
|
}) => {
|
||||||
const { data: hasActionResultsPrivileges, isFetched } = useActionResultsPrivileges();
|
const { data: hasActionResultsPrivileges, isLoading } = useActionResultsPrivileges();
|
||||||
|
|
||||||
const defaultValue = useMemo(() => {
|
const defaultValue = useMemo(() => {
|
||||||
if (agentId || agentPolicyIds?.length || query?.length) {
|
if (agentId || agentPolicyIds?.length || query?.length) {
|
||||||
|
@ -70,7 +70,7 @@ const LiveQueryComponent: React.FC<LiveQueryProps> = ({
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [agentId, agentIds, agentPolicyIds, ecs_mapping, query, savedQueryId]);
|
}, [agentId, agentIds, agentPolicyIds, ecs_mapping, query, savedQueryId]);
|
||||||
|
|
||||||
if (!isFetched) {
|
if (isLoading) {
|
||||||
return <EuiLoadingContent lines={10} />;
|
return <EuiLoadingContent lines={10} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -391,13 +391,13 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
|
||||||
toggleErrors,
|
toggleErrors,
|
||||||
expanded,
|
expanded,
|
||||||
}) => {
|
}) => {
|
||||||
const { data: lastResultsData, isFetched } = usePackQueryLastResults({
|
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
|
||||||
actionId,
|
actionId,
|
||||||
interval,
|
interval,
|
||||||
logsDataView,
|
logsDataView,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: errorsData, isFetched: errorsFetched } = usePackQueryErrors({
|
const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({
|
||||||
actionId,
|
actionId,
|
||||||
interval,
|
interval,
|
||||||
logsDataView,
|
logsDataView,
|
||||||
|
@ -408,7 +408,7 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
|
||||||
[queryId, interval, toggleErrors]
|
[queryId, interval, toggleErrors]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isFetched || !errorsFetched) {
|
if (isLoading || errorsLoading) {
|
||||||
return <EuiLoadingSpinner />;
|
return <EuiLoadingSpinner />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,17 +13,18 @@ import {
|
||||||
EuiBasicTableColumn,
|
EuiBasicTableColumn,
|
||||||
EuiLink,
|
EuiLink,
|
||||||
EuiToolTip,
|
EuiToolTip,
|
||||||
|
EuiLoadingContent,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { PackagePolicy } from '../../../fleet/common';
|
|
||||||
import { useRouterNavigate } from '../common/lib/kibana';
|
import { useRouterNavigate } from '../common/lib/kibana';
|
||||||
import { usePacks } from './use_packs';
|
import { usePacks } from './use_packs';
|
||||||
import { ActiveStateSwitch } from './active_state_switch';
|
import { ActiveStateSwitch } from './active_state_switch';
|
||||||
import { AgentsPolicyLink } from '../agent_policies/agents_policy_link';
|
import { AgentsPolicyLink } from '../agent_policies/agents_policy_link';
|
||||||
|
import { PackSavedObject } from './types';
|
||||||
|
|
||||||
const UpdatedBy = styled.span`
|
const UpdatedBy = styled.span`
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -82,7 +83,7 @@ export const AgentPoliciesPopover = ({ agentPolicyIds }: { agentPolicyIds: strin
|
||||||
};
|
};
|
||||||
|
|
||||||
const PacksTableComponent = () => {
|
const PacksTableComponent = () => {
|
||||||
const { data } = usePacks({});
|
const { data, isLoading } = usePacks({});
|
||||||
|
|
||||||
const renderAgentPolicy = useCallback(
|
const renderAgentPolicy = useCallback(
|
||||||
(agentPolicyIds) => <AgentPoliciesPopover agentPolicyIds={agentPolicyIds} />,
|
(agentPolicyIds) => <AgentPoliciesPopover agentPolicyIds={agentPolicyIds} />,
|
||||||
|
@ -112,15 +113,14 @@ const PacksTableComponent = () => {
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// @ts-expect-error update types
|
const columns: Array<EuiBasicTableColumn<PackSavedObject>> = useMemo(
|
||||||
const columns: Array<EuiBasicTableColumn<PackagePolicy>> = useMemo(
|
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
field: 'attributes.name',
|
field: 'attributes.name',
|
||||||
name: i18n.translate('xpack.osquery.packs.table.nameColumnTitle', {
|
name: i18n.translate('xpack.osquery.packs.table.nameColumnTitle', {
|
||||||
defaultMessage: 'Name',
|
defaultMessage: 'Name',
|
||||||
}),
|
}),
|
||||||
sortable: true,
|
sortable: (item) => item.attributes.name.toLowerCase(),
|
||||||
render: renderName,
|
render: renderName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -178,8 +178,12 @@ const PacksTableComponent = () => {
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <EuiLoadingContent lines={10} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiInMemoryTable<PackagePolicy>
|
<EuiInMemoryTable<PackSavedObject>
|
||||||
// eslint-disable-next-line react-perf/jsx-no-new-array-as-prop
|
// eslint-disable-next-line react-perf/jsx-no-new-array-as-prop
|
||||||
items={data?.saved_objects ?? []}
|
items={data?.saved_objects ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
|
@ -763,6 +763,7 @@ export const ECSMappingEditorForm = forwardRef<ECSMappingEditorFormRef, ECSMappi
|
||||||
defaultMessage: 'Delete ECS mapping row',
|
defaultMessage: 'Delete ECS mapping row',
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
id={`${defaultValue?.key}-trash`}
|
||||||
iconType="trash"
|
iconType="trash"
|
||||||
color="danger"
|
color="danger"
|
||||||
onClick={handleDeleteClick}
|
onClick={handleDeleteClick}
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const createFormSchema = (ids: Set<string>) => ({
|
||||||
description: {
|
description: {
|
||||||
type: FIELD_TYPES.TEXT,
|
type: FIELD_TYPES.TEXT,
|
||||||
label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.descriptionFieldLabel', {
|
label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.descriptionFieldLabel', {
|
||||||
defaultMessage: 'Description',
|
defaultMessage: 'Description (optional)',
|
||||||
}),
|
}),
|
||||||
validations: [],
|
validations: [],
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
import { SavedObject } from 'kibana/server';
|
||||||
|
|
||||||
export interface IQueryPayload {
|
export interface IQueryPayload {
|
||||||
attributes?: {
|
attributes?: {
|
||||||
|
@ -11,3 +12,14 @@ export interface IQueryPayload {
|
||||||
id: string;
|
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 [columns, setColumns] = useState<EuiDataGridColumn[]>([]);
|
||||||
|
|
||||||
const { data: allResultsData, isFetched } = useAllResults({
|
const {
|
||||||
|
data: allResultsData,
|
||||||
|
isFetched,
|
||||||
|
isLoading,
|
||||||
|
} = useAllResults({
|
||||||
actionId,
|
actionId,
|
||||||
activePage: pagination.pageIndex,
|
activePage: pagination.pageIndex,
|
||||||
limit: pagination.pageSize,
|
limit: pagination.pageSize,
|
||||||
|
@ -232,15 +236,11 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!allResultsData?.edges?.length) {
|
if (!allResultsData?.columns.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = [
|
const fields = ['agent.name', ...ecsMappingColumns.sort(), ...allResultsData?.columns];
|
||||||
'agent.name',
|
|
||||||
...ecsMappingColumns.sort(),
|
|
||||||
...keys(allResultsData?.edges[0]?.fields || {}).sort(),
|
|
||||||
];
|
|
||||||
|
|
||||||
const newColumns = fields.reduce(
|
const newColumns = fields.reduce(
|
||||||
(acc, fieldName) => {
|
(acc, fieldName) => {
|
||||||
|
@ -277,12 +277,15 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
||||||
|
|
||||||
if (fieldName.startsWith('osquery.')) {
|
if (fieldName.startsWith('osquery.')) {
|
||||||
const displayAsText = fieldName.split('.')[1];
|
const displayAsText = fieldName.split('.')[1];
|
||||||
|
const hasNumberType = fields.includes(`${fieldName}.number`);
|
||||||
if (!seen.has(displayAsText)) {
|
if (!seen.has(displayAsText)) {
|
||||||
|
const id = hasNumberType ? fieldName + '.number' : fieldName;
|
||||||
data.push({
|
data.push({
|
||||||
id: fieldName,
|
id,
|
||||||
displayAsText,
|
displayAsText,
|
||||||
display: getHeaderDisplay(displayAsText),
|
display: getHeaderDisplay(displayAsText),
|
||||||
defaultSortDirection: Direction.asc,
|
defaultSortDirection: Direction.asc,
|
||||||
|
...(hasNumberType ? { schema: 'numeric' } : {}),
|
||||||
});
|
});
|
||||||
seen.add(displayAsText);
|
seen.add(displayAsText);
|
||||||
}
|
}
|
||||||
|
@ -298,7 +301,8 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
||||||
!isEqual(map('id', currentColumns), map('id', newColumns)) ? newColumns : currentColumns
|
!isEqual(map('id', currentColumns), map('id', newColumns)) ? newColumns : currentColumns
|
||||||
);
|
);
|
||||||
setVisibleColumns(map('id', newColumns));
|
setVisibleColumns(map('id', newColumns));
|
||||||
}, [allResultsData?.edges, ecsMappingColumns, getHeaderDisplay]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [allResultsData?.columns.length, ecsMappingColumns, getHeaderDisplay]);
|
||||||
|
|
||||||
const toolbarVisibility = useMemo(
|
const toolbarVisibility = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -347,7 +351,7 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isFetched) {
|
if (isLoading) {
|
||||||
return <EuiLoadingContent lines={5} />;
|
return <EuiLoadingContent lines={5} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,12 @@
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
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 { useKibana } from '../common/lib/kibana';
|
||||||
import {
|
import {
|
||||||
ResultEdges,
|
ResultEdges,
|
||||||
|
@ -20,7 +25,6 @@ import {
|
||||||
} from '../../common/search_strategy';
|
} from '../../common/search_strategy';
|
||||||
import { ESTermQuery } from '../../common/typed_json';
|
import { ESTermQuery } from '../../common/typed_json';
|
||||||
|
|
||||||
import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers';
|
|
||||||
import { useErrorToast } from '../common/hooks/use_error_toast';
|
import { useErrorToast } from '../common/hooks/use_error_toast';
|
||||||
|
|
||||||
export interface ResultsArgs {
|
export interface ResultsArgs {
|
||||||
|
@ -78,10 +82,12 @@ export const useAllResults = ({
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...responseData,
|
...responseData,
|
||||||
|
columns: Object.keys(responseData.edges[0].fields || {}).sort(),
|
||||||
inspect: getInspectResponse(responseData, {} as InspectResponse),
|
inspect: getInspectResponse(responseData, {} as InspectResponse),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
keepPreviousData: true,
|
||||||
refetchInterval: isLive ? 5000 : false,
|
refetchInterval: isLive ? 5000 : false,
|
||||||
enabled: !skip,
|
enabled: !skip,
|
||||||
onSuccess: () => setErrorToast(),
|
onSuccess: () => setErrorToast(),
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
EuiFlexGroup,
|
EuiFlexGroup,
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
EuiText,
|
EuiText,
|
||||||
|
EuiBasicTableColumn,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
@ -28,10 +29,12 @@ import { useSavedQueries } from '../../../saved_queries/use_saved_queries';
|
||||||
|
|
||||||
type SavedQuerySO = SavedObject<{
|
type SavedQuerySO = SavedObject<{
|
||||||
name: string;
|
name: string;
|
||||||
|
id: string;
|
||||||
query: string;
|
query: string;
|
||||||
ecs_mapping: ECSMapping;
|
ecs_mapping: ECSMapping;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
interface PlayButtonProps {
|
interface PlayButtonProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
savedQuery: SavedQuerySO;
|
savedQuery: SavedQuerySO;
|
||||||
|
@ -141,14 +144,14 @@ const SavedQueriesPageComponent = () => {
|
||||||
return updatedAt ? `${moment(updatedAt).fromNow()}${updatedBy}` : '-';
|
return updatedAt ? `${moment(updatedAt).fromNow()}${updatedBy}` : '-';
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns: Array<EuiBasicTableColumn<SavedQuerySO>> = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
field: 'attributes.id',
|
field: 'attributes.id',
|
||||||
name: i18n.translate('xpack.osquery.savedQueries.table.queryIdColumnTitle', {
|
name: i18n.translate('xpack.osquery.savedQueries.table.queryIdColumnTitle', {
|
||||||
defaultMessage: 'Query ID',
|
defaultMessage: 'Query ID',
|
||||||
}),
|
}),
|
||||||
sortable: true,
|
sortable: (item) => item.attributes.id.toLowerCase(),
|
||||||
truncateText: true,
|
truncateText: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -156,7 +159,6 @@ const SavedQueriesPageComponent = () => {
|
||||||
name: i18n.translate('xpack.osquery.savedQueries.table.descriptionColumnTitle', {
|
name: i18n.translate('xpack.osquery.savedQueries.table.descriptionColumnTitle', {
|
||||||
defaultMessage: 'Description',
|
defaultMessage: 'Description',
|
||||||
}),
|
}),
|
||||||
sortable: true,
|
|
||||||
truncateText: true,
|
truncateText: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -172,7 +174,7 @@ const SavedQueriesPageComponent = () => {
|
||||||
name: i18n.translate('xpack.osquery.savedQueries.table.updatedAtColumnTitle', {
|
name: i18n.translate('xpack.osquery.savedQueries.table.updatedAtColumnTitle', {
|
||||||
defaultMessage: 'Last updated at',
|
defaultMessage: 'Last updated at',
|
||||||
}),
|
}),
|
||||||
sortable: (item: SavedQuerySO) =>
|
sortable: (item) =>
|
||||||
item.attributes.updated_at ? Date.parse(item.attributes.updated_at) : 0,
|
item.attributes.updated_at ? Date.parse(item.attributes.updated_at) : 0,
|
||||||
truncateText: true,
|
truncateText: true,
|
||||||
render: renderUpdatedAt,
|
render: renderUpdatedAt,
|
||||||
|
|
|
@ -54,7 +54,7 @@ export const useSavedQueryForm = ({
|
||||||
try {
|
try {
|
||||||
await handleSubmit({
|
await handleSubmit({
|
||||||
...formData,
|
...formData,
|
||||||
...(isEmpty(ecsFieldValue) ? {} : { ecs_mapping: ecsFieldValue }),
|
ecs_mapping: ecsFieldValue,
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line no-empty
|
// eslint-disable-next-line no-empty
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
|
@ -30,7 +30,11 @@ interface OsqueryActionProps {
|
||||||
const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
||||||
const permissions = useKibana().services.application.capabilities.osquery;
|
const permissions = useKibana().services.application.capabilities.osquery;
|
||||||
const agentId = metadata?.info?.agent?.id ?? undefined;
|
const agentId = metadata?.info?.agent?.id ?? undefined;
|
||||||
const { data: agentData, isFetched: agentFetched } = useAgentDetails({
|
const {
|
||||||
|
data: agentData,
|
||||||
|
isFetched: agentFetched,
|
||||||
|
isLoading,
|
||||||
|
} = useAgentDetails({
|
||||||
agentId,
|
agentId,
|
||||||
silent: true,
|
silent: true,
|
||||||
skip: !agentId,
|
skip: !agentId,
|
||||||
|
@ -72,7 +76,7 @@ const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!agentFetched) {
|
if (isLoading) {
|
||||||
return <EuiLoadingContent lines={10} />;
|
return <EuiLoadingContent lines={10} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,19 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import {
|
import { set, unset, has, difference, filter, find, map, mapKeys, uniq } from 'lodash';
|
||||||
isEmpty,
|
|
||||||
set,
|
|
||||||
unset,
|
|
||||||
has,
|
|
||||||
difference,
|
|
||||||
filter,
|
|
||||||
find,
|
|
||||||
map,
|
|
||||||
mapKeys,
|
|
||||||
pickBy,
|
|
||||||
uniq,
|
|
||||||
} from 'lodash';
|
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
import { produce } from 'immer';
|
import { produce } from 'immer';
|
||||||
import {
|
import {
|
||||||
|
@ -126,16 +114,11 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte
|
||||||
request.params.id,
|
request.params.id,
|
||||||
{
|
{
|
||||||
enabled,
|
enabled,
|
||||||
...pickBy(
|
name,
|
||||||
{
|
description: description || '',
|
||||||
name,
|
queries: queries && convertPackQueriesToSO(queries),
|
||||||
description,
|
updated_at: moment().toISOString(),
|
||||||
queries: queries && convertPackQueriesToSO(queries),
|
updated_by: currentUser,
|
||||||
updated_at: moment().toISOString(),
|
|
||||||
updated_by: currentUser,
|
|
||||||
},
|
|
||||||
(value) => !isEmpty(value)
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
policy_ids
|
policy_ids
|
||||||
? {
|
? {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { isEmpty, filter, pickBy } from 'lodash';
|
import { filter } from 'lodash';
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
|
|
||||||
import { PLUGIN_ID } from '../../../common';
|
import { PLUGIN_ID } from '../../../common';
|
||||||
|
@ -77,20 +77,17 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp
|
||||||
const updatedSavedQuerySO = await savedObjectsClient.update(
|
const updatedSavedQuerySO = await savedObjectsClient.update(
|
||||||
savedQuerySavedObjectType,
|
savedQuerySavedObjectType,
|
||||||
request.params.id,
|
request.params.id,
|
||||||
pickBy(
|
{
|
||||||
{
|
id,
|
||||||
id,
|
description: description || '',
|
||||||
description,
|
platform,
|
||||||
platform,
|
query,
|
||||||
query,
|
version,
|
||||||
version,
|
interval,
|
||||||
interval,
|
ecs_mapping: convertECSMappingToArray(ecs_mapping),
|
||||||
ecs_mapping: convertECSMappingToArray(ecs_mapping),
|
updated_by: currentUser,
|
||||||
updated_by: currentUser,
|
updated_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
},
|
||||||
},
|
|
||||||
(value) => !isEmpty(value)
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
refresh: 'wait_for',
|
refresh: 'wait_for',
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue