mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Osquery] Add live query to alerts (#128142)
[Osquery] Add osquery to alerts and timeline
This commit is contained in:
parent
829517bb64
commit
fb71a2d66e
31 changed files with 673 additions and 108 deletions
|
@ -48,7 +48,7 @@ const TabComponent = (props: TabProps) => {
|
|||
|
||||
return (
|
||||
<TabContent>
|
||||
<OsqueryAction metadata={metadata} />
|
||||
<OsqueryAction agentId={metadata?.info?.agent?.id} />
|
||||
</TabContent>
|
||||
);
|
||||
}, [OsqueryAction, loading, metadata]);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"attributes": {
|
||||
"created_at": "2022-01-28T09:01:46.147Z",
|
||||
"created_by": "elastic",
|
||||
"description": "gfd",
|
||||
"enabled": true,
|
||||
"name": "testpack",
|
||||
"queries": [
|
||||
{
|
||||
"id": "fds",
|
||||
"interval": 10,
|
||||
"query": "select * from uptime;"
|
||||
}
|
||||
],
|
||||
"updated_at": "2022-01-28T09:01:46.147Z",
|
||||
"updated_by": "elastic"
|
||||
},
|
||||
"coreMigrationVersion": "8.1.0",
|
||||
"id": "eb92a730-8018-11ec-88ce-bd5b5e3a7526",
|
||||
"references": [],
|
||||
"sort": [
|
||||
1643360506152,
|
||||
9062
|
||||
],
|
||||
"type": "osquery-pack",
|
||||
"updated_at": "2022-01-28T09:01:46.152Z",
|
||||
"version": "WzgzOTksMV0="
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"id": "c8ca6100-802e-11ec-952d-cf6018da8e2b",
|
||||
"type": "alert",
|
||||
"namespaces": [
|
||||
"default"
|
||||
],
|
||||
"updated_at": "2022-01-28T11:38:23.009Z",
|
||||
"version": "WzE5MjksMV0=",
|
||||
"attributes": {
|
||||
"name": "Test-rule",
|
||||
"tags": [
|
||||
"__internal_rule_id:22308402-5e0e-421b-8d22-a47ddc4b0188",
|
||||
"__internal_immutable:false"
|
||||
],
|
||||
"alertTypeId": "siem.queryRule",
|
||||
"consumer": "siem",
|
||||
"params": {
|
||||
"author": [],
|
||||
"description": "asd",
|
||||
"ruleId": "22308402-5e0e-421b-8d22-a47ddc4b0188",
|
||||
"falsePositives": [],
|
||||
"from": "now-360s",
|
||||
"immutable": false,
|
||||
"license": "",
|
||||
"outputIndex": ".siem-signals-default",
|
||||
"meta": {
|
||||
"from": "1m",
|
||||
"kibana_siem_app_url": "http://localhost:5601/app/security"
|
||||
},
|
||||
"maxSignals": 100,
|
||||
"riskScore": 21,
|
||||
"riskScoreMapping": [],
|
||||
"severity": "low",
|
||||
"severityMapping": [],
|
||||
"threat": [],
|
||||
"to": "now",
|
||||
"references": [],
|
||||
"version": 1,
|
||||
"exceptionsList": [],
|
||||
"type": "query",
|
||||
"language": "kuery",
|
||||
"index": [
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*"
|
||||
],
|
||||
"query": "_id:*",
|
||||
"filters": []
|
||||
},
|
||||
"schedule": {
|
||||
"interval": "5m"
|
||||
},
|
||||
"enabled": true,
|
||||
"actions": [],
|
||||
"throttle": null,
|
||||
"notifyWhen": "onActiveAlert",
|
||||
"apiKeyOwner": "elastic",
|
||||
"legacyId": null,
|
||||
"createdBy": "elastic",
|
||||
"updatedBy": "elastic",
|
||||
"createdAt": "2022-01-28T11:38:17.540Z",
|
||||
"updatedAt": "2022-01-28T11:38:19.894Z",
|
||||
"muteAll": true,
|
||||
"mutedInstanceIds": [],
|
||||
"executionStatus": {
|
||||
"status": "ok",
|
||||
"lastExecutionDate": "2022-01-28T11:38:21.638Z",
|
||||
"error": null,
|
||||
"lastDuration": 1369
|
||||
},
|
||||
"monitoring": {
|
||||
"execution": {
|
||||
"history": [
|
||||
{
|
||||
"success": true,
|
||||
"timestamp": 1643369903007
|
||||
}
|
||||
],
|
||||
"calculated_metrics": {
|
||||
"success_ratio": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"versionApiKeyLastmodified": "8.1.0"
|
||||
},
|
||||
"scheduledTaskId": "c8ca6100-802e-11ec-952d-cf6018da8e2b"
|
||||
},
|
||||
"references": [],
|
||||
"migrationVersion": {
|
||||
"alert": "8.0.0"
|
||||
},
|
||||
"coreMigrationVersion": "8.1.0"
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver';
|
||||
import { login } from '../../tasks/login';
|
||||
import {
|
||||
checkResults,
|
||||
findAndClickButton,
|
||||
findFormFieldByRowsLabelAndType,
|
||||
inputQuery,
|
||||
submitQuery,
|
||||
} from '../../tasks/live_query';
|
||||
import { preparePack } from '../../tasks/packs';
|
||||
import { closeModalIfVisible } from '../../tasks/integrations';
|
||||
import { navigateTo } from '../../tasks/navigation';
|
||||
|
||||
describe('Alert Event Details', () => {
|
||||
before(() => {
|
||||
runKbnArchiverScript(ArchiverMethod.LOAD, 'pack');
|
||||
runKbnArchiverScript(ArchiverMethod.LOAD, 'rule');
|
||||
});
|
||||
beforeEach(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'pack');
|
||||
runKbnArchiverScript(ArchiverMethod.UNLOAD, 'rule');
|
||||
});
|
||||
|
||||
it('should be able to run live query', () => {
|
||||
const PACK_NAME = 'testpack';
|
||||
const RULE_NAME = 'Test-rule';
|
||||
navigateTo('/app/osquery/packs');
|
||||
preparePack(PACK_NAME);
|
||||
findAndClickButton('Edit');
|
||||
cy.contains(`Edit ${PACK_NAME}`);
|
||||
findFormFieldByRowsLabelAndType(
|
||||
'Scheduled agent policies (optional)',
|
||||
'fleet server {downArrow}{enter}'
|
||||
);
|
||||
findAndClickButton('Update pack');
|
||||
closeModalIfVisible();
|
||||
cy.contains(PACK_NAME);
|
||||
cy.visit('/app/security/rules');
|
||||
cy.contains(RULE_NAME).click();
|
||||
cy.wait(2000);
|
||||
cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'true');
|
||||
cy.getBySel('ruleSwitch').click();
|
||||
cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'false');
|
||||
cy.getBySel('ruleSwitch').click();
|
||||
cy.getBySel('ruleSwitch').should('have.attr', 'aria-checked', 'true');
|
||||
cy.visit('/app/security/alerts');
|
||||
cy.getBySel('expand-event').first().click();
|
||||
cy.getBySel('take-action-dropdown-btn').click();
|
||||
cy.getBySel('osquery-action-item').click();
|
||||
inputQuery('select * from uptime;');
|
||||
submitQuery();
|
||||
checkResults();
|
||||
});
|
||||
});
|
|
@ -45,7 +45,6 @@ describe('Super User - Metrics', () => {
|
|||
|
||||
cy.getBySel('comboBoxInput').first().click();
|
||||
cy.wait(500);
|
||||
cy.get('div[role=listBox]').should('have.lengthOf.above', 0);
|
||||
cy.getBySel('comboBoxInput').first().type('{downArrow}{enter}');
|
||||
|
||||
submitQuery();
|
||||
|
|
|
@ -71,7 +71,7 @@ describe('SuperUser - Packs', () => {
|
|||
});
|
||||
|
||||
it('to click the edit button and edit pack', () => {
|
||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||
preparePack(PACK_NAME);
|
||||
findAndClickButton('Edit');
|
||||
cy.contains(`Edit ${PACK_NAME}`);
|
||||
findAndClickButton('Add query');
|
||||
|
@ -89,7 +89,7 @@ describe('SuperUser - Packs', () => {
|
|||
});
|
||||
|
||||
it('should trigger validation when saved query is being chosen', () => {
|
||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||
preparePack(PACK_NAME);
|
||||
findAndClickButton('Edit');
|
||||
findAndClickButton('Add query');
|
||||
cy.contains('Attach next query');
|
||||
|
@ -103,7 +103,7 @@ describe('SuperUser - Packs', () => {
|
|||
});
|
||||
// THIS TESTS TAKES TOO LONG FOR NOW - LET ME THINK IT THROUGH
|
||||
it.skip('to click the icon and visit discover', () => {
|
||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||
preparePack(PACK_NAME);
|
||||
cy.react('CustomItemAction', {
|
||||
props: { index: 0, item: { id: SAVED_QUERY_ID } },
|
||||
}).click();
|
||||
|
@ -124,7 +124,7 @@ describe('SuperUser - Packs', () => {
|
|||
lensUrl = url;
|
||||
});
|
||||
});
|
||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||
preparePack(PACK_NAME);
|
||||
cy.react('CustomItemAction', {
|
||||
props: { index: 1, item: { id: SAVED_QUERY_ID } },
|
||||
}).click();
|
||||
|
@ -154,7 +154,7 @@ describe('SuperUser - Packs', () => {
|
|||
});
|
||||
|
||||
it('delete all queries in the pack', () => {
|
||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||
preparePack(PACK_NAME);
|
||||
cy.contains(/^Edit$/).click();
|
||||
|
||||
cy.getBySel('checkboxSelectAll').click();
|
||||
|
@ -170,7 +170,7 @@ describe('SuperUser - Packs', () => {
|
|||
});
|
||||
|
||||
it('enable changing saved queries and ecs_mappings', () => {
|
||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||
preparePack(PACK_NAME);
|
||||
cy.contains(/^Edit$/).click();
|
||||
|
||||
findAndClickButton('Add query');
|
||||
|
@ -210,7 +210,7 @@ describe('SuperUser - Packs', () => {
|
|||
});
|
||||
|
||||
it('to click delete button', () => {
|
||||
preparePack(PACK_NAME, SAVED_QUERY_ID);
|
||||
preparePack(PACK_NAME);
|
||||
findAndClickButton('Edit');
|
||||
deleteAndConfirm('pack');
|
||||
});
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import { navigateTo } from '../../tasks/navigation';
|
||||
import { RESULTS_TABLE_BUTTON } from '../../screens/live_query';
|
||||
import {
|
||||
checkResults,
|
||||
DEFAULT_QUERY,
|
||||
BIG_QUERY,
|
||||
deleteAndConfirm,
|
||||
findFormFieldByRowsLabelAndType,
|
||||
inputQuery,
|
||||
|
@ -34,18 +35,18 @@ describe('Super User - Saved queries', () => {
|
|||
() => {
|
||||
cy.contains('New live query').click();
|
||||
selectAllAgents();
|
||||
inputQuery(DEFAULT_QUERY);
|
||||
inputQuery(BIG_QUERY);
|
||||
submitQuery();
|
||||
checkResults();
|
||||
// enter fullscreen
|
||||
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
|
||||
cy.contains(/Full screen$/).should('exist');
|
||||
cy.contains('Exit full screen').should('not.exist');
|
||||
cy.getBySel('dataGridFullScreenButton').click();
|
||||
cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover');
|
||||
cy.contains(/Enter fullscreen$/).should('exist');
|
||||
cy.contains('Exit fullscreen').should('not.exist');
|
||||
cy.getBySel(RESULTS_TABLE_BUTTON).click();
|
||||
|
||||
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
|
||||
cy.contains(/Full screen$/).should('not.exist');
|
||||
cy.contains('Exit full screen').should('exist');
|
||||
cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover');
|
||||
cy.contains(/Enter Fullscreen$/).should('not.exist');
|
||||
cy.contains('Exit fullscreen').should('exist');
|
||||
|
||||
// hidden columns
|
||||
cy.react('EuiDataGridHeaderCellWrapper', { props: { id: 'osquery.cmdline' } }).click();
|
||||
|
@ -59,10 +60,10 @@ describe('Super User - Saved queries', () => {
|
|||
cy.getBySel('pagination-button-next').click().wait(500).click();
|
||||
cy.contains('2 columns hidden').should('exist');
|
||||
|
||||
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
|
||||
cy.contains(/Full screen$/).should('not.exist');
|
||||
cy.contains('Exit full screen').should('exist');
|
||||
cy.getBySel('dataGridFullScreenButton').click();
|
||||
cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover');
|
||||
cy.contains(/Enter fullscreen$/).should('not.exist');
|
||||
cy.contains('Exit fullscreen').should('exist');
|
||||
cy.getBySel(RESULTS_TABLE_BUTTON).click();
|
||||
|
||||
// sorting
|
||||
cy.react('EuiDataGridHeaderCellWrapper', {
|
||||
|
@ -70,8 +71,8 @@ describe('Super User - Saved queries', () => {
|
|||
}).click();
|
||||
cy.contains(/Sort A-Z$/).click();
|
||||
cy.contains('2 columns hidden').should('exist');
|
||||
cy.getBySel('dataGridFullScreenButton').trigger('mouseover');
|
||||
cy.contains(/Full screen$/).should('exist');
|
||||
cy.getBySel(RESULTS_TABLE_BUTTON).trigger('mouseover');
|
||||
cy.contains(/Enter fullscreen$/).should('exist');
|
||||
|
||||
// save new query
|
||||
cy.contains('Exit full screen').should('not.exist');
|
||||
|
@ -111,8 +112,8 @@ describe('Super User - Saved queries', () => {
|
|||
props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } },
|
||||
}).click();
|
||||
deleteAndConfirm('query');
|
||||
cy.contains(SAVED_QUERY_ID);
|
||||
cy.contains(/^No items found/);
|
||||
cy.contains(SAVED_QUERY_ID).should('exist');
|
||||
cy.contains(SAVED_QUERY_ID).should('not.exist');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -9,3 +9,4 @@ export const AGENT_FIELD = '[data-test-subj="comboBoxInput"]';
|
|||
export const ALL_AGENTS_OPTION = '[title="All agents"]';
|
||||
export const LIVE_QUERY_EDITOR = '#osquery_editor';
|
||||
export const SUBMIT_BUTTON = '#submit-button';
|
||||
export const RESULTS_TABLE_BUTTON = 'dataGridFullScreenButton';
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
export const addIntegration = (agentPolicy = 'Default Fleet Server policy') => {
|
||||
cy.getBySel(ADD_POLICY_BTN).click();
|
||||
cy.getBySel(DATA_COLLECTION_SETUP_STEP).find('.euiLoadingSpinner').should('not.exist');
|
||||
cy.getBySel('agentPolicySelect').select(agentPolicy);
|
||||
cy.getBySel('agentPolicySelect').should('have.text', agentPolicy);
|
||||
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click();
|
||||
// sometimes agent is assigned to default policy, sometimes not
|
||||
closeModalIfVisible();
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
|
||||
import { LIVE_QUERY_EDITOR } from '../screens/live_query';
|
||||
|
||||
export const DEFAULT_QUERY = 'select * from processes, users;';
|
||||
export const DEFAULT_QUERY = 'select * from processes;';
|
||||
export const BIG_QUERY = 'select * from processes, users;';
|
||||
|
||||
export const selectAllAgents = () => {
|
||||
cy.react('EuiComboBox', { props: { placeholder: 'Select agents or groups' } }).type('All agents');
|
||||
cy.react('EuiFilterSelectItem').contains('All agents').should('exist');
|
||||
cy.react('EuiComboBox', { props: { placeholder: 'Select agents or groups' } }).type(
|
||||
'{downArrow}{enter}'
|
||||
'{downArrow}{enter}{esc}'
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const preparePack = (packName: string, savedQueryId: string) => {
|
||||
export const preparePack = (packName: string) => {
|
||||
cy.contains('Packs').click();
|
||||
const createdPack = cy.contains(packName);
|
||||
createdPack.click();
|
||||
|
|
|
@ -16,7 +16,7 @@ export interface IQueryPayload {
|
|||
export type PackSavedObject = SavedObject<{
|
||||
name: string;
|
||||
description: string | undefined;
|
||||
queries: Array<Record<string, any>>;
|
||||
queries: Array<Record<string, unknown>>;
|
||||
enabled: boolean | undefined;
|
||||
created_at: string;
|
||||
created_by: string | undefined;
|
||||
|
|
|
@ -26,7 +26,7 @@ import {
|
|||
LazyOsqueryManagedPolicyEditExtension,
|
||||
LazyOsqueryManagedCustomButtonExtension,
|
||||
} from './fleet_integration';
|
||||
import { getLazyOsqueryAction } from './shared_components';
|
||||
import { getLazyOsqueryAction, useIsOsqueryAvailableSimple } from './shared_components';
|
||||
|
||||
export class OsqueryPlugin implements Plugin<OsqueryPluginSetup, OsqueryPluginStart> {
|
||||
private kibanaVersion: string;
|
||||
|
@ -95,6 +95,7 @@ export class OsqueryPlugin implements Plugin<OsqueryPluginSetup, OsqueryPluginSt
|
|||
storage: this.storage,
|
||||
kibanaVersion: this.kibanaVersion,
|
||||
}),
|
||||
isOsqueryAvailable: useIsOsqueryAvailableSimple,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
*/
|
||||
|
||||
export { getLazyOsqueryAction } from './lazy_osquery_action';
|
||||
export { useIsOsqueryAvailableSimple } from './osquery_action/use_is_osquery_available_simple';
|
||||
|
|
|
@ -5,66 +5,68 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { find } from 'lodash';
|
||||
import { EuiErrorBoundary, EuiLoadingContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui';
|
||||
import React, { useMemo } from 'react';
|
||||
import { QueryClientProvider } from 'react-query';
|
||||
import { OSQUERY_INTEGRATION_NAME } from '../../../common';
|
||||
import { useAgentDetails } from '../../agents/use_agent_details';
|
||||
import { useAgentPolicy } from '../../agent_policies';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { KibanaContextProvider, useKibana } from '../../common/lib/kibana';
|
||||
|
||||
import { LiveQuery } from '../../live_queries';
|
||||
import { queryClient } from '../../query_client';
|
||||
import { OsqueryIcon } from '../../components/osquery_icon';
|
||||
import { KibanaThemeProvider } from '../../shared_imports';
|
||||
import { useIsOsqueryAvailable } from './use_is_osquery_available';
|
||||
|
||||
interface OsqueryActionProps {
|
||||
metadata?: {
|
||||
info: {
|
||||
agent: { id: string };
|
||||
};
|
||||
};
|
||||
agentId?: string;
|
||||
formType: 'steps' | 'simple';
|
||||
}
|
||||
|
||||
const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
||||
const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ agentId, formType = 'simple' }) => {
|
||||
const permissions = useKibana().services.application.capabilities.osquery;
|
||||
const agentId = metadata?.info?.agent?.id ?? undefined;
|
||||
const {
|
||||
data: agentData,
|
||||
isFetched: agentFetched,
|
||||
isLoading,
|
||||
} = useAgentDetails({
|
||||
agentId,
|
||||
silent: true,
|
||||
skip: !agentId,
|
||||
});
|
||||
const {
|
||||
data: agentPolicyData,
|
||||
isFetched: policyFetched,
|
||||
isError: policyError,
|
||||
isLoading: policyLoading,
|
||||
} = useAgentPolicy({
|
||||
policyId: agentData?.policy_id,
|
||||
skip: !agentData,
|
||||
silent: true,
|
||||
});
|
||||
|
||||
const osqueryAvailable = useMemo(() => {
|
||||
if (policyError) return false;
|
||||
const emptyPrompt = useMemo(
|
||||
() => (
|
||||
<EuiEmptyPrompt
|
||||
icon={<OsqueryIcon />}
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('xpack.osquery.action.shortEmptyTitle', {
|
||||
defaultMessage: 'Osquery is not available',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
titleSize="xs"
|
||||
body={
|
||||
<p>
|
||||
{i18n.translate('xpack.osquery.action.empty', {
|
||||
defaultMessage:
|
||||
'An Elastic Agent is not installed on this host. To run queries, install Elastic Agent on the host, and then add the Osquery Manager integration to the agent policy in Fleet.',
|
||||
})}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
);
|
||||
const { osqueryAvailable, agentFetched, isLoading, policyFetched, policyLoading, agentData } =
|
||||
useIsOsqueryAvailable(agentId);
|
||||
|
||||
const osqueryPackageInstalled = find(agentPolicyData?.package_policies, [
|
||||
'package.name',
|
||||
OSQUERY_INTEGRATION_NAME,
|
||||
]);
|
||||
return osqueryPackageInstalled?.enabled;
|
||||
}, [agentPolicyData?.package_policies, policyError]);
|
||||
if (!agentId || (agentFetched && !agentData)) {
|
||||
return emptyPrompt;
|
||||
}
|
||||
|
||||
if (!(permissions.runSavedQueries || permissions.writeLiveQueries)) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
icon={<OsqueryIcon />}
|
||||
title={<h2>Permissions denied</h2>}
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('xpack.osquery.action.permissionDenied', {
|
||||
defaultMessage: 'Permission denied',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
titleSize="xs"
|
||||
body={
|
||||
<p>
|
||||
|
@ -80,22 +82,6 @@ const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
|||
return <EuiLoadingContent lines={10} />;
|
||||
}
|
||||
|
||||
if (!agentId || (agentFetched && !agentData)) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
icon={<OsqueryIcon />}
|
||||
title={<h2>Osquery is not available</h2>}
|
||||
titleSize="xs"
|
||||
body={
|
||||
<p>
|
||||
An Elastic Agent is not installed on this host. To run queries, install Elastic Agent on
|
||||
the host, and then add the Osquery Manager integration to the agent policy in Fleet.
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!policyFetched && policyLoading) {
|
||||
return <EuiLoadingContent lines={10} />;
|
||||
}
|
||||
|
@ -104,12 +90,20 @@ const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
|||
return (
|
||||
<EuiEmptyPrompt
|
||||
icon={<OsqueryIcon />}
|
||||
title={<h2>Osquery is not available</h2>}
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('xpack.osquery.action.shortEmptyTitle', {
|
||||
defaultMessage: 'Osquery is not available',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
titleSize="xs"
|
||||
body={
|
||||
<p>
|
||||
The Osquery Manager integration is not added to the agent policy. To run queries on the
|
||||
host, add the Osquery Manager integration to the agent policy in Fleet.
|
||||
{i18n.translate('xpack.osquery.action.unavailable', {
|
||||
defaultMessage:
|
||||
'The Osquery Manager integration is not added to the agent policy. To run queries on the host, add the Osquery Manager integration to the agent policy in Fleet.',
|
||||
})}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
|
@ -120,30 +114,38 @@ const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
|||
return (
|
||||
<EuiEmptyPrompt
|
||||
icon={<OsqueryIcon />}
|
||||
title={<h2>Osquery is not available</h2>}
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('xpack.osquery.action.shortEmptyTitle', {
|
||||
defaultMessage: 'Osquery is not available',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
titleSize="xs"
|
||||
body={
|
||||
<p>
|
||||
To run queries on this host, the Elastic Agent must be active. Check the status of this
|
||||
agent in Fleet.
|
||||
{i18n.translate('xpack.osquery.action.agentStatus', {
|
||||
defaultMessage:
|
||||
'To run queries on this host, the Elastic Agent must be active. Check the status of this agent in Fleet.',
|
||||
})}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <LiveQuery formType="simple" agentId={agentId} />;
|
||||
return <LiveQuery formType={formType} agentId={agentId} />;
|
||||
};
|
||||
|
||||
export const OsqueryAction = React.memo(OsqueryActionComponent);
|
||||
const OsqueryAction = React.memo(OsqueryActionComponent);
|
||||
|
||||
// @ts-expect-error update types
|
||||
const OsqueryActionWrapperComponent = ({ services, ...props }) => (
|
||||
const OsqueryActionWrapperComponent = ({ services, agentId, formType }) => (
|
||||
<KibanaThemeProvider theme$={services.theme.theme$}>
|
||||
<KibanaContextProvider services={services}>
|
||||
<EuiErrorBoundary>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<OsqueryAction {...props} />
|
||||
<OsqueryAction agentId={agentId} formType={formType} />
|
||||
</QueryClientProvider>
|
||||
</EuiErrorBoundary>
|
||||
</KibanaContextProvider>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 { useMemo } from 'react';
|
||||
import { find } from 'lodash';
|
||||
import { useAgentDetails } from '../../agents/use_agent_details';
|
||||
import { useAgentPolicy } from '../../agent_policies';
|
||||
import { OSQUERY_INTEGRATION_NAME } from '../../../common';
|
||||
|
||||
export const useIsOsqueryAvailable = (agentId?: string) => {
|
||||
const {
|
||||
data: agentData,
|
||||
isFetched: agentFetched,
|
||||
isLoading,
|
||||
} = useAgentDetails({
|
||||
agentId,
|
||||
silent: true,
|
||||
skip: !agentId,
|
||||
});
|
||||
const {
|
||||
data: agentPolicyData,
|
||||
isFetched: policyFetched,
|
||||
isError: policyError,
|
||||
isLoading: policyLoading,
|
||||
} = useAgentPolicy({
|
||||
policyId: agentData?.policy_id,
|
||||
skip: !agentData,
|
||||
silent: true,
|
||||
});
|
||||
|
||||
const osqueryAvailable = useMemo(() => {
|
||||
if (policyError) return false;
|
||||
|
||||
const osqueryPackageInstalled = find(agentPolicyData?.package_policies, [
|
||||
'package.name',
|
||||
OSQUERY_INTEGRATION_NAME,
|
||||
]);
|
||||
return osqueryPackageInstalled?.enabled;
|
||||
}, [agentPolicyData?.package_policies, policyError]);
|
||||
|
||||
return { osqueryAvailable, agentFetched, isLoading, policyFetched, policyLoading, agentData };
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 { useKibana } from '../../common/lib/kibana';
|
||||
import { useIsOsqueryAvailableSimple } from './use_is_osquery_available_simple';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { createStartServicesMock } from '../../../../triggers_actions_ui/public/common/lib/kibana/kibana_react.mock';
|
||||
import { OSQUERY_INTEGRATION_NAME } from '../../../common';
|
||||
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
|
||||
|
||||
jest.mock('../../common/lib/kibana');
|
||||
|
||||
const response = {
|
||||
item: {
|
||||
policy_id: '4234234234',
|
||||
package_policies: [
|
||||
{
|
||||
package: { name: OSQUERY_INTEGRATION_NAME },
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
describe('UseIsOsqueryAvailableSimple', () => {
|
||||
const mockedHttp = httpServiceMock.createStartContract();
|
||||
mockedHttp.get.mockResolvedValue(response);
|
||||
beforeAll(() => {
|
||||
(useKibana as jest.Mock).mockImplementation(() => {
|
||||
const mockStartServicesMock = createStartServicesMock();
|
||||
|
||||
return {
|
||||
services: {
|
||||
...mockStartServicesMock,
|
||||
http: mockedHttp,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
it('should expect response from API and return enabled flag', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
useIsOsqueryAvailableSimple({
|
||||
agentId: '3242332',
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current).toBe(false);
|
||||
await waitForValueToChange(() => result.current);
|
||||
|
||||
expect(result.current).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 { useEffect, useState } from 'react';
|
||||
|
||||
import { find } from 'lodash';
|
||||
import { useKibana } from '../../common/lib/kibana';
|
||||
import { OSQUERY_INTEGRATION_NAME } from '../../../common';
|
||||
import { AgentPolicy, FleetServerAgent, NewPackagePolicy } from '../../../../fleet/common';
|
||||
|
||||
interface IProps {
|
||||
agentId: string;
|
||||
}
|
||||
|
||||
export const useIsOsqueryAvailableSimple = ({ agentId }: IProps) => {
|
||||
const { http } = useKibana().services;
|
||||
const [isAvailable, setIsAvailable] = useState(false);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const { item: agentInfo }: { item: FleetServerAgent } = await http.get(
|
||||
`/internal/osquery/fleet_wrapper/agents/${agentId}`
|
||||
);
|
||||
const { item: packageInfo }: { item: AgentPolicy } = await http.get(
|
||||
`/internal/osquery/fleet_wrapper/agent_policies/${agentInfo.policy_id}/`
|
||||
);
|
||||
const osqueryPackageInstalled = find(packageInfo?.package_policies, [
|
||||
'package.name',
|
||||
OSQUERY_INTEGRATION_NAME,
|
||||
]) as NewPackagePolicy;
|
||||
setIsAvailable(osqueryPackageInstalled.enabled);
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
})();
|
||||
}, [agentId, http]);
|
||||
|
||||
return isAvailable;
|
||||
};
|
|
@ -22,6 +22,7 @@ import { getLazyOsqueryAction } from './shared_components';
|
|||
export interface OsqueryPluginSetup {}
|
||||
export interface OsqueryPluginStart {
|
||||
OsqueryAction?: ReturnType<typeof getLazyOsqueryAction>;
|
||||
isOsqueryAvailable: (props: { agentId: string }) => boolean;
|
||||
}
|
||||
|
||||
export interface AppPluginStartDependencies {
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
"lists",
|
||||
"home",
|
||||
"telemetry",
|
||||
"dataViewFieldEditor"
|
||||
"dataViewFieldEditor",
|
||||
"osquery"
|
||||
],
|
||||
"server": true,
|
||||
"ui": true,
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { EuiContextMenuItem } from '@elastic/eui';
|
||||
import { ACTION_OSQUERY } from './translations';
|
||||
|
||||
interface IProps {
|
||||
handleClick: () => void;
|
||||
}
|
||||
|
||||
export const OsqueryActionItem = ({ handleClick }: IProps) => {
|
||||
return (
|
||||
<EuiContextMenuItem
|
||||
key="osquery-action-item"
|
||||
data-test-subj="osquery-action-item"
|
||||
onClick={handleClick}
|
||||
>
|
||||
{ACTION_OSQUERY}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { EuiFlyout, EuiFlyoutFooter, EuiFlyoutBody, EuiFlyoutHeader } from '@elastic/eui';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { OsqueryEventDetailsFooter } from './osquery_flyout_footer';
|
||||
import { OsqueryEventDetailsHeader } from './osquery_flyout_header';
|
||||
import { ACTION_OSQUERY } from './translations';
|
||||
|
||||
const OsqueryActionWrapper = styled.div`
|
||||
padding: 8px;
|
||||
`;
|
||||
|
||||
export interface OsqueryFlyoutProps {
|
||||
agentId: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const OsqueryFlyout: React.FC<OsqueryFlyoutProps> = ({ agentId, onClose }) => {
|
||||
const {
|
||||
services: { osquery },
|
||||
} = useKibana();
|
||||
|
||||
// @ts-expect-error
|
||||
const { OsqueryAction } = osquery;
|
||||
return (
|
||||
<EuiFlyout
|
||||
ownFocus
|
||||
maskProps={{ style: 'z-index: 5000' }} // For an edge case to display above the timeline flyout
|
||||
size="m"
|
||||
onClose={onClose}
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<OsqueryEventDetailsHeader
|
||||
primaryText={<h2>{ACTION_OSQUERY}</h2>}
|
||||
handleClick={onClose}
|
||||
data-test-subj="flyout-header-osquery"
|
||||
/>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<OsqueryActionWrapper data-test-subj="flyout-body-osquery">
|
||||
<OsqueryAction agentId={agentId} formType="steps" />
|
||||
</OsqueryActionWrapper>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<OsqueryEventDetailsFooter handleClick={onClose} data-test-subj="flyout-footer-osquery" />
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
);
|
||||
};
|
||||
|
||||
OsqueryFlyout.displayName = 'OsqueryFlyout';
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
interface EventDetailsFooterProps {
|
||||
handleClick: () => void;
|
||||
}
|
||||
|
||||
export const OsqueryEventDetailsFooterComponent = ({ handleClick }: EventDetailsFooterProps) => {
|
||||
return (
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={handleClick} data-test-subj="osquery-empty-button">
|
||||
<FormattedMessage id="xpack.securitySolution.footer.cancel" defaultMessage="Cancel" />
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export const OsqueryEventDetailsFooter = React.memo(OsqueryEventDetailsFooterComponent);
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { EuiButtonEmpty, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { BACK_TO_ALERT_DETAILS } from './translations';
|
||||
|
||||
interface IProps {
|
||||
primaryText: React.ReactElement;
|
||||
handleClick: () => void;
|
||||
}
|
||||
|
||||
const OsqueryEventDetailsHeaderComponent: React.FC<IProps> = ({ primaryText, handleClick }) => {
|
||||
return (
|
||||
<>
|
||||
<EuiButtonEmpty iconType="arrowLeft" iconSide="left" flush="left" onClick={handleClick}>
|
||||
<EuiText size="xs">
|
||||
<p>{BACK_TO_ALERT_DETAILS}</p>
|
||||
</EuiText>
|
||||
</EuiButtonEmpty>
|
||||
<EuiTitle>{primaryText}</EuiTitle>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const OsqueryEventDetailsHeader = React.memo(OsqueryEventDetailsHeaderComponent);
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
export const BACK_TO_ALERT_DETAILS = i18n.translate(
|
||||
'xpack.securitySolution.alertsView.osqueryBackToAlertDetails',
|
||||
{
|
||||
defaultMessage: 'Alert Details',
|
||||
}
|
||||
);
|
||||
|
||||
export const ACTION_OSQUERY = i18n.translate(
|
||||
'xpack.securitySolution.alertsView.osqueryAlertTitle',
|
||||
{
|
||||
defaultMessage: 'Run Osquery',
|
||||
}
|
||||
);
|
|
@ -78,6 +78,7 @@ describe('take action dropdown', () => {
|
|||
refetch: jest.fn(),
|
||||
refetchFlyoutData: jest.fn(),
|
||||
timelineId: TimelineId.active,
|
||||
onOsqueryClick: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
|
@ -89,8 +90,11 @@ describe('take action dropdown', () => {
|
|||
...mockStartServicesMock,
|
||||
timelines: { ...mockTimelines },
|
||||
cases: mockCasesContract(),
|
||||
osquery: {
|
||||
isOsqueryAvailable: jest.fn().mockReturnValue(true),
|
||||
},
|
||||
application: {
|
||||
capabilities: { siem: { crud_alerts: true, read_alerts: true } },
|
||||
capabilities: { siem: { crud_alerts: true, read_alerts: true }, osquery: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -190,6 +194,13 @@ describe('take action dropdown', () => {
|
|||
).toEqual('Investigate in timeline');
|
||||
});
|
||||
});
|
||||
test('should render "Run Osquery"', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="osquery-action-item"]').first().text()).toEqual(
|
||||
'Run Osquery'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should correctly enable/disable the "Add Endpoint event filter" button', () => {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { EuiButton, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
|
||||
import type { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
|
||||
import { TAKE_ACTION } from '../alerts_table/alerts_utility_bar/translations';
|
||||
|
@ -23,6 +23,8 @@ import { isAlertFromEndpointAlert } from '../../../common/utils/endpoint_alert_c
|
|||
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
|
||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
import { useAddToCaseActions } from '../alerts_table/timeline_actions/use_add_to_case_actions';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { OsqueryActionItem } from '../osquery/osquery_action_item';
|
||||
|
||||
interface ActionsData {
|
||||
alertStatus: Status;
|
||||
|
@ -45,6 +47,7 @@ export interface TakeActionDropdownProps {
|
|||
refetch: (() => void) | undefined;
|
||||
refetchFlyoutData: () => Promise<void>;
|
||||
timelineId: string;
|
||||
onOsqueryClick: (id: string) => void;
|
||||
}
|
||||
|
||||
export const TakeActionDropdown = React.memo(
|
||||
|
@ -61,6 +64,7 @@ export const TakeActionDropdown = React.memo(
|
|||
refetch,
|
||||
refetchFlyoutData,
|
||||
timelineId,
|
||||
onOsqueryClick,
|
||||
}: TakeActionDropdownProps) => {
|
||||
const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');
|
||||
const { loading: canAccessEndpointManagementLoading, canAccessEndpointManagement } =
|
||||
|
@ -70,6 +74,7 @@ export const TakeActionDropdown = React.memo(
|
|||
() => !canAccessEndpointManagementLoading && canAccessEndpointManagement,
|
||||
[canAccessEndpointManagement, canAccessEndpointManagementLoading]
|
||||
);
|
||||
const { osquery } = useKibana().services;
|
||||
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
|
||||
|
@ -97,6 +102,11 @@ export const TakeActionDropdown = React.memo(
|
|||
|
||||
const isEndpointEvent = useMemo(() => isEvent && isAgentEndpoint, [isEvent, isAgentEndpoint]);
|
||||
|
||||
const agentId = useMemo(
|
||||
() => getFieldValue({ category: 'agent', field: 'agent.id' }, detailsData),
|
||||
[detailsData]
|
||||
);
|
||||
|
||||
const togglePopoverHandler = useCallback(() => {
|
||||
setIsPopoverOpen(!isPopoverOpen);
|
||||
}, [isPopoverOpen]);
|
||||
|
@ -166,6 +176,23 @@ export const TakeActionDropdown = React.memo(
|
|||
onInvestigateInTimelineAlertClick: closePopoverHandler,
|
||||
});
|
||||
|
||||
const osqueryAvailable = osquery?.isOsqueryAvailable({
|
||||
agentId,
|
||||
});
|
||||
|
||||
const handleOnOsqueryClick = useCallback(() => {
|
||||
onOsqueryClick(agentId);
|
||||
setIsPopoverOpen(false);
|
||||
}, [onOsqueryClick, setIsPopoverOpen, agentId]);
|
||||
|
||||
const osqueryActionItem = useMemo(
|
||||
() =>
|
||||
OsqueryActionItem({
|
||||
handleClick: handleOnOsqueryClick,
|
||||
}),
|
||||
[handleOnOsqueryClick]
|
||||
);
|
||||
|
||||
const alertsActionItems = useMemo(
|
||||
() =>
|
||||
!isEvent && actionsData.ruleId
|
||||
|
@ -196,13 +223,16 @@ export const TakeActionDropdown = React.memo(
|
|||
...(tGridEnabled ? addToCaseActionItems : []),
|
||||
...alertsActionItems,
|
||||
...hostIsolationActionItems,
|
||||
...(osqueryAvailable ? [osqueryActionItem] : []),
|
||||
...investigateInTimelineActionItems,
|
||||
],
|
||||
[
|
||||
tGridEnabled,
|
||||
alertsActionItems,
|
||||
addToCaseActionItems,
|
||||
alertsActionItems,
|
||||
hostIsolationActionItems,
|
||||
osqueryAvailable,
|
||||
osqueryActionItem,
|
||||
investigateInTimelineActionItems,
|
||||
]
|
||||
);
|
||||
|
@ -220,7 +250,6 @@ export const TakeActionDropdown = React.memo(
|
|||
</EuiButton>
|
||||
);
|
||||
}, [togglePopoverHandler]);
|
||||
|
||||
return items.length && !loadingEventDetails && ecsData ? (
|
||||
<EuiPopover
|
||||
id="AlertTakeActionPanel"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { find } from 'lodash/fp';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
@ -20,6 +20,7 @@ import { getFieldValue } from '../../../../detections/components/host_isolation/
|
|||
import { Status } from '../../../../../common/detection_engine/schemas/common/schemas';
|
||||
import { Ecs } from '../../../../../common/ecs';
|
||||
import { inputsModel, inputsSelectors, State } from '../../../../common/store';
|
||||
import { OsqueryFlyout } from '../../../../detections/components/osquery/osquery_flyout';
|
||||
|
||||
interface EventDetailsFooterProps {
|
||||
detailsData: TimelineEventsDetailsItem[] | null;
|
||||
|
@ -109,6 +110,14 @@ export const EventDetailsFooterComponent = React.memo(
|
|||
const { closeAddEventFilterModal, isAddEventFilterModalOpen, onAddEventFilterClick } =
|
||||
useEventFilterModal();
|
||||
|
||||
const [isOsqueryFlyoutOpenWithAgentId, setOsqueryFlyoutOpenWithAgentId] = useState<
|
||||
null | string
|
||||
>(null);
|
||||
|
||||
const closeOsqueryFlyout = useCallback(() => {
|
||||
setOsqueryFlyoutOpenWithAgentId(null);
|
||||
}, [setOsqueryFlyoutOpenWithAgentId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyoutFooter data-test-subj="side-panel-flyout-footer">
|
||||
|
@ -128,6 +137,7 @@ export const EventDetailsFooterComponent = React.memo(
|
|||
refetch={refetchAll}
|
||||
indexName={expandedEvent.indexName}
|
||||
timelineId={timelineId}
|
||||
onOsqueryClick={setOsqueryFlyoutOpenWithAgentId}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
|
@ -154,6 +164,9 @@ export const EventDetailsFooterComponent = React.memo(
|
|||
maskProps={{ style: 'z-index: 5000' }}
|
||||
/>
|
||||
)}
|
||||
{isOsqueryFlyoutOpenWithAgentId && detailsEcsData != null && (
|
||||
<OsqueryFlyout agentId={isOsqueryFlyoutOpenWithAgentId} onClose={closeOsqueryFlyout} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import type { TimelinesUIStart } from '../../timelines/public';
|
|||
import type { ResolverPluginSetup } from './resolver/types';
|
||||
import type { Inspect } from '../common/search_strategy';
|
||||
import type { MlPluginSetup, MlPluginStart } from '../../ml/public';
|
||||
import type { OsqueryPluginStart } from '../../osquery/public';
|
||||
|
||||
import type { Detections } from './detections';
|
||||
import type { Cases } from './cases';
|
||||
|
@ -69,6 +70,7 @@ export interface StartPlugins {
|
|||
ml?: MlPluginStart;
|
||||
spaces?: SpacesPluginStart;
|
||||
dataViewFieldEditor: IndexPatternFieldEditorStart;
|
||||
osquery?: OsqueryPluginStart;
|
||||
}
|
||||
|
||||
export type StartServices = CoreStart &
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
{ "path": "../lists/tsconfig.json" },
|
||||
{ "path": "../maps/tsconfig.json" },
|
||||
{ "path": "../ml/tsconfig.json" },
|
||||
{ "path": "../osquery/tsconfig.json" },
|
||||
{ "path": "../spaces/tsconfig.json" },
|
||||
{ "path": "../security/tsconfig.json" },
|
||||
{ "path": "../timelines/tsconfig.json" }
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { last } from 'lodash';
|
||||
// import axios from 'axios';
|
||||
// import { last } from 'lodash';
|
||||
|
||||
export async function getLatestVersion(): Promise<string> {
|
||||
const response: any = await axios('https://artifacts-api.elastic.co/v1/versions');
|
||||
return last(response.data.versions as string[]) || '8.2.0-SNAPSHOT';
|
||||
return '8.1.0-SNAPSHOT';
|
||||
// const response: any = await axios('https://artifacts-api.elastic.co/v1/versions');
|
||||
// return last(response.data.versions as string[]) || '8.2.0-SNAPSHOT';
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue