mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution][Detections] Integrates installed integrations into interface (#132847)
## Summary Wires up the new Installed Integrations API added in https://github.com/elastic/kibana/pull/132667 to the new Related Integrations UI added in https://github.com/elastic/kibana/pull/131475. #### Additional changes include (though not all necessary for this specific PR): - [X] Updates integrations badge icon to `package` on Rules Table - [ ] Add Kibana Advanced Setting for disabling integrations badge on Rules Table - [ ] Add loaders where necessary since there can now be API delay - [ ] Separate description step components to specific files Please see https://github.com/elastic/kibana/pull/131475 for screenshots and additional details. #### Steps to test In this initial implementation these new fields are only visible with Prebuilt Rules, and so there is limited API support and currently no UI for editing them. If a Prebuilt Rule is duplicated, these fields are emptied (set to `''` or `[]`). When a Rule is exported these fields are included (as empty values), and it is possible to edit the `ndjson` and re-import and then see these fields for the Custom Rule (but still not editable in the UI). This is expected behavior, and is actually a nice and easy way to test. Here is a sample export you can paste into a `test.ndjson` file and import to test this feature. You can modify the `package`/`version` fields to test corner cases like if a package is installed but it's the wrong version. ``` {"id":"6cc39c80-da3a-11ec-9fce-65c1a0bee904","updated_at":"2022-05-23T01:48:23.422Z","updated_by":"elastic","created_at":"2022-05-23T01:48:20.940Z","created_by":"elastic","name":"Testing #131475, don't mind me...","tags":["Elastic","Endpoint Security"],"interval":"5m","enabled":false,"description":"Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.","risk_score":47,"severity":"medium","license":"Elastic License v2","output_index":".siem-signals-default","meta":{"from":"5m"},"rule_name_override":"message","timestamp_override":"event.ingested","author":["Elastic"],"false_positives":[],"from":"now-600s","rule_id":"2c66bf23-6ae9-4eb2-859e-446bea181ae9","max_signals":10000,"risk_score_mapping":[{"field":"event.risk_score","operator":"equals","value":""}],"severity_mapping":[{"field":"event.severity","operator":"equals","severity":"low","value":"21"},{"field":"event.severity","operator":"equals","severity":"medium","value":"47"},{"field":"event.severity","operator":"equals","severity":"high","value":"73"},{"field":"event.severity","operator":"equals","severity":"critical","value":"99"}],"threat":[],"to":"now","references":[],"version":7,"exceptions_list":[{"id":"endpoint_list","list_id":"endpoint_list","namespace_type":"agnostic","type":"endpoint"}],"immutable":false,"related_integrations":[{"package":"system","version":"1.6.4"},{"package":"aws","integration":"cloudtrail","version":"1.11.0"}],"required_fields":[{"ecs":true,"name":"event.code","type":"keyword"},{"ecs":true,"name":"message","type":"match_only_text"},{"ecs":false,"name":"winlog.event_data.AttributeLDAPDisplayName","type":"keyword"},{"ecs":false,"name":"winlog.event_data.AttributeValue","type":"keyword"},{"ecs":false,"name":"winlog.event_data.ShareName","type":"keyword"},{"ecs":false,"name":"winlog.event_data.RelativeTargetName","type":"keyword"},{"ecs":false,"name":"winlog.event_data.AccessList","type":"keyword"}],"setup":"## Config\\n\\nThe 'Audit Detailed File Share' audit policy must be configured (Success Failure).\\nSteps to implement the logging policy with with Advanced Audit Configuration:\\n\\n```\\nComputer Configuration > \\nPolicies > \\nWindows Settings > \\nSecurity Settings > \\nAdvanced Audit Policies Configuration > \\nAudit Policies > \\nObject Access > \\nAudit Detailed File Share (Success,Failure)\\n```\\n\\nThe 'Audit Directory Service Changes' audit policy must be configured (Success Failure).\\nSteps to implement the logging policy with with Advanced Audit Configuration:\\n\\n```\\nComputer Configuration > \\nPolicies > \\nWindows Settings > \\nSecurity Settings > \\nAdvanced Audit Policies Configuration > \\nAudit Policies > \\nDS Access > \\nAudit Directory Service Changes (Success,Failure)\\n```\\n","type":"query","language":"kuery","index":["logs-endpoint.alerts-*"],"query":"event.kind:alert and event.module:(endpoint and not endgame)\\n","filters":[],"throttle":"no_actions","actions":[]} {"exported_count":1,"exported_rules_count":1,"missing_rules":[],"missing_rules_count":0,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0} ```
This commit is contained in:
parent
b5ab073189
commit
3cacbe7c49
6 changed files with 116 additions and 101 deletions
|
@ -10,29 +10,58 @@ import { capitalize } from 'lodash';
|
|||
import React from 'react';
|
||||
import semver from 'semver';
|
||||
import {
|
||||
InstalledIntegration,
|
||||
InstalledIntegrationArray,
|
||||
RelatedIntegration,
|
||||
RelatedIntegrationArray,
|
||||
} from '../../../../common/detection_engine/schemas/common';
|
||||
|
||||
/**
|
||||
* Returns an `EuiLink` that will link to a given package/integration/version page within fleet
|
||||
* @param integration
|
||||
* @param basePath
|
||||
* TODO: Add `title` to RelatedIntegration so we can accurately display the integration pretty name
|
||||
*
|
||||
* @param integration either RelatedIntegration or InstalledIntegration
|
||||
* @param basePath kbn basepath for composing the fleet URL
|
||||
*/
|
||||
export const getIntegrationLink = (integration: RelatedIntegration, basePath: string) => {
|
||||
const integrationURL = `${basePath}/app/integrations/detail/${integration.package}-${
|
||||
integration.version
|
||||
}/overview${integration.integration ? `?integration=${integration.integration}` : ''}`;
|
||||
export const getIntegrationLink = (
|
||||
integration: RelatedIntegration | InstalledIntegration,
|
||||
basePath: string
|
||||
) => {
|
||||
let packageName: string;
|
||||
let integrationName: string | undefined;
|
||||
let integrationTitle: string;
|
||||
let version: string | null;
|
||||
|
||||
// InstalledIntegration
|
||||
if ('package_name' in integration) {
|
||||
packageName = integration.package_name;
|
||||
integrationName = integration.integration_name;
|
||||
integrationTitle = integration.integration_title ?? integration.package_name;
|
||||
version = integration.package_version;
|
||||
} else {
|
||||
// RelatedIntegration
|
||||
packageName = integration.package;
|
||||
integrationName = integration.integration;
|
||||
integrationTitle = `${capitalize(integration.package)} ${capitalize(integration.integration)}`;
|
||||
version = semver.valid(semver.coerce(integration.version));
|
||||
}
|
||||
|
||||
const integrationURL =
|
||||
version != null
|
||||
? `${basePath}/app/integrations/detail/${packageName}-${version}/overview${
|
||||
integrationName ? `?integration=${integrationName}` : ''
|
||||
}`
|
||||
: `${basePath}/app/integrations/detail/${packageName}`;
|
||||
return (
|
||||
<EuiLink href={integrationURL} target="_blank">
|
||||
{`${capitalize(integration.package)} ${capitalize(integration.integration)}`}
|
||||
{integrationTitle}
|
||||
</EuiLink>
|
||||
);
|
||||
};
|
||||
|
||||
export interface InstalledIntegration extends RelatedIntegration {
|
||||
export interface InstalledIntegrationAugmented extends InstalledIntegration {
|
||||
targetVersion: string;
|
||||
versionSatisfied?: boolean;
|
||||
versionSatisfied: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,21 +73,22 @@ export interface InstalledIntegration extends RelatedIntegration {
|
|||
*/
|
||||
export const getInstalledRelatedIntegrations = (
|
||||
integrations: RelatedIntegrationArray,
|
||||
installedIntegrations: RelatedIntegrationArray
|
||||
installedIntegrations: InstalledIntegrationArray
|
||||
): {
|
||||
availableIntegrations: RelatedIntegrationArray;
|
||||
installedRelatedIntegrations: InstalledIntegration[];
|
||||
installedRelatedIntegrations: InstalledIntegrationAugmented[];
|
||||
} => {
|
||||
const availableIntegrations: RelatedIntegrationArray = [];
|
||||
const installedRelatedIntegrations: InstalledIntegration[] = [];
|
||||
const installedRelatedIntegrations: InstalledIntegrationAugmented[] = [];
|
||||
|
||||
integrations.forEach((i: RelatedIntegration) => {
|
||||
const match = installedIntegrations.find(
|
||||
(installed) => installed.package === i.package && installed?.integration === i?.integration
|
||||
(installed) =>
|
||||
installed.package_name === i.package && installed?.integration_name === i?.integration
|
||||
);
|
||||
if (match != null) {
|
||||
// Version check e.g. fleet match `1.2.3` satisfies rule dependency `~1.2.1`
|
||||
const versionSatisfied = semver.satisfies(match.version, i.version);
|
||||
const versionSatisfied = semver.satisfies(match.package_version, i.version);
|
||||
installedRelatedIntegrations.push({ ...match, targetVersion: i.version, versionSatisfied });
|
||||
} else {
|
||||
availableIntegrations.push(i);
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
EuiIconTip,
|
||||
} from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { InstalledIntegrationArray } from '../../../../common/detection_engine/schemas/common';
|
||||
import { useBasePath } from '../../lib/kibana';
|
||||
import { getInstalledRelatedIntegrations, getIntegrationLink } from './helpers';
|
||||
import { useInstalledIntegrations } from '../../../detections/containers/detection_engine/rules/use_installed_integrations';
|
||||
|
@ -50,12 +51,13 @@ const IntegrationsPopoverComponent = ({ integrations }: IntegrationsPopoverProps
|
|||
const { data } = useInstalledIntegrations({ packages: [] });
|
||||
const basePath = useBasePath();
|
||||
|
||||
const allInstalledIntegrations: RelatedIntegrationArray = data ?? [];
|
||||
const allInstalledIntegrations: InstalledIntegrationArray = data?.installed_integrations ?? [];
|
||||
const { availableIntegrations, installedRelatedIntegrations } = getInstalledRelatedIntegrations(
|
||||
integrations,
|
||||
allInstalledIntegrations
|
||||
);
|
||||
|
||||
// TODO: Add loader to installed integrations value
|
||||
const badgeTitle =
|
||||
data != null
|
||||
? `${installedRelatedIntegrations.length}/${integrations.length} ${i18n.INTEGRATIONS_BADGE}`
|
||||
|
@ -72,7 +74,7 @@ const IntegrationsPopoverComponent = ({ integrations }: IntegrationsPopoverProps
|
|||
data-test-subj={'IntegrationsDisplayPopover'}
|
||||
button={
|
||||
<EuiBadge
|
||||
iconType={'tag'}
|
||||
iconType={'package'}
|
||||
color="hollow"
|
||||
data-test-subj={'IntegrationsDisplayPopoverButton'}
|
||||
onClick={() => setPopoverOpen(!isPopoverOpen)}
|
||||
|
@ -105,7 +107,7 @@ const IntegrationsPopoverComponent = ({ integrations }: IntegrationsPopoverProps
|
|||
<EuiIconTip
|
||||
type="alert"
|
||||
content={i18n.INTEGRATIONS_INSTALLED_VERSION_TOOLTIP(
|
||||
integration.version,
|
||||
integration.package_version,
|
||||
integration.targetVersion
|
||||
)}
|
||||
position="right"
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from '../../../../common/components/integrations_popover/helpers';
|
||||
|
||||
import {
|
||||
InstalledIntegrationArray,
|
||||
RelatedIntegration,
|
||||
RelatedIntegrationArray,
|
||||
} from '../../../../../common/detection_engine/schemas/common';
|
||||
|
@ -35,7 +36,7 @@ const IntegrationDescriptionComponent: React.FC<{ integration: RelatedIntegratio
|
|||
const badgeUninstalledColor = 'accent';
|
||||
const { data } = useInstalledIntegrations({ packages: [] });
|
||||
|
||||
const allInstalledIntegrations: RelatedIntegrationArray = data ?? [];
|
||||
const allInstalledIntegrations: InstalledIntegrationArray = data?.installed_integrations ?? [];
|
||||
const { availableIntegrations, installedRelatedIntegrations } = getInstalledRelatedIntegrations(
|
||||
[integration],
|
||||
allInstalledIntegrations
|
||||
|
@ -59,7 +60,7 @@ const IntegrationDescriptionComponent: React.FC<{ integration: RelatedIntegratio
|
|||
<EuiIconTip
|
||||
type="alert"
|
||||
content={INTEGRATIONS_INSTALLED_VERSION_TOOLTIP(
|
||||
installedRelatedIntegrations[0]?.version,
|
||||
installedRelatedIntegrations[0]?.package_version,
|
||||
installedRelatedIntegrations[0]?.targetVersion
|
||||
)}
|
||||
position="right"
|
||||
|
|
|
@ -23,7 +23,6 @@ import {
|
|||
import {
|
||||
AggregateRuleExecutionEvent,
|
||||
BulkAction,
|
||||
RelatedIntegrationArray,
|
||||
RuleExecutionStatus,
|
||||
} from '../../../../../common/detection_engine/schemas/common';
|
||||
import {
|
||||
|
@ -34,6 +33,7 @@ import {
|
|||
RulesSchema,
|
||||
GetAggregateRuleExecutionEventsResponse,
|
||||
} from '../../../../../common/detection_engine/schemas/response';
|
||||
import { GetInstalledIntegrationsResponse } from '../../../../../common/detection_engine/schemas/response/get_installed_integrations_response_schema';
|
||||
|
||||
import {
|
||||
UpdateRulesProps,
|
||||
|
@ -425,8 +425,8 @@ export const fetchInstalledIntegrations = async ({
|
|||
}: {
|
||||
packages?: string[];
|
||||
signal?: AbortSignal;
|
||||
}): Promise<RelatedIntegrationArray> =>
|
||||
KibanaServices.get().http.fetch<RelatedIntegrationArray>(
|
||||
}): Promise<GetInstalledIntegrationsResponse> =>
|
||||
KibanaServices.get().http.fetch<GetInstalledIntegrationsResponse>(
|
||||
DETECTION_ENGINE_INSTALLED_INTEGRATIONS_URL,
|
||||
{
|
||||
method: 'GET',
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
*/
|
||||
|
||||
import { useQuery } from 'react-query';
|
||||
import { RelatedIntegrationArray } from '../../../../../common/detection_engine/schemas/common';
|
||||
import { GetInstalledIntegrationsResponse } from '../../../../../common/detection_engine/schemas/response/get_installed_integrations_response_schema';
|
||||
import { fetchInstalledIntegrations } from './api';
|
||||
import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
|
||||
import * as i18n from './translations';
|
||||
|
||||
|
@ -17,9 +18,7 @@ export interface UseInstalledIntegrationsArgs {
|
|||
export const useInstalledIntegrations = ({ packages }: UseInstalledIntegrationsArgs) => {
|
||||
const { addError } = useAppToasts();
|
||||
|
||||
// TODO: Once API is merged update return type:
|
||||
// See: https://github.com/elastic/kibana/pull/132667/files#diff-f9d9583d37123ed28fd08fc153eb06026e7ee0c3241364656fb707dcbc0a4872R58-R65
|
||||
return useQuery<RelatedIntegrationArray | undefined>(
|
||||
return useQuery<GetInstalledIntegrationsResponse>(
|
||||
[
|
||||
'installedIntegrations',
|
||||
{
|
||||
|
@ -27,27 +26,10 @@ export const useInstalledIntegrations = ({ packages }: UseInstalledIntegrationsA
|
|||
},
|
||||
],
|
||||
async ({ signal }) => {
|
||||
return undefined;
|
||||
|
||||
// Mock data -- uncomment to test full UI
|
||||
// const mockInstalledIntegrations = [
|
||||
// {
|
||||
// package: 'system',
|
||||
// version: '1.7.4',
|
||||
// },
|
||||
// // {
|
||||
// // package: 'aws',
|
||||
// // integration: 'cloudtrail',
|
||||
// // version: '1.11.0',
|
||||
// // },
|
||||
// ];
|
||||
// return mockInstalledIntegrations;
|
||||
|
||||
// Or fetch from new API
|
||||
// return fetchInstalledIntegrations({
|
||||
// packages,
|
||||
// signal,
|
||||
// });
|
||||
return fetchInstalledIntegrations({
|
||||
packages,
|
||||
signal,
|
||||
});
|
||||
},
|
||||
{
|
||||
keepPreviousData: true,
|
||||
|
|
|
@ -731,6 +731,33 @@ exports[`Details Panel Component DetailsPanel:HostDetails: rendering it should r
|
|||
color: #535966;
|
||||
}
|
||||
|
||||
.c2 dt {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.c2 dd {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c2 dd > div {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.c1 .euiButtonIcon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
|
@ -759,33 +786,6 @@ exports[`Details Panel Component DetailsPanel:HostDetails: rendering it should r
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.c2 dt {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.c2 dd {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c2 dd > div {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.c1 .euiButtonIcon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
padding: 16px;
|
||||
background: rgba(250,251,253,0.9);
|
||||
|
@ -1872,6 +1872,33 @@ exports[`Details Panel Component DetailsPanel:NetworkDetails: rendering it shoul
|
|||
color: #535966;
|
||||
}
|
||||
|
||||
.c2 dt {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.c2 dd {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c2 dd > div {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.c1 .euiButtonIcon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
|
@ -1900,33 +1927,6 @@ exports[`Details Panel Component DetailsPanel:NetworkDetails: rendering it shoul
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.c2 dt {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.c2 dd {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c2 dd > div {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.c1 .euiButtonIcon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
padding: 16px;
|
||||
background: rgba(250,251,253,0.9);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue