diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts index 936d43fff0b48..af9a5ab765571 100644 --- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts +++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts @@ -19,6 +19,7 @@ export const alertsHeaders: ColumnHeader[] = [ columnHeaderType: defaultColumnHeaderType, id: 'event.module', width: DEFAULT_COLUMN_MIN_WIDTH, + linkField: 'rule.reference', }, { columnHeaderType: defaultColumnHeaderType, diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/constants.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/constants.tsx index 0330fb458e364..e8074c2f6f381 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/constants.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/constants.tsx @@ -8,4 +8,6 @@ export const DATE_FIELD_TYPE = 'date'; export const HOST_NAME_FIELD_NAME = 'host.name'; export const IP_FIELD_TYPE = 'ip'; export const MESSAGE_FIELD_NAME = 'message'; +export const EVENT_MODULE_FIELD_NAME = 'event.module'; +export const RULE_REFERENCE_FIELD_NAME = 'rule.reference'; export const SIGNAL_RULE_NAME_FIELD_NAME = 'signal.rule.name'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/formatted_field.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/formatted_field.tsx index 010a328d2993d..0f650d6386194 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/formatted_field.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiLink } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { isNumber, isString, isEmpty } from 'lodash/fp'; import React from 'react'; @@ -15,7 +15,7 @@ import { getOrEmptyTagFromValue, getEmptyTagValue } from '../../../empty_value'; import { FormattedDate } from '../../../formatted_date'; import { FormattedIp } from '../../../formatted_ip'; import { HostDetailsLink } from '../../../links'; -import { getRuleDetailsUrl } from '../../../link_to/redirect_to_detection_engine'; + import { Port, PORT_NAMES } from '../../../port'; import { TruncatableText } from '../../../truncatable_text'; import { @@ -23,8 +23,11 @@ import { HOST_NAME_FIELD_NAME, IP_FIELD_TYPE, MESSAGE_FIELD_NAME, + EVENT_MODULE_FIELD_NAME, + RULE_REFERENCE_FIELD_NAME, SIGNAL_RULE_NAME_FIELD_NAME, } from './constants'; +import { renderRuleName, renderEventModule, renderRulReference } from './formatted_field_helpers'; // simple black-list to prevent dragging and dropping fields such as message name const columnNamesNotDraggable = [MESSAGE_FIELD_NAME]; @@ -88,6 +91,12 @@ const FormattedFieldValueComponent: React.FC<{ return ( ); + } else if (fieldName === SIGNAL_RULE_NAME_FIELD_NAME) { + return renderRuleName({ contextId, eventId, fieldName, linkValue, truncate, value }); + } else if (fieldName === EVENT_MODULE_FIELD_NAME) { + return renderEventModule({ contextId, eventId, fieldName, linkValue, truncate, value }); + } else if (fieldName === RULE_REFERENCE_FIELD_NAME) { + return renderRulReference({ contextId, eventId, fieldName, linkValue, truncate, value }); } else if (columnNamesNotDraggable.includes(fieldName)) { return truncate && !isEmpty(value) ? ( @@ -110,24 +119,6 @@ const FormattedFieldValueComponent: React.FC<{ ) : ( <>{value} ); - } else if (fieldName === SIGNAL_RULE_NAME_FIELD_NAME) { - const ruleName = `${value}`; - const ruleId = linkValue; - - return isString(value) && ruleName.length > 0 && ruleId != null ? ( - - - {value} - - - ) : ( - getEmptyTagValue() - ); } else { const contentValue = getOrEmptyTagFromValue(value); const content = truncate ? {contentValue} : contentValue; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/formatted_field_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/formatted_field_helpers.tsx new file mode 100644 index 0000000000000..dc21cf03d0445 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/formatted_field_helpers.tsx @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiLink, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiToolTip } from '@elastic/eui'; +import { isString, isEmpty } from 'lodash/fp'; +import React from 'react'; + +import { DefaultDraggable } from '../../../draggables'; +import { getEmptyTagValue } from '../../../empty_value'; +import { getRuleDetailsUrl } from '../../../link_to/redirect_to_detection_engine'; +import { TruncatableText } from '../../../truncatable_text'; + +import { isUrlInvalid } from '../../../../pages/detection_engine/rules/components/step_about_rule/helpers'; +import endPointSvg from '../../../../utils/logo_endpoint/64_color.svg'; + +import * as i18n from './translations'; + +export const renderRuleName = ({ + contextId, + eventId, + fieldName, + linkValue, + truncate, + value, +}: { + contextId: string; + eventId: string; + fieldName: string; + linkValue: string | null | undefined; + truncate?: boolean; + value: string | number | null | undefined; +}) => { + const ruleName = `${value}`; + const ruleId = linkValue; + + const content = truncate ? {value} : value; + + return isString(value) && ruleName.length > 0 && ruleId != null ? ( + + {content} + + ) : ( + getEmptyTagValue() + ); +}; + +export const renderEventModule = ({ + contextId, + eventId, + fieldName, + linkValue, + truncate, + value, +}: { + contextId: string; + eventId: string; + fieldName: string; + linkValue: string | null | undefined; + truncate?: boolean; + value: string | number | null | undefined; +}) => { + const moduleName = `${value}`; + const endpointRefUrl = linkValue; + + const content = truncate ? {value} : value; + + return isString(value) && moduleName.length > 0 ? ( + + + + {content} + + + {endpointRefUrl != null && + !isEmpty(endpointRefUrl) && + !isUrlInvalid(endpointRefUrl) && + endpointRefUrl.includes('/alerts/') && ( + + +

{i18n.LINK_ELASTIC_ENDPOINT_SECURITY}

+

{endpointRefUrl}

+ + } + > + + + +
+
+ )} +
+ ) : ( + getEmptyTagValue() + ); +}; + +export const renderRulReference = ({ + contextId, + eventId, + fieldName, + linkValue, + truncate, + value, +}: { + contextId: string; + eventId: string; + fieldName: string; + linkValue: string | null | undefined; + truncate?: boolean; + value: string | number | null | undefined; +}) => { + const referenceUrlName = `${value}`; + + const content = truncate ? {value} : value; + + return isString(value) && referenceUrlName.length > 0 ? ( + + {!isUrlInvalid(referenceUrlName) && ( + + {content} + + )} + {isUrlInvalid(referenceUrlName) && <>{content}} + + ) : ( + getEmptyTagValue() + ); +}; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts index 2c3c3efdb2993..5bdeccbd0f4ba 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts @@ -29,3 +29,10 @@ export const IN = i18n.translate('xpack.siem.auditd.inDescription', { export const NON_EXISTENT = i18n.translate('xpack.siem.auditd.nonExistentDescription', { defaultMessage: 'an unknown process', }); + +export const LINK_ELASTIC_ENDPOINT_SECURITY = i18n.translate( + 'xpack.siem.event.module.linkToElasticEndpointSecurityDescription', + { + defaultMessage: 'Open in Elastic Endpoint Security', + } +); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx index b6ca4fe125c69..cbad2d42cf8af 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx @@ -121,7 +121,7 @@ describe('Footer Timeline Component', () => { .find('[data-test-subj="TimelineMoreButton"]') .dive() .text(); - expect(loadButton).toContain('Load More'); + expect(loadButton).toContain('Load more'); }); test('it does NOT render the loadMore button because there is nothing else to fetch', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/footer/translations.ts b/x-pack/legacy/plugins/siem/public/components/timeline/footer/translations.ts index 886866ce1b0c2..814311d4e14de 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/footer/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/timeline/footer/translations.ts @@ -27,7 +27,7 @@ export const LOADING = i18n.translate('xpack.siem.footer.loadingLabel', { }); export const LOAD_MORE = i18n.translate('xpack.siem.footer.loadMoreLabel', { - defaultMessage: 'Load More', + defaultMessage: 'Load more', }); export const TOTAL_COUNT_OF_EVENTS = i18n.translate('xpack.siem.footer.totalCountOfEvents', { diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx index 3dc6bac07be34..45f191f4a6fe5 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx @@ -45,10 +45,10 @@ export const useQuerySignals = ( useEffect(() => { let isSubscribed = true; const abortCtrl = new AbortController(); - setLoading(true); async function fetchData() { try { + setLoading(true); const signalResponse = await fetchQuerySignals({ query, signal: abortCtrl.signal, diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts b/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts index 9bd580f832230..c54238c5d8687 100644 --- a/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts +++ b/x-pack/legacy/plugins/siem/public/containers/timeline/index.gql_query.ts @@ -134,6 +134,9 @@ export const timelineQuery = gql` name ip } + rule { + reference + } source { bytes ip diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json index a9247403bf22c..b356b67b75c7b 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json @@ -3985,6 +3985,14 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "rule", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "RuleEcsField", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "signal", "description": "", @@ -4743,6 +4751,25 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "RuleEcsField", + "description": "", + "fields": [ + { + "name": "reference", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "SignalField", diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts index 6a24ffcc13020..0103713a8c8a2 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts @@ -791,6 +791,8 @@ export interface Ecs { network?: Maybe; + rule?: Maybe; + signal?: Maybe; source?: Maybe; @@ -970,6 +972,10 @@ export interface NetworkEcsField { transport?: Maybe; } +export interface RuleEcsField { + reference?: Maybe; +} + export interface SignalField { rule?: Maybe; @@ -4456,6 +4462,8 @@ export namespace GetTimelineQuery { host: Maybe; + rule: Maybe; + source: Maybe<_Source>; destination: Maybe; @@ -4671,6 +4679,12 @@ export namespace GetTimelineQuery { ip: Maybe; }; + export type Rule = { + __typename?: 'RuleEcsField'; + + reference: Maybe; + }; + export type _Source = { __typename?: 'SourceEcsFields'; @@ -4792,10 +4806,10 @@ export namespace GetTimelineQuery { original_time: Maybe; - rule: Maybe; + rule: Maybe<_Rule>; }; - export type Rule = { + export type _Rule = { __typename?: 'RuleField'; id: Maybe; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx index f5d138a3afcb8..e6bbffa4fd927 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx @@ -86,6 +86,11 @@ export const buildSignalsRuleIdFilter = (ruleId: string): esFilters.Filter[] => ]; export const signalsHeaders: ColumnHeader[] = [ + { + columnHeaderType: defaultColumnHeaderType, + id: '@timestamp', + width: DEFAULT_DATE_COLUMN_MIN_WIDTH, + }, { columnHeaderType: defaultColumnHeaderType, id: 'signal.rule.name', @@ -117,6 +122,12 @@ export const signalsHeaders: ColumnHeader[] = [ label: i18n.SIGNALS_HEADERS_RISK_SCORE, width: 120, }, + { + columnHeaderType: defaultColumnHeaderType, + id: 'event.module', + linkField: 'rule.reference', + width: DEFAULT_COLUMN_MIN_WIDTH, + }, { category: 'event', columnHeaderType: defaultColumnHeaderType, @@ -150,11 +161,6 @@ export const signalsHeaders: ColumnHeader[] = [ id: 'destination.ip', width: 140, }, - { - columnHeaderType: defaultColumnHeaderType, - id: '@timestamp', - width: DEFAULT_DATE_COLUMN_MIN_WIDTH, - }, ]; export const signalsDefaultModel: SubsetTimelineModel = { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index 1914f967813a1..7b615d5f159c2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -24,7 +24,11 @@ import { ActionCreator } from 'typescript-fsa'; import { connect } from 'react-redux'; import { FiltersGlobal } from '../../../../components/filters_global'; import { FormattedDate } from '../../../../components/formatted_date'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../../components/link_to/redirect_to_detection_engine'; +import { + getDetectionEngineUrl, + getEditRuleUrl, + getRulesUrl, +} from '../../../../components/link_to/redirect_to_detection_engine'; import { SiemSearchBar } from '../../../../components/search_bar'; import { WrapperPage } from '../../../../components/wrapper_page'; import { useRule } from '../../../../containers/detection_engine/rules'; @@ -237,7 +241,7 @@ const RuleDetailsPageComponent: FC = ({ isAuthenticated != null && (!isSignalIndexExists || !isAuthenticated) ) { - return ; + return ; } return ( @@ -257,7 +261,7 @@ const RuleDetailsPageComponent: FC = ({ = ({ diff --git a/x-pack/legacy/plugins/siem/public/utils/logo_endpoint/64_color.svg b/x-pack/legacy/plugins/siem/public/utils/logo_endpoint/64_color.svg new file mode 100644 index 0000000000000..b03007a76ffcc --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/utils/logo_endpoint/64_color.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts index 730e6b884a182..f897236b3470e 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts @@ -417,6 +417,10 @@ export const ecsSchema = gql` original_time: ToStringArray } + type RuleEcsField { + reference: ToStringArray + } + type ECS { _id: String! _index: String @@ -428,6 +432,7 @@ export const ecsSchema = gql` geo: GeoEcsFields host: HostEcsFields network: NetworkEcsField + rule: RuleEcsField signal: SignalField source: SourceEcsFields suricata: SuricataEcsFields diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts index 303262ece5c7f..c3fd6e9dde286 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts @@ -793,6 +793,8 @@ export interface Ecs { network?: Maybe; + rule?: Maybe; + signal?: Maybe; source?: Maybe; @@ -972,6 +974,10 @@ export interface NetworkEcsField { transport?: Maybe; } +export interface RuleEcsField { + reference?: Maybe; +} + export interface SignalField { rule?: Maybe; @@ -4279,6 +4285,8 @@ export namespace EcsResolvers { network?: NetworkResolver, TypeParent, TContext>; + rule?: RuleResolver, TypeParent, TContext>; + signal?: SignalResolver, TypeParent, TContext>; source?: SourceResolver, TypeParent, TContext>; @@ -4358,6 +4366,11 @@ export namespace EcsResolvers { Parent = Ecs, TContext = SiemContext > = Resolver; + export type RuleResolver< + R = Maybe, + Parent = Ecs, + TContext = SiemContext + > = Resolver; export type SignalResolver< R = Maybe, Parent = Ecs, @@ -4935,6 +4948,18 @@ export namespace NetworkEcsFieldResolvers { > = Resolver; } +export namespace RuleEcsFieldResolvers { + export interface Resolvers { + reference?: ReferenceResolver, TypeParent, TContext>; + } + + export type ReferenceResolver< + R = Maybe, + Parent = RuleEcsField, + TContext = SiemContext + > = Resolver; +} + export namespace SignalFieldResolvers { export interface Resolvers { rule?: RuleResolver, TypeParent, TContext>; @@ -9231,6 +9256,7 @@ export type IResolvers = { EndgameEcsFields?: EndgameEcsFieldsResolvers.Resolvers; EventEcsFields?: EventEcsFieldsResolvers.Resolvers; NetworkEcsField?: NetworkEcsFieldResolvers.Resolvers; + RuleEcsField?: RuleEcsFieldResolvers.Resolvers; SignalField?: SignalFieldResolvers.Resolvers; RuleField?: RuleFieldResolvers.Resolvers; SuricataEcsFields?: SuricataEcsFieldsResolvers.Resolvers; diff --git a/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts b/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts index f85fb2c9fd753..eb483de000915 100644 --- a/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/ecs_fields/index.ts @@ -318,6 +318,10 @@ export const signalFieldsMap: Readonly> = { 'signal.rule.version': 'signal.rule.version', }; +export const ruleFieldsMap: Readonly> = { + 'rule.reference': 'rule.reference', +}; + export const eventFieldsMap: Readonly> = { timestamp: '@timestamp', '@timestamp': '@timestamp', @@ -331,6 +335,7 @@ export const eventFieldsMap: Readonly> = { ...{ ...geoFieldsMap }, ...{ ...hostFieldsMap }, ...{ ...networkFieldsMap }, + ...{ ...ruleFieldsMap }, ...{ ...signalFieldsMap }, ...{ ...sourceFieldsMap }, ...{ ...suricataFieldsMap },