diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.ts b/.buildkite/scripts/steps/storybooks/build_and_upload.ts index 483a5c28a295b..b1135490a2023 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.ts +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.ts @@ -18,15 +18,16 @@ const STORYBOOKS = [ 'canvas', 'cases', 'cell_actions', - 'coloring', 'chart_icons', + 'cloud_security_posture_packages', + 'coloring', 'content_management_examples', 'custom_integrations', 'dashboard_enhanced', 'dashboard', 'data', - 'logs_explorer', 'embeddable', + 'esql_editor', 'expression_error', 'expression_image', 'expression_metric', @@ -34,28 +35,28 @@ const STORYBOOKS = [ 'expression_reveal_image', 'expression_shape', 'expression_tagcloud', - 'management', 'fleet', 'grouping', 'home', 'infra', 'kibana_react', + 'language_documentation_popover', 'lists', - 'observability', + 'logs_explorer', + 'management', 'observability_ai_assistant', 'observability_inventory', 'observability_shared', + 'observability', 'presentation', - 'security_solution', + 'random_sampling', 'security_solution_packages', + 'security_solution', 'serverless', 'shared_ux', 'triggers_actions_ui', 'ui_actions_enhanced', - 'language_documentation_popover', 'unified_search', - 'random_sampling', - 'esql_editor', ]; const GITHUB_CONTEXT = 'Build and Publish Storybooks'; diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3e73a2a40a388..80d9c8b64e9e6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1911,10 +1911,10 @@ x-pack/plugins/osquery @elastic/security-defend-workflows /x-pack/plugins/security_solution/public/detections/components/osquery @elastic/security-defend-workflows # Cloud Defend -/x-pack/plugins/cloud_defend/ @elastic/kibana-cloud-security-posture /x-pack/plugins/security_solution/public/cloud_defend @elastic/kibana-cloud-security-posture # Cloud Security Posture +x-pack/packages/kbn-cloud-security-posture @elastic/kibana-cloud-security-posture /x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.* @elastic/kibana-cloud-security-posture /x-pack/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture /x-pack/test/api_integration/apis/cloud_security_posture/ @elastic/kibana-cloud-security-posture diff --git a/docs/api/synthetics/params/edit-param.asciidoc b/docs/api/synthetics/params/edit-param.asciidoc index e615dd0c0bd1f..07a2568207dfe 100644 --- a/docs/api/synthetics/params/edit-param.asciidoc +++ b/docs/api/synthetics/params/edit-param.asciidoc @@ -26,13 +26,13 @@ You must have `all` privileges for the *Synthetics* feature in the *{observabili [[parameter-edit-request-body]] ==== Request body -The request body should contain the following attributes: +The request body can contain the following attributes, it can't be empty at least one attribute is required.: `key`:: -(Required, string) The key of the parameter. +(Optional, string) The key of the parameter. `value`:: -(Required, string) The updated value associated with the parameter. +(Optional, string) The updated value associated with the parameter. `description`:: (Optional, string) The updated description of the parameter. diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 52a837724480d..96a17a6ae7229 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -259,6 +259,27 @@ export function getWebpackConfig( }, }, }, + { + test: /\.js$/, + include: /node_modules[\\\/]@dagrejs/, + use: { + loader: 'babel-loader', + options: { + envName: worker.dist ? 'production' : 'development', + presets: ['@babel/preset-env'], // Doesn't work with BABEL_PRESET + plugins: ['@babel/plugin-proposal-class-properties'], + }, + }, + }, + { + test: /node_modules[\/\\]@?xyflow[\/\\].*.js$/, + loaders: 'babel-loader', + options: { + envName: worker.dist ? 'production' : 'development', + presets: [BABEL_PRESET], + plugins: ['@babel/plugin-transform-logical-assignment-operators'], + }, + }, { test: /\.(html|md|txt|tmpl)$/, use: { diff --git a/packages/kbn-storybook/src/webpack.config.ts b/packages/kbn-storybook/src/webpack.config.ts index b03d78dbbc190..fad795a1e4619 100644 --- a/packages/kbn-storybook/src/webpack.config.ts +++ b/packages/kbn-storybook/src/webpack.config.ts @@ -136,6 +136,25 @@ export default ({ config: storybookConfig }: { config: Configuration }) => { }, }, }, + { + test: /\.js$/, + include: /node_modules[\\\/]@dagrejs/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'], // Doesn't work with @kbn/babel-preset/webpack_preset + plugins: ['@babel/plugin-proposal-class-properties'], + }, + }, + }, + { + test: /node_modules[\/\\]@?xyflow[\/\\].*.js$/, + loaders: 'babel-loader', + options: { + presets: [require.resolve('@kbn/babel-preset/webpack_preset')], + plugins: ['@babel/plugin-transform-logical-assignment-operators'], + }, + }, ], }, plugins: [new IgnoreNotFoundExportPlugin()], diff --git a/test/functional/apps/discover/group3/_lens_vis.ts b/test/functional/apps/discover/group3/_lens_vis.ts index db526fe978610..5e13c8bbb243c 100644 --- a/test/functional/apps/discover/group3/_lens_vis.ts +++ b/test/functional/apps/discover/group3/_lens_vis.ts @@ -110,8 +110,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return seriesType; } - // Failing: See https://github.com/elastic/kibana/issues/197342 - describe.skip('discover lens vis', function () { + describe('discover lens vis', function () { before(async () => { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); @@ -616,8 +615,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await getCurrentVisTitle()).to.be('Pie'); await testSubjects.existOrFail('partitionVisChart'); - await discover.chooseLensSuggestion('barVerticalStacked'); - await changeVisShape('Line'); + await discover.chooseLensSuggestion('waffle'); + await changeVisShape('Treemap'); await testSubjects.existOrFail('unsavedChangesBadge'); await discover.saveUnsavedChanges(); @@ -626,8 +625,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); - expect(await getCurrentVisTitle()).to.be('Line'); - await testSubjects.existOrFail('xyVisChart'); + expect(await getCurrentVisTitle()).to.be('Treemap'); + await testSubjects.existOrFail('partitionVisChart'); }); it('should close lens flyout on revert changes', async () => { diff --git a/x-pack/packages/kbn-cloud-security-posture/common/kibana.jsonc b/x-pack/packages/kbn-cloud-security-posture/common/kibana.jsonc index 21721cfb69f44..f3bd18f10c7a8 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/kibana.jsonc +++ b/x-pack/packages/kbn-cloud-security-posture/common/kibana.jsonc @@ -1,6 +1,5 @@ - { - "id": "@kbn/cloud-security-posture-common", - "owner": "@elastic/kibana-cloud-security-posture", - "type": "shared-common" - } \ No newline at end of file + "id": "@kbn/cloud-security-posture-common", + "owner": "@elastic/kibana-cloud-security-posture", + "type": "shared-common" +} diff --git a/x-pack/packages/kbn-cloud-security-posture/common/package.json b/x-pack/packages/kbn-cloud-security-posture/common/package.json index 8ead7b37ceeb6..37276e735987d 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/package.json +++ b/x-pack/packages/kbn-cloud-security-posture/common/package.json @@ -1,8 +1,8 @@ { - "name": "@kbn/cloud-security-posture-common", - "private": true, - "version": "1.0.0", - "license": "Elastic License 2.0", - "description": "Shared components for cloud security posture, both client and server side", - "sideEffects": false - } \ No newline at end of file + "name": "@kbn/cloud-security-posture-common", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0", + "description": "Shared components for cloud security posture, both client and server side", + "sideEffects": false +} diff --git a/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts index f27ddb397c57c..3d37331b4cc5d 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts @@ -71,8 +71,6 @@ export const groupNodeDataSchema = schema.allOf([ export const labelNodeDataSchema = schema.allOf([ nodeBaseDataSchema, schema.object({ - source: schema.string(), - target: schema.string(), shape: schema.literal('label'), parentId: schema.maybe(schema.string()), color: colorSchema, @@ -82,8 +80,6 @@ export const labelNodeDataSchema = schema.allOf([ export const edgeDataSchema = schema.object({ id: schema.string(), source: schema.string(), - sourceShape: nodeShapeSchema, target: schema.string(), - targetShape: nodeShapeSchema, color: colorSchema, }); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/README.md b/x-pack/packages/kbn-cloud-security-posture/graph/README.md index c67ca622fe414..bde99acb4e7a6 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/README.md +++ b/x-pack/packages/kbn-cloud-security-posture/graph/README.md @@ -7,11 +7,104 @@ security solution plugin. ## How to use this -Standalone examples will follow. In the meantime check out storybook to view the graph's progress. +### Step 1: Import the Component -## The most important public api members +First, import the `Graph` component into your desired file. -- GraphComponent itself (comming soon..) +```tsx +import { Graph } from '@kbn/cloud-security-posture-graph'; +``` + +### Step 2: Prepare the Data + +Create the nodes and edges data models. These should follow the `NodeViewModel` and `EdgeViewModel` interfaces. + +```tsx +const nodes: NodeViewModel[] = [ + { + id: 'node1', + label: 'Node 1', + color: 'primary', + shape: 'ellipse', + icon: 'user', + }, + { + id: 'node2', + label: 'Node 2', + color: 'primary', + shape: 'hexagon', + icon: 'questionInCircle', + }, +]; + +const edges: EdgeViewModel[] = [ + { + id: 'edge1', + source: 'node1', + target: 'node2', + color: 'primary', + }, +]; +``` + +### Step 3: Render the Component + +Use the `Graph` component in your JSX/TSX, passing the nodes, edges, and interactivity flag as props. + +```tsx + +``` + +### Example Usage + +Here is a complete example of how to use the `Graph` component in a React component. + +```tsx +import React from 'react'; +import { Graph } from '@kbn/cloud-security-posture-graph'; +import type { NodeViewModel, EdgeViewModel } from '@kbn/cloud-security-posture-graph'; + +const App: React.FC = () => { + const nodes: NodeViewModel[] = [ + { + id: 'node1', + label: 'Node 1', + color: 'primary', + shape: 'ellipse', + icon: 'user', + }, + { + id: 'node2', + label: 'Node 2', + color: 'primary', + shape: 'hexagon', + icon: 'questionInCircle', + }, + ]; + + const edges: EdgeViewModel[] = [ + { + id: 'edge1', + source: 'node1', + target: 'node2', + color: 'primary', + }, + ]; + + return ( +
+

Graph Visualization

+ +
+ ); +}; + +export default App; +``` + +### Storybook Example + +You can also see how the `Graph` component is used in the Storybook file `graph_layout.stories.tsx`. ### Extras diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/index.ts b/x-pack/packages/kbn-cloud-security-posture/graph/index.ts index 1fec1c76430eb..c50969cfd6402 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/index.ts +++ b/x-pack/packages/kbn-cloud-security-posture/graph/index.ts @@ -4,3 +4,5 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + +export * from './src/components'; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/jest.config.js b/x-pack/packages/kbn-cloud-security-posture/graph/jest.config.js index 9e295d0f4d626..3b8fbbd9384a4 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/jest.config.js +++ b/x-pack/packages/kbn-cloud-security-posture/graph/jest.config.js @@ -9,4 +9,6 @@ module.exports = { preset: '@kbn/test', roots: ['/x-pack/packages/kbn-cloud-security-posture/graph'], rootDir: '../../../..', + setupFiles: ['jest-canvas-mock'], + setupFilesAfterEnv: ['/x-pack/packages/kbn-cloud-security-posture/graph/setup_tests.ts'], }; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/kibana.jsonc b/x-pack/packages/kbn-cloud-security-posture/graph/kibana.jsonc index 455f1607a22a2..513861b347059 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/kibana.jsonc +++ b/x-pack/packages/kbn-cloud-security-posture/graph/kibana.jsonc @@ -1,5 +1,5 @@ { - "type": "shared-browser", "id": "@kbn/cloud-security-posture-graph", - "owner": "@elastic/kibana-cloud-security-posture" + "owner": "@elastic/kibana-cloud-security-posture", + "type": "shared-browser" } diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/setup_tests.ts b/x-pack/packages/kbn-cloud-security-posture/graph/setup_tests.ts new file mode 100644 index 0000000000000..7f6858c0fd40d --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/setup_tests.ts @@ -0,0 +1,13 @@ +/* + * 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. + */ + +// eslint-disable-next-line @kbn/imports/no_boundary_crossing +import { mockReactFlow } from './src/components/mock/react_flow'; +// eslint-disable-next-line import/no-extraneous-dependencies +import '@testing-library/jest-dom'; + +mockReactFlow(); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.test.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.test.tsx new file mode 100644 index 0000000000000..18ba84207f962 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.test.tsx @@ -0,0 +1,110 @@ +/* + * 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 { render } from '@testing-library/react'; +import React from 'react'; +import { Graph, type GraphProps } from './graph'; +import { TestProviders } from '../mock/test_providers'; + +const renderGraphPreview = (props: GraphProps) => + render( + + + + ); + +describe('', () => { + it('should render empty graph', () => { + const { container } = renderGraphPreview({ + nodes: [], + edges: [], + interactive: false, + }); + + expect(container).not.toBeNull(); + const nodes = container.querySelectorAll('.react-flow__nodes .react-flow__node'); + expect(nodes).toHaveLength(0); + }); + + it('should render hexagon node', () => { + const { container } = renderGraphPreview({ + nodes: [ + { + id: '1', + label: 'Node 1', + color: 'primary', + shape: 'hexagon', + }, + ], + edges: [], + interactive: false, + }); + + const nodeEl = container.querySelector('[data-id="1"]'); + expect(nodeEl).not.toBeNull(); + expect(nodeEl?.textContent).toBe('Node 1'); + }); + + it('should render label node', () => { + const { container } = renderGraphPreview({ + nodes: [ + { + id: '2', + label: 'Node 2', + color: 'primary', + shape: 'label', + }, + ], + edges: [], + interactive: false, + }); + + const nodeEl = container.querySelector('[data-id="2"]'); + expect(nodeEl).not.toBeNull(); + expect(nodeEl?.textContent).toBe('Node 2'); + }); + + it('should render 2 nodes connected', () => { + const { container } = renderGraphPreview({ + nodes: [ + { + id: '1', + label: 'Node 1', + color: 'primary', + shape: 'hexagon', + }, + { + id: '2', + label: 'Node 2', + color: 'primary', + shape: 'label', + }, + ], + edges: [ + { + id: 'a(1)-b(2)', + color: 'primary', + source: '1', + target: '2', + }, + ], + interactive: false, + }); + + const srcNodeEl = container.querySelector('[data-id="1"]'); + expect(srcNodeEl).not.toBeNull(); + expect(srcNodeEl?.textContent).toBe('Node 1'); + + const targetNodeEl = container.querySelector('[data-id="2"]'); + expect(targetNodeEl).not.toBeNull(); + expect(targetNodeEl?.textContent).toBe('Node 2'); + + // TODO: Fix this test (currently it is not rendered in xyflow version 12) https://github.com/xyflow/xyflow/issues/716#issuecomment-2414721074 + // const edgeEl = container.querySelector('[data-id="a(1)-b(2)"]'); + // expect(edgeEl).not.toBeNull(); + }); +}); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.tsx new file mode 100644 index 0000000000000..eca9872d73897 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.tsx @@ -0,0 +1,204 @@ +/* + * 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, useRef, useState, useCallback } from 'react'; +import { + Background, + Controls, + Position, + ReactFlow, + useEdgesState, + useNodesState, +} from '@xyflow/react'; +import type { Edge, Node } from '@xyflow/react'; +import type { CommonProps } from '@elastic/eui'; +import { SvgDefsMarker } from '../edge/styles'; +import { + HexagonNode, + PentagonNode, + EllipseNode, + RectangleNode, + DiamondNode, + LabelNode, + EdgeGroupNode, +} from '../node'; +import { layoutGraph } from './layout_graph'; +import { DefaultEdge } from '../edge'; +import type { EdgeViewModel, NodeViewModel } from '../types'; + +import '@xyflow/react/dist/style.css'; + +export interface GraphProps extends CommonProps { + nodes: NodeViewModel[]; + edges: EdgeViewModel[]; + interactive: boolean; +} + +const nodeTypes = { + hexagon: HexagonNode, + pentagon: PentagonNode, + ellipse: EllipseNode, + rectangle: RectangleNode, + diamond: DiamondNode, + label: LabelNode, + group: EdgeGroupNode, +}; + +const edgeTypes = { + default: DefaultEdge, +}; + +/** + * Graph component renders a graph visualization using ReactFlow. + * It takes nodes and edges as input and provides interactive controls + * for panning, zooming, and manipulating the graph. + * + * @component + * @param {GraphProps} props - The properties for the Graph component. + * @param {NodeViewModel[]} props.nodes - Array of node view models to be rendered in the graph. + * @param {EdgeViewModel[]} props.edges - Array of edge view models to be rendered in the graph. + * @param {boolean} props.interactive - Flag to enable or disable interactivity (panning, zooming, etc.). + * @param {CommonProps} [props.rest] - Additional common properties. + * + * @returns {JSX.Element} The rendered Graph component. + */ +export const Graph: React.FC = ({ nodes, edges, interactive, ...rest }) => { + const layoutCalled = useRef(false); + const [isGraphLocked, setIsGraphLocked] = useState(interactive); + const { initialNodes, initialEdges } = useMemo( + () => processGraph(nodes, edges, isGraphLocked), + [nodes, edges, isGraphLocked] + ); + + const [nodesState, setNodes, onNodesChange] = useNodesState(initialNodes); + const [edgesState, _setEdges, onEdgesChange] = useEdgesState(initialEdges); + + if (!layoutCalled.current) { + const { nodes: layoutedNodes } = layoutGraph(nodesState, edgesState); + setNodes(layoutedNodes); + layoutCalled.current = true; + } + + const onInteractiveStateChange = useCallback( + (interactiveStatus: boolean): void => { + setIsGraphLocked(interactiveStatus); + setNodes((prevNodes) => + prevNodes.map((node) => ({ + ...node, + data: { + ...node.data, + interactive: interactiveStatus, + }, + })) + ); + }, + [setNodes] + ); + + return ( +
+ + { + window.requestAnimationFrame(() => xyflow.fitView()); + + // When the graph is not initialized as interactive, we need to fit the view on resize + if (!interactive) { + const resizeObserver = new ResizeObserver(() => { + xyflow.fitView(); + }); + resizeObserver.observe(document.querySelector('.react-flow') as Element); + return () => resizeObserver.disconnect(); + } + }} + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + nodes={nodesState} + edges={edgesState} + onNodesChange={onNodesChange} + onEdgesChange={onEdgesChange} + proOptions={{ hideAttribution: true }} + panOnDrag={isGraphLocked} + zoomOnScroll={isGraphLocked} + zoomOnPinch={isGraphLocked} + zoomOnDoubleClick={isGraphLocked} + preventScrolling={isGraphLocked} + nodesDraggable={interactive && isGraphLocked} + maxZoom={1.3} + > + {interactive && } + + +
+ ); +}; + +const processGraph = ( + nodesModel: NodeViewModel[], + edgesModel: EdgeViewModel[], + interactive: boolean +): { + initialNodes: Array>; + initialEdges: Array>; +} => { + const nodesById: { [key: string]: NodeViewModel } = {}; + + const initialNodes = nodesModel.map((nodeData) => { + nodesById[nodeData.id] = nodeData; + + const node: Node = { + id: nodeData.id, + type: nodeData.shape, + data: { ...nodeData, interactive }, + position: { x: 0, y: 0 }, // Default position, should be updated later + }; + + if (node.type === 'group' && nodeData.shape === 'group') { + node.sourcePosition = Position.Right; + node.targetPosition = Position.Left; + node.resizing = false; + node.focusable = false; + } else if (nodeData.shape === 'label' && nodeData.parentId) { + node.parentId = nodeData.parentId; + node.extent = 'parent'; + node.expandParent = false; + node.draggable = false; + } + + return node; + }); + + const initialEdges: Array> = edgesModel.map((edgeData) => { + const isIn = + nodesById[edgeData.source].shape !== 'label' && nodesById[edgeData.target].shape === 'group'; + const isInside = + nodesById[edgeData.source].shape === 'group' && nodesById[edgeData.target].shape === 'label'; + const isOut = + nodesById[edgeData.source].shape === 'label' && nodesById[edgeData.target].shape === 'group'; + const isOutside = + nodesById[edgeData.source].shape === 'group' && nodesById[edgeData.target].shape !== 'label'; + + return { + id: edgeData.id, + type: 'default', + source: edgeData.source, + sourceHandle: isInside ? 'inside' : isOutside ? 'outside' : undefined, + target: edgeData.target, + targetHandle: isIn ? 'in' : isOut ? 'out' : undefined, + focusable: false, + selectable: false, + data: { + ...edgeData, + sourceShape: nodesById[edgeData.source].shape, + targetShape: nodesById[edgeData.target].shape, + }, + }; + }); + + return { initialNodes, initialEdges }; +}; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/layout_graph.ts b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/layout_graph.ts index d9f637483c115..868461f99cdee 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/layout_graph.ts +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph/layout_graph.ts @@ -6,18 +6,16 @@ */ import Dagre from '@dagrejs/dagre'; -import type { - EdgeDataModel, - NodeDataModel, -} from '@kbn/cloud-security-posture-common/types/graph/latest'; -import type { NodeViewModel, Size } from '../types'; +import type { Node, Edge } from '@xyflow/react'; +import type { EdgeViewModel, NodeViewModel, Size } from '../types'; import { calcLabelSize } from './utils'; +import { GroupStyleOverride, NODE_HEIGHT, NODE_WIDTH } from '../node/styles'; export const layoutGraph = ( - nodes: NodeDataModel[], - edges: EdgeDataModel[] -): { nodes: NodeViewModel[] } => { - const nodesById: { [key: string]: NodeViewModel } = {}; + nodes: Array>, + edges: Array> +): { nodes: Array> } => { + const nodesById: { [key: string]: Node } = {}; const graphOpts = { compound: true, }; @@ -29,28 +27,27 @@ export const layoutGraph = ( edges.forEach((edge) => g.setEdge(edge.source, edge.target)); nodes.forEach((node) => { - let size = { width: 90, height: 90 }; - const position = { x: 0, y: 0 }; + let size = { width: NODE_WIDTH, height: node.measured?.height ?? NODE_HEIGHT }; - if (node.shape === 'label') { - size = calcLabelSize(node.label); + if (node.data.shape === 'label') { + size = calcLabelSize(node.data.label); // TODO: waiting for a fix: https://github.com/dagrejs/dagre/issues/238 // if (node.parentId) { // g.setParent(node.id, node.parentId); // } - } else if (node.shape === 'group') { + } else if (node.data.shape === 'group') { const res = layoutGroupChildren(node, nodes); size = res.size; res.children.forEach((child) => { - nodesById[child.id] = { ...child }; + nodesById[child.data.id] = child; }); } if (!nodesById[node.id]) { - nodesById[node.id] = { ...node, position }; + nodesById[node.id] = node; } g.setNode(node.id, { @@ -61,8 +58,8 @@ export const layoutGraph = ( Dagre.layout(g); - const nodesViewModel: NodeViewModel[] = nodes.map((nodeData) => { - const dagreNode = g.node(nodeData.id); + const layoutedNodes = nodes.map((node) => { + const dagreNode = g.node(node.data.id); // We are shifting the dagre node position (anchor=center center) to the top left // so it matches the React Flow node anchor point (top left). @@ -70,37 +67,43 @@ export const layoutGraph = ( const y = dagreNode.y - (dagreNode.height ?? 0) / 2; // For grouped nodes, we want to keep the original position relative to the parent - if (nodeData.shape === 'label' && nodeData.parentId) { + if (node.data.shape === 'label' && node.data.parentId) { return { - ...nodeData, - position: nodesById[nodeData.id].position, + ...node, + position: nodesById[node.data.id].position, }; - } else if (nodeData.shape === 'group') { + } else if (node.data.shape === 'group') { return { - ...nodeData, + ...node, position: { x, y }, - size: { + style: GroupStyleOverride({ width: dagreNode.width, height: dagreNode.height, - }, + }), + }; + } else if (node.data.shape === 'label') { + return { + ...node, + position: { x, y }, + }; + } else { + // Align nodes to labels by shifting the node position by it's label height + return { + ...node, + position: { x, y: y + (dagreNode.height - NODE_HEIGHT) / 2 }, }; } - - return { - ...nodeData, - position: { x, y }, - }; }); - return { nodes: nodesViewModel }; + return { nodes: layoutedNodes }; }; const layoutGroupChildren = ( - groupNode: NodeDataModel, - nodes: NodeDataModel[] -): { size: Size; children: NodeViewModel[] } => { + groupNode: Node, + nodes: Array> +): { size: Size; children: Array> } => { const children = nodes.filter( - (child) => child.shape === 'label' && child.parentId === groupNode.id + (child) => child.data.shape === 'label' && child.parentId === groupNode.id ); const STACK_VERTICAL_PADDING = 20; @@ -108,7 +111,7 @@ const layoutGroupChildren = ( const PADDING = 20; const stackSize = children.length; const allChildrenHeight = children.reduce( - (prevHeight, node) => prevHeight + calcLabelSize(node.label).height, + (prevHeight, node) => prevHeight + calcLabelSize(node.data.label).height, 0 ); const stackHeight = Math.max( @@ -118,23 +121,21 @@ const layoutGroupChildren = ( const space = (stackHeight - allChildrenHeight) / (stackSize - 1); const groupNodeWidth = children.reduce((acc, child) => { - const currLblWidth = PADDING * 2 + calcLabelSize(child.label).width; + const currLblWidth = PADDING * 2 + calcLabelSize(child.data.label).width; return Math.max(acc, currLblWidth); }, 0); // Layout children relative to parent - const positionedChildren: NodeViewModel[] = children.map((child, index) => { - const childSize = calcLabelSize(child.label); - const childPosition = { + children.forEach((child, index) => { + const childSize = calcLabelSize(child.data.label); + child.position = { x: groupNodeWidth / 2 - childSize.width / 2, y: index * (childSize.height * 2 + space), }; - - return { ...child, position: childPosition }; }); return { size: { width: groupNodeWidth, height: stackHeight }, - children: positionedChildren, + children, }; }; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/dagree_layout_graph.stories.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph_layout.stories.tsx similarity index 76% rename from x-pack/packages/kbn-cloud-security-posture/graph/src/components/dagree_layout_graph.stories.tsx rename to x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph_layout.stories.tsx index 94bc7e8af353b..140e81238d390 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/dagree_layout_graph.stories.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/graph_layout.stories.tsx @@ -7,68 +7,40 @@ import React from 'react'; import { ThemeProvider } from '@emotion/react'; -import { - ReactFlow, - Controls, - Background, - Node, - Edge, - Position, - useNodesState, - useEdgesState, -} from '@xyflow/react'; import { Story } from '@storybook/react'; -import type { - EdgeDataModel, - LabelNodeDataModel, - NodeDataModel, -} from '@kbn/cloud-security-posture-common/types/graph/latest'; import { Writable } from '@kbn/utility-types'; -import { - HexagonNode, - PentagonNode, - EllipseNode, - RectangleNode, - DiamondNode, - LabelNode, - EdgeGroupNode, -} from './node'; -import type { NodeViewModel } from './types'; -import { DefaultEdge } from './edge'; -import { SvgDefsMarker } from './edge/styles'; -import { GroupStyleOverride } from './node/styles'; - -import '@xyflow/react/dist/style.css'; -import { layoutGraph } from './graph/layout_graph'; +import { css } from '@emotion/react'; +import type { + EdgeViewModel, + LabelNodeViewModel, + NodeViewModel, + EntityNodeViewModel, + GroupNodeViewModel, +} from '.'; +import { Graph } from '.'; export default { - title: 'Components/Graph Components/Dagree Layout Graph', + title: 'Components/Graph Components/Graph Layout', description: 'CDR - Graph visualization', -}; - -const nodeTypes = { - hexagon: HexagonNode, - pentagon: PentagonNode, - ellipse: EllipseNode, - rectangle: RectangleNode, - diamond: DiamondNode, - label: LabelNode, - group: EdgeGroupNode, -}; - -const edgeTypes = { - default: DefaultEdge, + argTypes: { + interactive: { control: 'boolean', defaultValue: true }, + }, }; interface GraphData { - nodes: NodeDataModel[]; - edges: EdgeDataModel[]; + nodes: NodeViewModel[]; + edges: EdgeViewModel[]; interactive: boolean; } +type EnhancedNodeViewModel = + | EntityNodeViewModel + | GroupNodeViewModel + | (LabelNodeViewModel & { source: string; target: string }); + const extractEdges = ( - graphData: NodeDataModel[] -): { nodes: NodeDataModel[]; edges: EdgeDataModel[] } => { + graphData: EnhancedNodeViewModel[] +): { nodes: NodeViewModel[]; edges: EdgeViewModel[] } => { // Process nodes, transform nodes of id in the format of a(source)-b(target) to edges from a to label and from label to b // If there are multiple edges from a to b, create a parent node and group the labels under it. The parent node will be a group node. // Connect from a to the group node and from the group node to all the labels. and from the labels to the group again and from the group to b. @@ -77,14 +49,14 @@ const extractEdges = ( [key: string]: { source: string; target: string; edgesStacked: number; edges: string[] }; } = {}; const labelsMetadata: { - [key: string]: { source: string; target: string; labelsNodes: LabelNodeDataModel[] }; + [key: string]: { source: string; target: string; labelsNodes: LabelNodeViewModel[] }; } = {}; - const nodes: { [key: string]: NodeDataModel } = {}; - const edges: EdgeDataModel[] = []; + const nodes: { [key: string]: NodeViewModel } = {}; + const edges: EdgeViewModel[] = []; graphData.forEach((node) => { if (node.shape === 'label') { - const labelNode = { ...node, id: `${node.id}label(${node.label})` }; + const labelNode: LabelNodeViewModel = { ...node, id: `${node.id}label(${node.label})` }; const { source, target } = node; if (labelsMetadata[node.id]) { @@ -119,7 +91,7 @@ const extractEdges = ( Object.values(labelsMetadata).forEach((edge) => { if (edge.labelsNodes.length > 1) { - const groupNode: NodeDataModel = { + const groupNode: NodeViewModel = { id: `grp(a(${edge.source})-b(${edge.target}))`, shape: 'group', }; @@ -143,7 +115,7 @@ const extractEdges = ( color: edge.labelsNodes[0].color, }); - edge.labelsNodes.forEach((labelNode: Writable) => { + edge.labelsNodes.forEach((labelNode: Writable) => { labelNode.parentId = groupNode.id; edges.push({ @@ -189,28 +161,18 @@ const extractEdges = ( return { nodes: Object.values(nodes).reverse(), edges }; }; -const Template: Story = ({ nodes, edges }: GraphData) => { - const { initialNodes, initialEdges } = processGraph(nodes, edges); - - const [nodesState, _setNodes, onNodesChange] = useNodesState(initialNodes); - const [edgesState, _setEdges, onEdgesChange] = useEdgesState(initialEdges); - +const Template: Story = ({ nodes, edges, interactive }: GraphData) => { return ( - - - - - + ); }; @@ -359,8 +321,8 @@ GroupWithWarningAPIMock.args = { ], }; -export const Graph = Template.bind({}); -const baseGraph: NodeDataModel[] = [ +export const LargeGraph = Template.bind({}); +const baseGraph: EnhancedNodeViewModel[] = [ { id: 'siem-windows', label: '', @@ -483,7 +445,7 @@ const baseGraph: NodeDataModel[] = [ }, ]; -Graph.args = { +LargeGraph.args = { ...extractEdges(baseGraph), }; @@ -541,67 +503,3 @@ GraphStackedEdgeCases.args = { }, ]), }; - -function processGraph( - nodesModel: NodeDataModel[], - edgesModel: EdgeDataModel[] -): { - initialNodes: Node[]; - initialEdges: Edge[]; -} { - const { nodes: nodesViewModel } = layoutGraph(nodesModel, edgesModel); - - const nodesById: { [key: string]: NodeViewModel } = {}; - - const initialNodes = nodesViewModel.map((nodeData) => { - nodesById[nodeData.id] = nodeData; - - const node: Node = { - id: nodeData.id, - type: nodeData.shape, - data: { ...nodeData, interactive: true }, - position: nodeData.position, - draggable: true, - }; - - if (node.type === 'group' && nodeData.shape === 'group') { - node.sourcePosition = Position.Right; - node.targetPosition = Position.Left; - node.resizing = false; - node.style = GroupStyleOverride({ - width: nodeData.size?.width ?? 0, - height: nodeData.size?.height ?? 0, - }); - } else if (nodeData.shape === 'label' && nodeData.parentId) { - node.parentId = nodeData.parentId; - node.extent = 'parent'; - node.expandParent = false; - node.draggable = false; - } - - return node; - }); - - const initialEdges: Edge[] = edgesModel.map((edgeData) => { - const isIn = - nodesById[edgeData.source].shape !== 'label' && nodesById[edgeData.target].shape === 'group'; - const isInside = - nodesById[edgeData.source].shape === 'group' && nodesById[edgeData.target].shape === 'label'; - const isOut = - nodesById[edgeData.source].shape === 'label' && nodesById[edgeData.target].shape === 'group'; - const isOutside = - nodesById[edgeData.source].shape === 'group' && nodesById[edgeData.target].shape !== 'label'; - - return { - id: edgeData.id, - type: 'default', - source: edgeData.source, - sourceHandle: isInside ? 'inside' : isOutside ? 'outside' : undefined, - target: edgeData.target, - targetHandle: isIn ? 'in' : isOut ? 'out' : undefined, - data: { ...edgeData }, - }; - }); - - return { initialNodes, initialEdges }; -} diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/index.ts b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/index.ts index 1fec1c76430eb..5b2f8d71323bb 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/index.ts +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/index.ts @@ -4,3 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + +export { Graph } from './graph/graph'; +export type { GraphProps } from './graph/graph'; +export type { + NodeViewModel, + EdgeViewModel, + GroupNodeViewModel, + LabelNodeViewModel, + EntityNodeViewModel, + NodeProps, +} from './types'; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/mock/react_flow.ts b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/mock/react_flow.ts new file mode 100644 index 0000000000000..35282dedcc6de --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/mock/react_flow.ts @@ -0,0 +1,69 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ + +// Copied from https://reactflow.dev/learn/advanced-use/testing#using-jest + +// To make sure that the tests are working, it's important that you are using +// this implementation of ResizeObserver and DOMMatrixReadOnly +class ResizeObserver { + callback: globalThis.ResizeObserverCallback; + + constructor(callback: globalThis.ResizeObserverCallback) { + this.callback = callback; + } + + observe(target: Element) { + this.callback([{ target } as globalThis.ResizeObserverEntry], this); + } + + unobserve() {} + + disconnect() {} +} + +class DOMMatrixReadOnly { + m22: number; + constructor(transform: string) { + const scale = transform?.match(/scale\(([1-9.])\)/)?.[1]; + this.m22 = scale !== undefined ? +scale : 1; + } +} + +// Only run the shim once when requested +let init = false; + +export const mockReactFlow = () => { + if (init) return; + init = true; + + global.ResizeObserver = ResizeObserver; + + // @ts-ignore + global.DOMMatrixReadOnly = DOMMatrixReadOnly; + + Object.defineProperties(global.HTMLElement.prototype, { + offsetHeight: { + get() { + return parseFloat(this.style.height) || 1; + }, + }, + offsetWidth: { + get() { + return parseFloat(this.style.width) || 1; + }, + }, + }); + + (global.SVGElement as any).prototype.getBBox = () => ({ + x: 0, + y: 0, + width: 0, + height: 0, + }); +}; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/mock/test_providers.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/mock/test_providers.tsx new file mode 100644 index 0000000000000..3d07c1c6037ed --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/mock/test_providers.tsx @@ -0,0 +1,23 @@ +/* + * 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 { euiDarkVars } from '@kbn/ui-theme'; +import { ThemeProvider } from '@emotion/react'; + +interface Props { + children?: React.ReactNode; +} + +/** A utility for wrapping children in the providers required to run most tests */ +export const TestProvidersComponent: React.FC = ({ children }) => { + return ( + ({ eui: euiDarkVars, darkMode: true })}>{children} + ); +}; + +export const TestProviders = React.memo(TestProvidersComponent); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/button.stories.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/button.stories.tsx index 5e6e4cd37b432..4a034c05ee166 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/button.stories.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/button.stories.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ThemeProvider } from '@emotion/react'; import { Story } from '@storybook/react'; -import { NodeButton, type NodeButtonProps, NodeContainer } from './styles'; +import { NodeButton, type NodeButtonProps, NodeShapeContainer } from './styles'; export default { title: 'Components/Graph Components', @@ -20,10 +20,10 @@ export default { const Template: Story = (args) => ( - + Hover me - + ); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx index 76e7a3cd9eeeb..f96068061a433 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx @@ -10,7 +10,7 @@ import { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; import { Handle, Position } from '@xyflow/react'; import type { EntityNodeViewModel, NodeProps } from '../types'; import { - NodeContainer, + NodeShapeContainer, NodeLabel, NodeShapeOnHoverSvg, NodeShapeSvg, @@ -18,6 +18,7 @@ import { NodeButton, HandleStyleOverride, } from './styles'; +import { DiamondHoverShape, DiamondShape } from './shapes/diamond_shape'; const NODE_WIDTH = 99; const NODE_HEIGHT = 98; @@ -27,59 +28,55 @@ export const DiamondNode: React.FC = memo((props: NodeProps) => { props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); return ( - - {interactive && ( - + + {interactive && ( + + + + )} + - - - )} - - } + + {interactive && ( + expandButtonClick?.(e, props)} + x={`${NODE_WIDTH - NodeButton.ExpandButtonSize}px`} + y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2 - 4}px`} + /> + )} + - {icon && } - - {interactive && ( - expandButtonClick?.(e, props)} - x={`${NODE_WIDTH - NodeButton.ExpandButtonSize}px`} - y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2 - 4}px`} + - )} - - + {Boolean(label) ? label : id} - + ); }); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx index 3359905196b9d..987b9d7577812 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx @@ -9,7 +9,7 @@ import React, { memo } from 'react'; import { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; import { Handle, Position } from '@xyflow/react'; import { - NodeContainer, + NodeShapeContainer, NodeLabel, NodeShapeOnHoverSvg, NodeShapeSvg, @@ -18,6 +18,7 @@ import { HandleStyleOverride, } from './styles'; import type { EntityNodeViewModel, NodeProps } from '../types'; +import { EllipseHoverShape, EllipseShape } from './shapes/ellipse_shape'; const NODE_WIDTH = 90; const NODE_HEIGHT = 90; @@ -27,63 +28,55 @@ export const EllipseNode: React.FC = memo((props: NodeProps) => { props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); return ( - - {interactive && ( - + + {interactive && ( + + + + )} + - - - )} - - } + + {interactive && ( + expandButtonClick?.(e, props)} + x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 2}px`} + y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2}px`} + /> + )} + - {icon && } - - {interactive && ( - expandButtonClick?.(e, props)} - x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 2}px`} - y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2}px`} + - )} - - + {Boolean(label) ? label : id} - + ); }); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx index dee8df697c844..0bd8c33fc6484 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx @@ -9,7 +9,7 @@ import React, { memo } from 'react'; import { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; import { Handle, Position } from '@xyflow/react'; import { - NodeContainer, + NodeShapeContainer, NodeLabel, NodeShapeOnHoverSvg, NodeShapeSvg, @@ -18,6 +18,7 @@ import { HandleStyleOverride, } from './styles'; import type { EntityNodeViewModel, NodeProps } from '../types'; +import { HexagonHoverShape, HexagonShape } from './shapes/hexagon_shape'; const NODE_WIDTH = 87; const NODE_HEIGHT = 96; @@ -27,59 +28,55 @@ export const HexagonNode: React.FC = memo((props: NodeProps) => { props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); return ( - - {interactive && ( - + + {interactive && ( + + + + )} + - - - )} - - } + + {interactive && ( + expandButtonClick?.(e, props)} + x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 2 + 2}px`} + y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2 - 2}px`} + /> + )} + - {icon && } - - {interactive && ( - expandButtonClick?.(e, props)} - x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 2 + 2}px`} - y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2 - 2}px`} + - )} - - + {Boolean(label) ? label : id} - + ); }); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx index 74ea8c05b5940..f2282e9fa2d7d 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx @@ -10,7 +10,7 @@ import { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; import styled from '@emotion/styled'; import { Handle, Position } from '@xyflow/react'; import { - NodeContainer, + NodeShapeContainer, NodeLabel, NodeShapeOnHoverSvg, NodeShapeSvg, @@ -19,6 +19,7 @@ import { HandleStyleOverride, } from './styles'; import type { EntityNodeViewModel, NodeProps } from '../types'; +import { PentagonHoverShape, PentagonShape } from './shapes/pentagon_shape'; const PentagonShapeOnHover = styled(NodeShapeOnHoverSvg)` transform: translate(-50%, -51.5%); @@ -32,59 +33,55 @@ export const PentagonNode: React.FC = memo((props: NodeProps) => { props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); return ( - - {interactive && ( - + + {interactive && ( + + + + )} + - - - )} - - } + + {interactive && ( + expandButtonClick?.(e, props)} + x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 2}px`} + y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2}px`} + /> + )} + - {icon && } - - {interactive && ( - expandButtonClick?.(e, props)} - x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 2}px`} - y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize) / 2}px`} + - )} - - + {Boolean(label) ? label : id} - + ); }); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx index 22d9fbf25a4eb..7a5fc14855bc9 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx @@ -9,7 +9,7 @@ import React, { memo } from 'react'; import { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; import { Handle, Position } from '@xyflow/react'; import { - NodeContainer, + NodeShapeContainer, NodeLabel, NodeShapeOnHoverSvg, NodeShapeSvg, @@ -18,6 +18,7 @@ import { HandleStyleOverride, } from './styles'; import type { EntityNodeViewModel, NodeProps } from '../types'; +import { RectangleHoverShape, RectangleShape } from './shapes/rectangle_shape'; const NODE_WIDTH = 81; const NODE_HEIGHT = 80; @@ -27,67 +28,55 @@ export const RectangleNode: React.FC = memo((props: NodeProps) => { props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); return ( - - {interactive && ( - + + {interactive && ( + + + + )} + - - - )} - - } + + {interactive && ( + expandButtonClick?.(e, props)} + x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 4}px`} + y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize / 2) / 2}px`} + /> + )} + - {icon && } - - {interactive && ( - expandButtonClick?.(e, props)} - x={`${NODE_WIDTH - NodeButton.ExpandButtonSize / 4}px`} - y={`${(NODE_HEIGHT - NodeButton.ExpandButtonSize / 2) / 2}px`} + - )} - - + {Boolean(label) ? label : id} - + ); }); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/diamond_shape.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/diamond_shape.tsx new file mode 100644 index 0000000000000..126a5702cf5d0 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/diamond_shape.tsx @@ -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, { memo } from 'react'; +import type { HoverShapeProps, ShapeProps } from './types'; + +export const DiamondHoverShape: React.FC = memo(({ stroke }) => ( + +)); + +export const DiamondShape: React.FC = memo(({ stroke, fill }) => ( + +)); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/ellipse_shape.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/ellipse_shape.tsx new file mode 100644 index 0000000000000..18fe56440c5f5 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/ellipse_shape.tsx @@ -0,0 +1,17 @@ +/* + * 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, { memo } from 'react'; +import type { HoverShapeProps, ShapeProps } from './types'; + +export const EllipseHoverShape: React.FC = memo(({ stroke }) => ( + +)); + +export const EllipseShape: React.FC = memo(({ stroke, fill }) => ( + +)); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/hexagon_shape.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/hexagon_shape.tsx new file mode 100644 index 0000000000000..12aeebec88605 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/hexagon_shape.tsx @@ -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, { memo } from 'react'; +import type { HoverShapeProps, ShapeProps } from './types'; + +export const HexagonHoverShape: React.FC = memo(({ stroke }) => ( + +)); + +export const HexagonShape: React.FC = memo(({ stroke, fill }) => ( + +)); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/pentagon_shape.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/pentagon_shape.tsx new file mode 100644 index 0000000000000..f4d07fc69db79 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/pentagon_shape.tsx @@ -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, { memo } from 'react'; +import type { HoverShapeProps, ShapeProps } from './types'; + +export const PentagonHoverShape: React.FC = memo(({ stroke }) => ( + +)); + +export const PentagonShape: React.FC = memo(({ stroke, fill }) => ( + +)); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/rectangle_shape.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/rectangle_shape.tsx new file mode 100644 index 0000000000000..7d019ce331baf --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/rectangle_shape.tsx @@ -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, { memo } from 'react'; +import type { HoverShapeProps, ShapeProps } from './types'; + +export const RectangleHoverShape: React.FC = memo(({ stroke }) => ( + +)); + +export const RectangleShape: React.FC = memo(({ stroke, fill }) => ( + +)); diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/types.ts b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/types.ts new file mode 100644 index 0000000000000..0f6910258fed6 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/types.ts @@ -0,0 +1,15 @@ +/* + * 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. + */ + +export interface HoverShapeProps { + stroke: string; +} + +export interface ShapeProps { + stroke: string; + fill: string; +} diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx index f1bee0cf95e30..e76da737af0f9 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx @@ -23,6 +23,7 @@ export const LABEL_PADDING_X = 15; export const LABEL_BORDER_WIDTH = 1; export const NODE_WIDTH = 90; export const NODE_HEIGHT = 90; +const NODE_LABEL_WIDTH = 120; export const LabelNodeContainer = styled.div` text-wrap: nowrap; @@ -79,9 +80,13 @@ export const LabelShapeOnHover = styled.div` ${LabelNodeContainer}:hover & { opacity: 1; /* Show on hover */ } + + .react-flow__node:focus:focus-visible & { + opacity: 1; /* Show on hover */ + } `; -export const NodeContainer = styled.div` +export const NodeShapeContainer = styled.div` position: relative; width: ${NODE_WIDTH}px; height: ${NODE_HEIGHT}px; @@ -99,7 +104,11 @@ export const NodeShapeOnHoverSvg = styled(NodeShapeSvg)` opacity: 0; /* Hidden by default */ transition: opacity 0.2s ease; /* Smooth transition */ - ${NodeContainer}:hover & { + ${NodeShapeContainer}:hover & { + opacity: 1; /* Show on hover */ + } + + .react-flow__node:focus:focus-visible & { opacity: 1; /* Show on hover */ } `; @@ -124,11 +133,8 @@ export const NodeIcon = ({ icon, color, x, y }: NodeIconProps) => { }; export const NodeLabel = styled(EuiText)` - position: absolute; - top: 108%; - left: 50%; - transform: translateX(-50%); - width: 130%; + width: ${NODE_LABEL_WIDTH}px; + margin-left: ${-(NODE_LABEL_WIDTH - NODE_WIDTH) / 2}px; text-overflow: ellipsis; // white-space: nowrap; overflow: hidden; @@ -167,9 +173,17 @@ export const StyledNodeButton = styled.div` position: absolute; z-index: 1; - ${NodeContainer}:hover & { + ${NodeShapeContainer}:hover & { opacity: 1; /* Show on hover */ } + + &:has(button:focus) { + opacity: 1; /* Show when button is active */ + } + + .react-flow__node:focus:focus-visible & { + opacity: 1; /* Show on node focus */ + } `; export interface NodeButtonProps { diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/types.ts b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/types.ts index 262254c80afe3..27ec18f35f45b 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/src/components/types.ts +++ b/x-pack/packages/kbn-cloud-security-posture/graph/src/components/types.ts @@ -10,29 +10,17 @@ import type { GroupNodeDataModel, LabelNodeDataModel, EdgeDataModel, + NodeShape, } from '@kbn/cloud-security-posture-common/types/graph/latest'; import type { Node, NodeProps as xyNodeProps } from '@xyflow/react'; import type { Edge, EdgeProps as xyEdgeProps } from '@xyflow/react'; -export interface PositionXY { - x: number; - y: number; -} - export interface Size { width: number; height: number; } -export interface GraphMetadata { - nodes: { [key: string]: { edgesIn: number; edgesOut: number } }; - edges: { - [key: string]: { source: string; target: string; edgesStacked: number; edges: string[] }; - }; -} - interface BaseNodeDataViewModel { - position: PositionXY; interactive?: boolean; } @@ -46,9 +34,7 @@ export interface EntityNodeViewModel export interface GroupNodeViewModel extends Record, GroupNodeDataModel, - BaseNodeDataViewModel { - size?: Size; -} + BaseNodeDataViewModel {} export interface LabelNodeViewModel extends Record, @@ -61,10 +47,13 @@ export type NodeViewModel = EntityNodeViewModel | GroupNodeViewModel | LabelNode export type NodeProps = xyNodeProps>; -export interface EdgeViewModel extends Record, EdgeDataModel { - graphMetadata?: GraphMetadata; - interactive?: boolean; - onClick?: (e: React.MouseEvent, edge: EdgeProps) => void; -} +export interface EdgeViewModel extends Record, EdgeDataModel {} -export type EdgeProps = xyEdgeProps>; +export type EdgeProps = xyEdgeProps< + Edge< + EdgeViewModel & { + sourceShape: NodeShape; + targetShape: NodeShape; + } + > +>; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/main.ts b/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/main.ts deleted file mode 100644 index 186e1a2a76bed..0000000000000 --- a/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/main.ts +++ /dev/null @@ -1,44 +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 { defaultConfig } from '@kbn/storybook'; -import { Configuration } from 'webpack'; - -module.exports = { - ...defaultConfig, - stories: ['../../**/*.stories.+(tsx|mdx)'], - reactOptions: { - strictMode: true, - }, - webpack: (config: Configuration) => { - config.module?.rules.push({ - test: /\.js$/, - include: /node_modules[\\\/]@dagrejs/, - use: { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env'], - plugins: ['@babel/plugin-proposal-class-properties'], - }, - }, - }); - config.module?.rules.push({ - test: /node_modules[\/\\]@?xyflow[\/\\].*.js$/, - loaders: 'babel-loader', - options: { - presets: [['@babel/preset-env', { modules: false }], '@babel/preset-react'], - plugins: [ - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - '@babel/plugin-transform-logical-assignment-operators', - ], - }, - }); - - return config; - }, -}; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json index d97809a59772d..d0056e29e6784 100644 --- a/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json +++ b/x-pack/packages/kbn-cloud-security-posture/graph/tsconfig.json @@ -13,6 +13,6 @@ "kbn_references": [ "@kbn/cloud-security-posture-common", "@kbn/utility-types", - "@kbn/storybook" + "@kbn/ui-theme", ] } diff --git a/x-pack/packages/kbn-cloud-security-posture/public/kibana.jsonc b/x-pack/packages/kbn-cloud-security-posture/public/kibana.jsonc index 4c5a4f1f0165d..811a1ab5dad41 100644 --- a/x-pack/packages/kbn-cloud-security-posture/public/kibana.jsonc +++ b/x-pack/packages/kbn-cloud-security-posture/public/kibana.jsonc @@ -1,5 +1,5 @@ { - "id": "@kbn/cloud-security-posture", - "owner": "@elastic/kibana-cloud-security-posture", - "type": "shared-browser" -} \ No newline at end of file + "id": "@kbn/cloud-security-posture", + "owner": "@elastic/kibana-cloud-security-posture", + "type": "shared-browser" +} diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/README.mdx b/x-pack/packages/kbn-cloud-security-posture/storybook/config/README.mdx new file mode 100644 index 0000000000000..ab9ce5263ec69 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/storybook/config/README.mdx @@ -0,0 +1,3 @@ +# @kbn/cloud-security-posture-storybook-config + +Storybook configuration used by `yarn storybook`. Refer to `@kbn/storybook` for more information. diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/constants.ts b/x-pack/packages/kbn-cloud-security-posture/storybook/config/constants.ts similarity index 100% rename from x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/constants.ts rename to x-pack/packages/kbn-cloud-security-posture/storybook/config/constants.ts diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/index.ts b/x-pack/packages/kbn-cloud-security-posture/storybook/config/index.ts similarity index 100% rename from x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/index.ts rename to x-pack/packages/kbn-cloud-security-posture/storybook/config/index.ts diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/main.ts b/x-pack/packages/kbn-cloud-security-posture/storybook/config/main.ts new file mode 100644 index 0000000000000..4e7fca030c2f6 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/storybook/config/main.ts @@ -0,0 +1,16 @@ +/* + * 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 { defaultConfig } from '@kbn/storybook'; + +module.exports = { + ...defaultConfig, + stories: ['../../**/*.stories.+(tsx|mdx)'], + reactOptions: { + strictMode: true, + }, +}; diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/manager.ts b/x-pack/packages/kbn-cloud-security-posture/storybook/config/manager.ts similarity index 100% rename from x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/manager.ts rename to x-pack/packages/kbn-cloud-security-posture/storybook/config/manager.ts diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/package.json b/x-pack/packages/kbn-cloud-security-posture/storybook/config/package.json new file mode 100644 index 0000000000000..4ba8c8fd3d57f --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/storybook/config/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/cloud-security-posture-storybook-config", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/preview.ts b/x-pack/packages/kbn-cloud-security-posture/storybook/config/preview.ts similarity index 100% rename from x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/preview.ts rename to x-pack/packages/kbn-cloud-security-posture/storybook/config/preview.ts diff --git a/x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/styles.css b/x-pack/packages/kbn-cloud-security-posture/storybook/config/styles.css similarity index 100% rename from x-pack/packages/kbn-cloud-security-posture/graph/storybook/config/styles.css rename to x-pack/packages/kbn-cloud-security-posture/storybook/config/styles.css diff --git a/x-pack/packages/kbn-cloud-security-posture/storybook/config/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/storybook/config/tsconfig.json new file mode 100644 index 0000000000000..1f8b2275f5191 --- /dev/null +++ b/x-pack/packages/kbn-cloud-security-posture/storybook/config/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "@kbn/ambient-storybook-types", + ] + }, + "include": [ + "**/*.ts" + ], + "kbn_references": [ + "@kbn/storybook", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.tsx b/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.tsx index 4f1890e58554f..383bbbb341c8e 100644 --- a/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.tsx +++ b/x-pack/packages/security-solution/common/src/flyout/common/components/expandable_panel.tsx @@ -22,7 +22,7 @@ import { EuiSkeletonText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { IconType } from '@elastic/eui'; +import type { EuiPanelProps, IconType } from '@elastic/eui'; import { css } from '@emotion/react'; export interface ExpandablePanelPanelProps { @@ -59,6 +59,10 @@ export interface ExpandablePanelPanelProps { * Returns a null component if true */ error?: boolean; + /** + * Content's padding size + */ + paddingSize?: EuiPanelProps['paddingSize']; }; expand?: { /** @@ -84,7 +88,11 @@ export interface ExpandablePanelPanelProps { */ export const ExpandablePanel: FC> = ({ header: { title, link, iconType, headerContent }, - content: { loading, error } = { loading: false, error: false }, + content: { loading, error, paddingSize: contentPaddingSize } = { + loading: false, + error: false, + paddingSize: 'm', + }, expand: { expandable, expandedOnFirstRender } = { expandable: false, expandedOnFirstRender: false, @@ -228,7 +236,9 @@ export const ExpandablePanel: FC> = {showContent && ( - {content} + + {content} + )} diff --git a/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts b/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts index eb372a2bfea4f..5102d153c1905 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts @@ -6,6 +6,7 @@ */ import { castArray } from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; import type { Logger, IScopedClusterClient } from '@kbn/core/server'; import type { EdgeDataModel, @@ -13,10 +14,9 @@ import type { EntityNodeDataModel, LabelNodeDataModel, GroupNodeDataModel, - NodeShape, } from '@kbn/cloud-security-posture-common/types/graph/latest'; import type { EsqlToRecords } from '@elastic/elasticsearch/lib/helpers'; -import type { Writeable } from '@kbn/zod'; +import type { Writable } from '@kbn/utility-types'; import type { GraphContextServices, GraphContext } from './types'; interface GraphEdge { @@ -31,6 +31,11 @@ interface GraphEdge { isAlert: boolean; } +interface LabelEdges { + source: string; + target: string; +} + export const getGraph = async ( services: GraphContextServices, query: { @@ -63,28 +68,29 @@ export const getGraph = async ( interface ParseContext { nodesMap: Record; - edgeLabelsNodes: Record; edgesMap: Record; + edgeLabelsNodes: Record; + labelEdges: Record; } const parseRecords = (logger: Logger, records: GraphEdge[]): GraphContext => { - const nodesMap: Record = {}; - const edgeLabelsNodes: Record = {}; - const edgesMap: Record = {}; + const ctx: ParseContext = { nodesMap: {}, edgeLabelsNodes: {}, edgesMap: {}, labelEdges: {} }; logger.trace(`Parsing records [length: ${records.length}]`); - createNodes(logger, records, { nodesMap, edgeLabelsNodes }); - createEdgesAndGroups(logger, { edgeLabelsNodes, edgesMap, nodesMap }); + createNodes(logger, records, ctx); + createEdgesAndGroups(logger, ctx); logger.trace( - `Parsed [nodes: ${Object.keys(nodesMap).length}, edges: ${Object.keys(edgesMap).length}]` + `Parsed [nodes: ${Object.keys(ctx.nodesMap).length}, edges: ${ + Object.keys(ctx.edgesMap).length + }]` ); // Sort groups to be first (fixes minor layout issue) - const nodes = sortNodes(nodesMap); + const nodes = sortNodes(ctx.nodesMap); - return { nodes, edges: Object.values(edgesMap) }; + return { nodes, edges: Object.values(ctx.edgesMap) }; }; const fetchGraph = async ({ @@ -168,12 +174,21 @@ const createNodes = ( records: GraphEdge[], context: Omit ) => { - const { nodesMap, edgeLabelsNodes } = context; + const { nodesMap, edgeLabelsNodes, labelEdges } = context; for (const record of records) { const { ips, hosts, users, actorIds, action, targetIds, isAlert, eventOutcome } = record; const actorIdsArray = castArray(actorIds); const targetIdsArray = castArray(targetIds); + const unknownTargets: string[] = []; + + // Ensure all targets has an id (target can return null from the query) + targetIdsArray.forEach((id, idx) => { + if (!id) { + targetIdsArray[idx] = `unknown ${uuidv4()}`; + unknownTargets.push(targetIdsArray[idx]); + } + }); logger.trace( `Parsing record [actorIds: ${actorIdsArray.join( @@ -186,7 +201,7 @@ const createNodes = ( if (nodesMap[id] === undefined) { nodesMap[id] = { id, - label: id, + label: unknownTargets.includes(id) ? 'Unknown' : undefined, color: isAlert ? 'danger' : 'primary', ...determineEntityNodeShape(id, ips ?? [], hosts ?? [], users ?? []), }; @@ -204,19 +219,18 @@ const createNodes = ( edgeLabelsNodes[edgeId] = []; } - const labelNode = { + const labelNode: LabelNodeDataModel = { id: edgeId + `label(${action})outcome(${eventOutcome})`, label: action, - source: actorId, - target: targetId, color: isAlert ? 'danger' : eventOutcome === 'failed' ? 'warning' : 'primary', shape: 'label', - } as LabelNodeDataModel; + }; logger.trace(`Creating label node [${labelNode.id}]`); nodesMap[labelNode.id] = labelNode; edgeLabelsNodes[edgeId].push(labelNode.id); + labelEdges[labelNode.id] = { source: actorId, target: targetId }; } } } @@ -265,7 +279,7 @@ const sortNodes = (nodesMap: Record) => { }; const createEdgesAndGroups = (logger: Logger, context: ParseContext) => { - const { edgeLabelsNodes, edgesMap, nodesMap } = context; + const { edgeLabelsNodes, edgesMap, nodesMap, labelEdges } = context; Object.entries(edgeLabelsNodes).forEach(([edgeId, edgeLabelsIds]) => { // When there's more than one edge label, create a group node @@ -276,9 +290,9 @@ const createEdgesAndGroups = (logger: Logger, context: ParseContext) => { logger, edgesMap, nodesMap, - (nodesMap[edgeLabelId] as LabelNodeDataModel).source, + labelEdges[edgeLabelId].source, edgeLabelId, - (nodesMap[edgeLabelId] as LabelNodeDataModel).target + labelEdges[edgeLabelId].target ); } else { const groupNode: GroupNodeDataModel = { @@ -291,13 +305,13 @@ const createEdgesAndGroups = (logger: Logger, context: ParseContext) => { logger, edgesMap, nodesMap, - (nodesMap[edgeLabelsIds[0]] as LabelNodeDataModel).source, + labelEdges[edgeLabelsIds[0]].source, groupNode.id, - (nodesMap[edgeLabelsIds[0]] as LabelNodeDataModel).target + labelEdges[edgeLabelsIds[0]].target ); edgeLabelsIds.forEach((edgeLabelId) => { - (nodesMap[edgeLabelId] as Writeable).parentId = groupNode.id; + (nodesMap[edgeLabelId] as Writable).parentId = groupNode.id; connectEntitiesAndLabelNode( logger, edgesMap, @@ -332,7 +346,7 @@ const connectNodes = ( nodesMap: Record, sourceNodeId: string, targetNodeId: string -) => { +): EdgeDataModel => { const sourceNode = nodesMap[sourceNodeId]; const targetNode = nodesMap[targetNodeId]; const color = @@ -345,9 +359,7 @@ const connectNodes = ( return { id: `a(${sourceNodeId})-b(${targetNodeId})`, source: sourceNodeId, - sourceShape: nodesMap[sourceNodeId].shape as NodeShape, target: targetNodeId, - targetShape: nodesMap[targetNodeId].shape as NodeShape, color, - } as EdgeDataModel; + }; }; diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index 456035c9112d1..f3883e0fc43c6 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -66,7 +66,6 @@ "@kbn/cloud-security-posture-common", "@kbn/cloud-security-posture", "@kbn/analytics", - "@kbn/zod" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/fleet/README.md b/x-pack/plugins/fleet/README.md index fe61ca1d7cc51..d033e857b5ebd 100644 --- a/x-pack/plugins/fleet/README.md +++ b/x-pack/plugins/fleet/README.md @@ -32,6 +32,7 @@ In addition, it is typically needed to set up a Fleet Server and enroll Elastic - [Running a local Fleet Server and enrolling Elastic Agents](dev_docs/local_setup/enrolling_agents.md) for developing Kibana in stateful (not serverless) mode - [Developing Kibana in serverless mode](dev_docs/local_setup/developing_kibana_in_serverless.md) for developing Kibana in serverless mode - [Developing Kibana and Fleet Server simultaneously](dev_docs/local_setup/developing_kibana_and_fleet_server.md) for doing simultaneous Kibana and Fleet Server development +- [Testing agentless integrations](dev_docs/local_setup/agentless.md) ### Running Fleet locally in stateful mode diff --git a/x-pack/plugins/fleet/dev_docs/local_setup/agentless.md b/x-pack/plugins/fleet/dev_docs/local_setup/agentless.md new file mode 100644 index 0000000000000..a6f62a29a7179 --- /dev/null +++ b/x-pack/plugins/fleet/dev_docs/local_setup/agentless.md @@ -0,0 +1,28 @@ +# Testing agentless integrations + +Integrations have two possible deployment modes: +* on user-managed agents (most cases) +* on internally managed agents: these are called agentless + +## Kibana config + +Agentless integrations are available in ESS and Serverless, so in order to test or develop these in a local environment, the config should emulate either of these. + +At the time of writing, this can be achieved by adding the following to your `kibana.dev.yml`: +``` +# Emulate cloud +xpack.cloud.id: "123456789" + +# Enable agentless experimental feature flag in Fleet +xpack.fleet.enableExperimental: ['agentless'] +# Agentless Fleet config +xpack.fleet.agentless.enabled: true +xpack.fleet.agentless.api.url: 'https://api.agentless.url/api/v1/ess' +xpack.fleet.agentless.api.tls.certificate: './config/node.crt' +xpack.fleet.agentless.api.tls.key: './config/node.key' +xpack.fleet.agentless.api.tls.ca: './config/ca.crt' +``` + +## Which integrations to test with? + +At the time of writing, the Elastic Connectors integration is [agentless only](https://github.com/elastic/integrations/blob/2ebdf0cada6faed352e71a82cf71487672f27bf2/packages/elastic_connectors/manifest.yml#L35-L39) and the Cloud Security Posture Management (CSPM) integration offers both agent-based and agentless deployment modes. These are still in technical preview, so "Display beta integrations" should be checked. diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts index 38663d88e5b23..bd57576a4cafa 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts @@ -227,6 +227,74 @@ describe('useSetupTechnology', () => { expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); }); + it('should set the default selected setup technology to agent-based when creating a non agentless-only package policy', async () => { + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + api: { + url: 'https://agentless.api.url', + }, + }, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isCloudEnabled: true, + }, + }); + + const { result } = renderHook(() => + useSetupTechnology({ + setNewAgentPolicy, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicies: updateAgentPoliciesMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + packageInfo: packageInfoMock, + packagePolicy: packagePolicyMock, + }) + ); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + }); + + it('should set the default selected setup technology to agentless when creating an agentless-only package policy', async () => { + (useConfig as MockFn).mockReturnValue({ + agentless: { + enabled: true, + api: { + url: 'https://agentless.api.url', + }, + }, + } as any); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isCloudEnabled: true, + }, + }); + const agentlessOnlyPackageInfoMock = { + policy_templates: [ + { + deployment_modes: { + default: { enabled: false }, + agentless: { enabled: true }, + }, + }, + ], + } as PackageInfo; + + const { result } = renderHook(() => + useSetupTechnology({ + setNewAgentPolicy, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicies: updateAgentPoliciesMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + packageInfo: agentlessOnlyPackageInfoMock, + packagePolicy: packagePolicyMock, + }) + ); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENTLESS); + }); + it('should fetch agentless policy if agentless feature is enabled and isServerless is true', async () => { const { waitForNextUpdate } = renderHook(() => useSetupTechnology({ @@ -336,8 +404,12 @@ describe('useSetupTechnology', () => { }) ); + await rerender(); + expect(generateNewAgentPolicyWithDefaults).toHaveBeenCalled(); + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + act(() => { result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts index 2a88fecc6b145..465a7241b3ad3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useConfig } from '../../../../../hooks'; import { ExperimentalFeaturesService } from '../../../../../services'; @@ -28,6 +28,7 @@ import { import { isAgentlessIntegration as isAgentlessIntegrationFn, getAgentlessAgentPolicyNameFromPackagePolicyName, + isOnlyAgentlessIntegration, } from '../../../../../../../../common/services/agentless_policy_helper'; export const useAgentless = () => { @@ -97,9 +98,13 @@ export function useSetupTechnology({ // this is a placeholder for the new agent-BASED policy that will be used when the user switches from agentless to agent-based and back const newAgentBasedPolicy = useRef(newAgentPolicy); - const [selectedSetupTechnology, setSelectedSetupTechnology] = useState( - SetupTechnology.AGENT_BASED - ); + const defaultSetupTechnology = useMemo(() => { + return isOnlyAgentlessIntegration(packageInfo) + ? SetupTechnology.AGENTLESS + : SetupTechnology.AGENT_BASED; + }, [packageInfo]); + const [selectedSetupTechnology, setSelectedSetupTechnology] = + useState(defaultSetupTechnology); const [newAgentlessPolicy, setNewAgentlessPolicy] = useState(() => { const agentless = generateNewAgentPolicyWithDefaults({ inactivity_timeout: 3600, @@ -154,6 +159,13 @@ export function useSetupTechnology({ } }, [isDefaultAgentlessPolicyEnabled]); + useEffect(() => { + if (isEditPage) { + return; + } + setSelectedSetupTechnology(defaultSetupTechnology); + }, [packageInfo, defaultSetupTechnology, isEditPage]); + const handleSetupTechnologyChange = useCallback( (setupTechnology: SetupTechnology, policyTemplateName?: string) => { if (!isAgentlessEnabled || setupTechnology === selectedSetupTechnology) { diff --git a/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts index 4f1c7b46d06be..c9a97c0d170a0 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts @@ -189,7 +189,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: false, + hide: true, previewIcon: IconChartMetric, score: 0.51, }, @@ -221,7 +221,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: false, + hide: true, previewIcon: IconChartMetric, score: 0.51, }, @@ -294,7 +294,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: false, + hide: true, previewIcon: IconChartMetric, score: 0.52, }, @@ -326,7 +326,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: false, + hide: true, previewIcon: IconChartMetric, score: 0.52, }, @@ -357,7 +357,7 @@ describe('metric suggestions', () => { breakdownByAccessor: bucketColumn.columnId, }, title: 'Metric', - hide: false, + hide: true, previewIcon: IconChartMetric, score: 0.52, }, diff --git a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts index 877d2b45d66af..5d066318d734f 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts @@ -30,8 +30,6 @@ export const getSuggestions: Visualization['getSuggest const bucketedColumns = table.columns.filter(({ operation }) => operation.isBucketed); - const hasInterval = bucketedColumns.some(({ operation }) => operation.scale === 'interval'); - const unsupportedColumns = table.columns.filter( ({ operation }) => !supportedDataTypes.has(operation.dataType) && !operation.isBucketed ); @@ -64,7 +62,7 @@ export const getSuggestions: Visualization['getSuggest title: metricColumns[0]?.operation.label || metricLabel, previewIcon: IconChartMetric, score: 0.5, - hide: hasInterval, + hide: !!bucketedColumns.length, }; const accessorMappings: Pick = diff --git a/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx b/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx index 9c2ea13cf753e..d3d28fe040198 100644 --- a/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx +++ b/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx @@ -17,7 +17,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { HttpStart } from '@kbn/core-http-browser'; import { action } from '@storybook/addon-actions'; import type { InventoryKibanaContext } from '../public/hooks/use_kibana'; -import type { ITelemetryClient } from '../public/services/telemetry/types'; +import { ITelemetryClient } from '../public/services/telemetry/types'; export function getMockInventoryContext(): InventoryKibanaContext { const coreStart = coreMock.createStart(); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx index f3488dfddbc4e..982a616da8fda 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entity_name/index.tsx @@ -25,13 +25,22 @@ interface EntityNameProps { } export function EntityName({ entity }: EntityNameProps) { - const { services } = useKibana(); + const { + services: { telemetry, share }, + } = useKibana(); const assetDetailsLocator = - services.share?.url.locators.get(ASSET_DETAILS_LOCATOR_ID); + share?.url.locators.get(ASSET_DETAILS_LOCATOR_ID); const serviceOverviewLocator = - services.share?.url.locators.get('serviceOverviewLocator'); + share?.url.locators.get('serviceOverviewLocator'); + + const handleLinkClick = useCallback(() => { + telemetry.reportEntityViewClicked({ + view_type: 'detail', + entity_type: entity['entity.type'], + }); + }, [entity, telemetry]); const getEntityRedirectUrl = useCallback(() => { const type = entity[ENTITY_TYPE]; @@ -58,7 +67,12 @@ export function EntityName({ entity }: EntityNameProps) { }, [entity, assetDetailsLocator, serviceOverviewLocator]); return ( - + // eslint-disable-next-line @elastic/eui/href-or-on-click + diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx index 6d65669c61651..e3c0d24837f91 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx @@ -84,12 +84,13 @@ export function EntitiesGrid({ } const columnEntityTableId = columnId as EntityColumnIds; + const entityType = entity[ENTITY_TYPE]; + switch (columnEntityTableId) { case 'alertsCount': return entity?.alertsCount ? : null; case ENTITY_TYPE: - const entityType = entity[columnEntityTableId]; return ( @@ -36,7 +37,7 @@ const INVENTORY_FEEDBACK_LINK = 'https://ela.st/feedback-new-inventory'; export function InventoryPageTemplate({ children }: { children: React.ReactNode }) { const { - services: { observabilityShared, inventoryAPIClient, kibanaEnvironment }, + services: { observabilityShared, inventoryAPIClient, kibanaEnvironment, telemetry }, } = useKibana(); const { PageTemplate: ObservabilityPageTemplate } = observabilityShared.navigation; @@ -62,6 +63,23 @@ export function InventoryPageTemplate({ children }: { children: React.ReactNode [inventoryAPIClient] ); + const isLoadingComplete = useIsLoadingComplete({ + loadingStates: [isEnablementLoading, hasDataLoading], + }); + + useEffect(() => { + if (isLoadingComplete) { + const viewState = isEntityManagerEnabled + ? value.hasData + ? 'populated' + : 'empty' + : 'eem_disabled'; + telemetry.reportEntityInventoryViewed({ + view_state: viewState, + }); + } + }, [isEntityManagerEnabled, value.hasData, telemetry, isLoadingComplete]); + if (isEnablementLoading || hasDataLoading) { return ( { + telemetry.reportEntityInventorySearchQuerySubmitted({ + kuery_fields: getKqlFieldsWithFallback(searchQuery?.query as string), + entity_types: searchEntityTypes || [], + action: searchIsUpdate ? 'submit' : 'refresh', + }); + }, + [telemetry] + ); + + const registerEntityTypeFilteredEvent = useCallback( + ({ filterEntityTypes, filterKuery }: { filterEntityTypes: string[]; filterKuery?: string }) => { + telemetry.reportEntityInventoryEntityTypeFiltered({ + entity_types: filterEntityTypes, + kuery_fields: filterKuery ? getKqlFieldsWithFallback(filterKuery) : [], + }); + }, + [telemetry] + ); + const handleEntityTypesChange = useCallback( (nextEntityTypes: string[]) => { searchBarContentSubject$.next({ kuery, entityTypes: nextEntityTypes, refresh: false }); + registerEntityTypeFilteredEvent({ filterEntityTypes: nextEntityTypes, filterKuery: kuery }); }, - [kuery, searchBarContentSubject$] + [kuery, registerEntityTypeFilteredEvent, searchBarContentSubject$] ); const handleQuerySubmit = useCallback>( @@ -64,8 +97,14 @@ export function SearchBar() { entityTypes, refresh: !isUpdate, }); + + registerSearchSubmittedEvent({ + searchQuery: query, + searchEntityTypes: entityTypes, + searchIsUpdate: isUpdate, + }); }, - [entityTypes, searchBarContentSubject$] + [entityTypes, registerSearchSubmittedEvent, searchBarContentSubject$] ); return ( diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_is_loading_complete.test.ts b/x-pack/plugins/observability_solution/inventory/public/hooks/use_is_loading_complete.test.ts new file mode 100644 index 0000000000000..61306a0b66a3b --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_is_loading_complete.test.ts @@ -0,0 +1,109 @@ +/* + * 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 { renderHook } from '@testing-library/react-hooks'; +import { useIsLoadingComplete } from './use_is_loading_complete'; + +describe('useIsLoadingComplete', () => { + describe('initialization', () => { + it('should initialize with undefined', () => { + const { result } = renderHook(() => useIsLoadingComplete({ loadingStates: [false, false] })); + expect(result.current).toBeUndefined(); + }); + + it('should handle an empty array of loadingStates', () => { + const { result } = renderHook(() => useIsLoadingComplete({ loadingStates: [] })); + expect(result.current).toBeUndefined(); + }); + + it('should handle a single loading state that is false', () => { + const { result } = renderHook(() => useIsLoadingComplete({ loadingStates: [false] })); + expect(result.current).toBeUndefined(); + }); + }); + + describe('loading states', () => { + it('should set isLoadingComplete to false when some loadingStates are true', () => { + const { result } = renderHook(() => useIsLoadingComplete({ loadingStates: [true, false] })); + expect(result.current).toBe(false); + }); + + it('should set isLoadingComplete to false when all loadingStates are true', () => { + const { result } = renderHook(() => useIsLoadingComplete({ loadingStates: [true, true] })); + expect(result.current).toBe(false); + }); + + it('should handle a single loading state that is true', () => { + const { result } = renderHook(() => useIsLoadingComplete({ loadingStates: [true] })); + expect(result.current).toBe(false); + }); + }); + + describe('loading completion', () => { + it('should set isLoadingComplete to true when all loadingStates are false after being true', () => { + const { result, rerender } = renderHook( + ({ loadingStates }) => useIsLoadingComplete({ loadingStates }), + { + initialProps: { loadingStates: [true, false] }, + } + ); + + expect(result.current).toBe(false); + + rerender({ loadingStates: [false, false] }); + + expect(result.current).toBe(true); + }); + + it('should set isLoadingComplete to true when all loadingStates are false after being mixed', () => { + const { result, rerender } = renderHook( + ({ loadingStates }) => useIsLoadingComplete({ loadingStates }), + { + initialProps: { loadingStates: [true, false] }, + } + ); + + expect(result.current).toBe(false); + + rerender({ loadingStates: [false, false] }); + + expect(result.current).toBe(true); + }); + }); + + describe('mixed states', () => { + it('should not change isLoadingComplete if loadingStates are mixed', () => { + const { result, rerender } = renderHook( + ({ loadingStates }) => useIsLoadingComplete({ loadingStates }), + { + initialProps: { loadingStates: [true, true] }, + } + ); + + expect(result.current).toBe(false); + + rerender({ loadingStates: [true, false] }); + + expect(result.current).toBe(false); + }); + + it('should not change isLoadingComplete if loadingStates change from all true to mixed', () => { + const { result, rerender } = renderHook( + ({ loadingStates }) => useIsLoadingComplete({ loadingStates }), + { + initialProps: { loadingStates: [true, true] }, + } + ); + + expect(result.current).toBe(false); + + rerender({ loadingStates: [true, false] }); + + expect(result.current).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_is_loading_complete.ts b/x-pack/plugins/observability_solution/inventory/public/hooks/use_is_loading_complete.ts new file mode 100644 index 0000000000000..76b863efaeceb --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_is_loading_complete.ts @@ -0,0 +1,29 @@ +/* + * 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 { useState, useEffect } from 'react'; + +interface UseIsLoadingCompleteProps { + loadingStates: boolean[]; +} + +export const useIsLoadingComplete = ({ loadingStates }: UseIsLoadingCompleteProps) => { + const [isLoadingComplete, setIsLoadingComplete] = useState(undefined); + + useEffect(() => { + const someLoading = loadingStates.some((loading) => loading); + const allLoaded = loadingStates.every((loading) => !loading); + + if (isLoadingComplete === undefined && someLoading) { + setIsLoadingComplete(false); + } else if (isLoadingComplete === false && allLoaded) { + setIsLoadingComplete(true); + } + }, [isLoadingComplete, loadingStates]); + + return isLoadingComplete; +}; diff --git a/x-pack/plugins/observability_solution/inventory/public/plugin.ts b/x-pack/plugins/observability_solution/inventory/public/plugin.ts index b6771d2f95550..109123859d4ca 100644 --- a/x-pack/plugins/observability_solution/inventory/public/plugin.ts +++ b/x-pack/plugins/observability_solution/inventory/public/plugin.ts @@ -49,6 +49,7 @@ export class InventoryPlugin this.kibanaVersion = context.env.packageInfo.version; this.isServerlessEnv = context.env.packageInfo.buildFlavor === 'serverless'; } + setup( coreSetup: CoreSetup, pluginsSetup: InventorySetupDependencies @@ -58,6 +59,13 @@ export class InventoryPlugin 'observability:entityCentricExperience', true ); + + this.telemetry.setup({ + analytics: coreSetup.analytics, + }); + + const telemetry = this.telemetry.start(); + const getStartServices = coreSetup.getStartServices(); const hideInventory$ = from(getStartServices).pipe( @@ -105,9 +113,6 @@ export class InventoryPlugin pluginsSetup.observabilityShared.navigation.registerSections(sections$); - this.telemetry.setup({ analytics: coreSetup.analytics }); - const telemetry = this.telemetry.start(); - const isCloudEnv = !!pluginsSetup.cloud?.isCloudEnabled; const isServerlessEnv = pluginsSetup.cloud?.isServerlessEnabled || this.isServerlessEnv; diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts index 1e36e8d6649ae..54d20ea324b11 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts @@ -6,7 +6,16 @@ */ import { AnalyticsServiceSetup } from '@kbn/core-analytics-browser'; -import { type ITelemetryClient, TelemetryEventTypes, type InventoryAddDataParams } from './types'; + +import { + type ITelemetryClient, + TelemetryEventTypes, + type InventoryAddDataParams, + type EntityInventoryViewedParams, + type EntityInventorySearchQuerySubmittedParams, + type EntityViewClickedParams, + type EntityInventoryEntityTypeFilteredParams, +} from './types'; export class TelemetryClient implements ITelemetryClient { constructor(private analytics: AnalyticsServiceSetup) {} @@ -14,4 +23,24 @@ export class TelemetryClient implements ITelemetryClient { public reportInventoryAddData = (params: InventoryAddDataParams) => { this.analytics.reportEvent(TelemetryEventTypes.INVENTORY_ADD_DATA_CLICKED, params); }; + + public reportEntityInventoryViewed = (params: EntityInventoryViewedParams) => { + this.analytics.reportEvent(TelemetryEventTypes.ENTITY_INVENTORY_VIEWED, params); + }; + + public reportEntityInventorySearchQuerySubmitted = ( + params: EntityInventorySearchQuerySubmittedParams + ) => { + this.analytics.reportEvent(TelemetryEventTypes.ENTITY_INVENTORY_SEARCH_QUERY_SUBMITTED, params); + }; + + public reportEntityInventoryEntityTypeFiltered = ( + params: EntityInventoryEntityTypeFilteredParams + ) => { + this.analytics.reportEvent(TelemetryEventTypes.ENTITY_INVENTORY_ENTITY_TYPE_FILTERED, params); + }; + + public reportEntityViewClicked = (params: EntityViewClickedParams) => { + this.analytics.reportEvent(TelemetryEventTypes.ENTITY_VIEW_CLICKED, params); + }; } diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts index c1509499e694b..d61a90f7d30ab 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts @@ -25,4 +25,94 @@ const inventoryAddDataEventType: TelemetryEvent = { }, }; -export const inventoryTelemetryEventBasedTypes = [inventoryAddDataEventType]; +const entityInventoryViewedEventType: TelemetryEvent = { + eventType: TelemetryEventTypes.ENTITY_INVENTORY_VIEWED, + schema: { + view_state: { + type: 'keyword', + _meta: { + description: 'State of the view: empty, populated or eem_disabled.', + }, + }, + }, +}; + +const searchQuerySubmittedEventType: TelemetryEvent = { + eventType: TelemetryEventTypes.ENTITY_INVENTORY_SEARCH_QUERY_SUBMITTED, + schema: { + kuery_fields: { + type: 'array', + items: { + type: 'text', + _meta: { + description: 'Kuery fields used in the search.', + }, + }, + }, + entity_types: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: 'Entity types used in the search.', + }, + }, + }, + action: { + type: 'keyword', + _meta: { + description: 'Action performed: submit or refresh.', + }, + }, + }, +}; + +const entityInventoryEntityTypeFilteredEventType: TelemetryEvent = { + eventType: TelemetryEventTypes.ENTITY_INVENTORY_ENTITY_TYPE_FILTERED, + schema: { + entity_types: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: 'Entity types used in the filter.', + }, + }, + }, + kuery_fields: { + type: 'array', + items: { + type: 'text', + _meta: { + description: 'Kuery fields used in the filter.', + }, + }, + }, + }, +}; + +const entityViewClickedEventType: TelemetryEvent = { + eventType: TelemetryEventTypes.ENTITY_VIEW_CLICKED, + schema: { + entity_type: { + type: 'keyword', + _meta: { + description: 'Type of the entity: container, host or service.', + }, + }, + view_type: { + type: 'keyword', + _meta: { + description: 'Type of the view: detail or flyout.', + }, + }, + }, +}; + +export const inventoryTelemetryEventBasedTypes = [ + inventoryAddDataEventType, + entityInventoryViewedEventType, + searchQuerySubmittedEventType, + entityInventoryEntityTypeFilteredEventType, + entityViewClickedEventType, +]; diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts index ffa05ffbff9a2..415cf0e7d4406 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts @@ -8,7 +8,13 @@ import { coreMock } from '@kbn/core/server/mocks'; import { inventoryTelemetryEventBasedTypes } from './telemetry_events'; import { TelemetryService } from './telemetry_service'; -import { TelemetryEventTypes } from './types'; +import { + type EntityInventoryViewedParams, + type EntityViewClickedParams, + type EntityInventorySearchQuerySubmittedParams, + TelemetryEventTypes, + type EntityInventoryEntityTypeFilteredParams, +} from './types'; describe('TelemetryService', () => { let service: TelemetryService; @@ -48,7 +54,15 @@ describe('TelemetryService', () => { service.setup(setupParams); const telemetry = service.start(); - expect(telemetry).toHaveProperty('reportInventoryAddData'); + const expectedProperties = [ + 'reportInventoryAddData', + 'reportEntityInventoryViewed', + 'reportEntityInventorySearchQuerySubmitted', + 'reportEntityViewClicked', + ]; + expectedProperties.forEach((property) => { + expect(telemetry).toHaveProperty(property); + }); }); }); @@ -73,4 +87,84 @@ describe('TelemetryService', () => { ); }); }); + + describe('#reportEntityInventoryViewed', () => { + it('should report entity inventory viewed with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + const params: EntityInventoryViewedParams = { + view_state: 'empty', + }; + + telemetry.reportEntityInventoryViewed(params); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + TelemetryEventTypes.ENTITY_INVENTORY_VIEWED, + params + ); + }); + }); + + describe('#reportEntityInventorySearchQuerySubmitted', () => { + it('should report search query submitted with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + const params: EntityInventorySearchQuerySubmittedParams = { + kuery_fields: ['_index'], + action: 'submit', + entity_types: ['container'], + }; + + telemetry.reportEntityInventorySearchQuerySubmitted(params); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + TelemetryEventTypes.ENTITY_INVENTORY_SEARCH_QUERY_SUBMITTED, + params + ); + }); + }); + + describe('#reportEntityInventoryEntityTypeFiltered', () => { + it('should report entity type filtered with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + const params: EntityInventoryEntityTypeFilteredParams = { + kuery_fields: ['_index'], + entity_types: ['container'], + }; + + telemetry.reportEntityInventoryEntityTypeFiltered(params); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + TelemetryEventTypes.ENTITY_INVENTORY_ENTITY_TYPE_FILTERED, + params + ); + }); + }); + + describe('#reportEntityViewClicked', () => { + it('should report entity view clicked with properties', async () => { + const setupParams = getSetupParams(); + service.setup(setupParams); + const telemetry = service.start(); + const params: EntityViewClickedParams = { + entity_type: 'container', + view_type: 'detail', + }; + + telemetry.reportEntityViewClicked(params); + + expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); + expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( + TelemetryEventTypes.ENTITY_VIEW_CLICKED, + params + ); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.ts index fa416f76b3c16..b81aff39672bb 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.ts @@ -5,7 +5,7 @@ * 2.0. */ import type { AnalyticsServiceSetup } from '@kbn/core-analytics-browser'; -import type { TelemetryServiceSetupParams, ITelemetryClient, TelemetryEventParams } from './types'; +import type { TelemetryServiceSetupParams, TelemetryEventParams } from './types'; import { inventoryTelemetryEventBasedTypes } from './telemetry_events'; import { TelemetryClient } from './telemetry_client'; @@ -23,7 +23,7 @@ export class TelemetryService { ); } - public start(): ITelemetryClient { + public start(): TelemetryClient { if (!this.analytics) { throw new Error( 'The TelemetryService.setup() method has not been invoked, be sure to call it during the plugin setup.' diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts index e5fdf162b750c..0e52d115d4597 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts @@ -6,24 +6,64 @@ */ import type { AnalyticsServiceSetup, RootSchema } from '@kbn/core/public'; +import { EntityManagerPublicPluginSetup } from '@kbn/entityManager-plugin/public'; export interface TelemetryServiceSetupParams { analytics: AnalyticsServiceSetup; } +export interface TelemetryServiceStartParams { + entityManager: EntityManagerPublicPluginSetup; +} + export interface InventoryAddDataParams { view: 'add_data_button' | 'empty_state'; journey?: 'add_data' | 'associate_existing_service_logs'; } -export type TelemetryEventParams = InventoryAddDataParams; +export interface EntityInventoryViewedParams { + view_state: 'empty' | 'populated' | 'eem_disabled'; +} + +export interface EntityInventorySearchQuerySubmittedParams { + kuery_fields: string[]; + entity_types: string[]; + action: 'submit' | 'refresh'; +} + +export interface EntityInventoryEntityTypeFilteredParams { + kuery_fields: string[]; + entity_types: string[]; +} + +export interface EntityViewClickedParams { + entity_type: string; + view_type: 'detail' | 'flyout'; +} + +export type TelemetryEventParams = + | InventoryAddDataParams + | EntityInventoryViewedParams + | EntityInventorySearchQuerySubmittedParams + | EntityInventoryEntityTypeFilteredParams + | EntityViewClickedParams; export interface ITelemetryClient { reportInventoryAddData(params: InventoryAddDataParams): void; + reportEntityInventoryViewed(params: EntityInventoryViewedParams): void; + reportEntityInventorySearchQuerySubmitted( + params: EntityInventorySearchQuerySubmittedParams + ): void; + reportEntityInventoryEntityTypeFiltered(params: EntityInventoryEntityTypeFilteredParams): void; + reportEntityViewClicked(params: EntityViewClickedParams): void; } export enum TelemetryEventTypes { INVENTORY_ADD_DATA_CLICKED = 'inventory_add_data_clicked', + ENTITY_INVENTORY_VIEWED = 'Entity Inventory Viewed', + ENTITY_INVENTORY_SEARCH_QUERY_SUBMITTED = 'Entity Inventory Search Query Submitted', + ENTITY_INVENTORY_ENTITY_TYPE_FILTERED = 'Entity Inventory Entity Type Filtered', + ENTITY_VIEW_CLICKED = 'Entity View Clicked', } export interface TelemetryEvent { diff --git a/x-pack/plugins/observability_solution/inventory/public/services/types.ts b/x-pack/plugins/observability_solution/inventory/public/services/types.ts index d0cc176e7b53f..b498a1fd49079 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/types.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/types.ts @@ -6,7 +6,7 @@ */ import type { InventoryAPIClient } from '../api'; -import type { ITelemetryClient } from './telemetry/types'; +import { ITelemetryClient } from './telemetry/types'; export interface InventoryServices { inventoryAPIClient: InventoryAPIClient; diff --git a/x-pack/plugins/observability_solution/inventory/public/utils/get_kql_field_names_with_fallback.test.ts b/x-pack/plugins/observability_solution/inventory/public/utils/get_kql_field_names_with_fallback.test.ts new file mode 100644 index 0000000000000..89305e2bb08c9 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/utils/get_kql_field_names_with_fallback.test.ts @@ -0,0 +1,44 @@ +/* + * 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 { getKqlFieldsWithFallback } from './get_kql_field_names_with_fallback'; +import { getKqlFieldNamesFromExpression } from '@kbn/es-query'; + +jest.mock('@kbn/es-query', () => ({ + getKqlFieldNamesFromExpression: jest.fn(), +})); + +describe('getKqlFieldsWithFallback', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return field names when getKqlFieldNamesFromExpression succeeds', () => { + const mockFieldNames = ['field1', 'field2']; + (getKqlFieldNamesFromExpression as jest.Mock).mockReturnValue(mockFieldNames); + const expectedArg = 'testKuery'; + + const result = getKqlFieldsWithFallback(expectedArg); + expect(result).toEqual(mockFieldNames); + expect(getKqlFieldNamesFromExpression).toHaveBeenCalledWith(expectedArg); + }); + + it('should return an empty array when getKqlFieldNamesFromExpression throws an error', () => { + (getKqlFieldNamesFromExpression as jest.Mock).mockImplementation(() => { + throw new Error('Test error'); + }); + const expectedArg = 'testKuery'; + + const result = getKqlFieldsWithFallback(expectedArg); + expect(result).toEqual([]); + expect(getKqlFieldNamesFromExpression).toHaveBeenCalledWith(expectedArg); + }); +}); diff --git a/x-pack/plugins/observability_solution/inventory/public/utils/get_kql_field_names_with_fallback.ts b/x-pack/plugins/observability_solution/inventory/public/utils/get_kql_field_names_with_fallback.ts new file mode 100644 index 0000000000000..029405b5fc235 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/utils/get_kql_field_names_with_fallback.ts @@ -0,0 +1,16 @@ +/* + * 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 { getKqlFieldNamesFromExpression } from '@kbn/es-query'; + +export function getKqlFieldsWithFallback(kuery: string): string[] { + try { + return getKqlFieldNamesFromExpression(kuery); + } catch (e) { + return []; + } +} diff --git a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts index a36c85dbf937a..07bb33ebb5a98 100644 --- a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts +++ b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts @@ -325,6 +325,15 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { } ), }, + { + link: 'ml:esqlDataVisualizer', + title: i18n.translate( + 'xpack.observability.obltNav.ml.data_visualizer.esql_data_visualizer', + { + defaultMessage: 'ES|QL data visualizer', + } + ), + }, { link: 'ml:dataDrift', title: i18n.translate( diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts index 831f8d107f36a..b328b273836a7 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts @@ -76,8 +76,8 @@ journey(`GlobalParameters`, async ({ page, params }) => { await page.click('text=Delete ParameterEdit Parameter >> :nth-match(button, 2)'); await page.click('[aria-label="Key"]'); await page.fill('[aria-label="Key"]', 'username2'); - await page.click('[aria-label="Value"]'); - await page.fill('[aria-label="Value"]', 'elastic2'); + await page.click('[aria-label="New value"]'); + await page.fill('[aria-label="New value"]', 'elastic2'); await page.click('.euiComboBox__inputWrap'); await page.fill('[aria-label="Tags"]', 'staging'); await page.press('[aria-label="Tags"]', 'Enter'); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/components/optional_text.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/components/optional_text.tsx new file mode 100644 index 0000000000000..a764cf3b27cdc --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/components/optional_text.tsx @@ -0,0 +1,20 @@ +/* + * 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 { EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export function OptionalText() { + return ( + + {i18n.translate('xpack.synthetics.sloEdit.optionalLabel', { + defaultMessage: 'Optional', + })} + + ); +} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx index 3fd17335d2ea5..70c2eb77526af 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_flyout.tsx @@ -22,6 +22,7 @@ import { FormProvider } from 'react-hook-form'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { i18n } from '@kbn/i18n'; import { useDispatch, useSelector } from 'react-redux'; +import { isEmpty } from 'lodash'; import { NoPermissionsTooltip } from '../../common/components/permissions'; import { addNewGlobalParamAction, @@ -80,18 +81,29 @@ export const AddParamFlyout = ({ const onSubmit = (formData: SyntheticsParams) => { const { namespaces, ...paramRequest } = formData; const shareAcrossSpaces = namespaces?.includes(ALL_SPACES_ID); + const newParamData = { + ...paramRequest, + }; + + if (isEditingItem && id) { + // omit value if it's empty + if (isEmpty(newParamData.value)) { + // @ts-ignore this is a valid check + delete newParamData.value; + } + } if (isEditingItem && id) { dispatch( editGlobalParamAction.get({ id, - paramRequest: { ...paramRequest, share_across_spaces: shareAcrossSpaces }, + paramRequest, }) ); } else { dispatch( addNewGlobalParamAction.get({ - ...paramRequest, + ...newParamData, share_across_spaces: shareAcrossSpaces, }) ); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx index 1b219a0f6fec4..d472ec62237e9 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/add_param_form.tsx @@ -6,16 +6,11 @@ */ import React from 'react'; import { ALL_SPACES_ID } from '@kbn/security-plugin/public'; -import { - EuiCheckbox, - EuiComboBox, - EuiFieldText, - EuiForm, - EuiFormRow, - EuiTextArea, -} from '@elastic/eui'; +import { EuiCheckbox, EuiComboBox, EuiFieldText, EuiForm, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Controller, useFormContext, useFormState } from 'react-hook-form'; +import { OptionalText } from '../components/optional_text'; +import { ParamValueField } from './param_value_field'; import { SyntheticsParams } from '../../../../../../common/runtime_types'; import { ListParamItem } from './params_list'; @@ -61,25 +56,8 @@ export const AddParamForm = ({ })} /> - - - - + + }> - + }> { + const { register } = useFormContext(); + const { errors } = useFormState(); + + if (isEditingItem) { + return ( + <> + } + > + + + + + + ); + } + + return ( + + + + ); +}; + +export const NEW_VALUE_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.paramForm.newValue', + { + defaultMessage: 'New value', + } +); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/global_params/api.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/global_params/api.ts index ce7f9bd81ea3d..33eb4622bf6c5 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/global_params/api.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/global_params/api.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { isEmpty } from 'lodash'; import { INITIAL_REST_VERSION, SYNTHETICS_API_URLS } from '../../../../../common/constants'; import { DeleteParamsResponse, @@ -35,16 +36,22 @@ export const editGlobalParam = async ({ id, }: { id: string; - paramRequest: SyntheticsParamRequest; -}): Promise => - apiService.put( + paramRequest: Partial; +}): Promise => { + const data = paramRequest; + if (isEmpty(paramRequest.value)) { + // omit empty value + delete data.value; + } + return await apiService.put( SYNTHETICS_API_URLS.PARAMS + `/${id}`, - paramRequest, + data, SyntheticsParamsCodec, { version: INITIAL_REST_VERSION, } ); +}; export const deleteGlobalParams = async (ids: string[]): Promise => apiService.delete( diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/common.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/common.ts index 0bdc7989b8a8a..2a906f3cf6a4d 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/common.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/common.ts @@ -9,6 +9,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { SavedObjectsFindResponse } from '@kbn/core/server'; import { isEmpty } from 'lodash'; import { escapeQuotes } from '@kbn/es-query'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { RouteContext } from './types'; import { MonitorSortFieldSchema } from '../../common/runtime_types/monitor_management/sort_field'; import { getAllLocations } from '../synthetics_service/get_all_locations'; @@ -269,3 +270,26 @@ function parseMappingKey(key: string | undefined) { return key; } } + +export const validateRouteSpaceName = async (routeContext: RouteContext) => { + const { spaceId, server, request, response } = routeContext; + if (spaceId === DEFAULT_SPACE_ID) { + // default space is always valid + return { spaceId: DEFAULT_SPACE_ID }; + } + + try { + await server.spaces?.spacesService.getActiveSpace(request); + } catch (error) { + if (error.output?.statusCode === 404) { + return { + spaceId, + invalidResponse: response.notFound({ + body: { message: `Kibana space '${spaceId}' does not exist` }, + }), + }; + } + } + + return { invalidResponse: undefined }; +}; diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/add_param.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/add_param.ts index a51079f366eff..7d0cac7d7e57c 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/add_param.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/add_param.ts @@ -19,8 +19,12 @@ import { syntheticsParamType } from '../../../../common/types/saved_objects'; import { SYNTHETICS_API_URLS } from '../../../../common/constants'; const ParamsObjectSchema = schema.object({ - key: schema.string(), - value: schema.string(), + key: schema.string({ + minLength: 1, + }), + value: schema.string({ + minLength: 1, + }), description: schema.maybe(schema.string()), tags: schema.maybe(schema.arrayOf(schema.string())), share_across_spaces: schema.maybe(schema.boolean()), diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/edit_param.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/edit_param.ts index 3555963b76bf1..eb9f41696da97 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/edit_param.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/edit_param.ts @@ -6,8 +6,9 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import { SavedObject } from '@kbn/core/server'; -import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; +import { SavedObject, SavedObjectsErrorHelpers } from '@kbn/core/server'; +import { isEmpty } from 'lodash'; +import { validateRouteSpaceName } from '../../common'; import { SyntheticsRestApiRouteFactory } from '../../types'; import { SyntheticsParamRequest, SyntheticsParams } from '../../../../common/runtime_types'; import { syntheticsParamType } from '../../../../common/types/saved_objects'; @@ -20,7 +21,7 @@ const RequestParamsSchema = schema.object({ type RequestParams = TypeOf; export const editSyntheticsParamsRoute: SyntheticsRestApiRouteFactory< - SyntheticsParams, + SyntheticsParams | undefined, RequestParams > = () => ({ method: 'PUT', @@ -30,46 +31,63 @@ export const editSyntheticsParamsRoute: SyntheticsRestApiRouteFactory< request: { params: RequestParamsSchema, body: schema.object({ - key: schema.string(), - value: schema.string(), + key: schema.maybe( + schema.string({ + minLength: 1, + }) + ), + value: schema.maybe( + schema.string({ + minLength: 1, + }) + ), description: schema.maybe(schema.string()), tags: schema.maybe(schema.arrayOf(schema.string())), - share_across_spaces: schema.maybe(schema.boolean()), }), }, }, - handler: async ({ savedObjectsClient, request, server, response }) => { + handler: async (routeContext) => { + const { savedObjectsClient, request, response, spaceId, server } = routeContext; + const { invalidResponse } = await validateRouteSpaceName(routeContext); + if (invalidResponse) return invalidResponse; + + const { id: paramId } = request.params; + const data = request.body as SyntheticsParamRequest; + if (isEmpty(data)) { + return response.badRequest({ body: { message: 'Request body cannot be empty' } }); + } + const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient(); + try { - const { id: _spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? { - id: DEFAULT_SPACE_ID, + const existingParam = + await encryptedSavedObjectsClient.getDecryptedAsInternalUser( + syntheticsParamType, + paramId, + { namespace: spaceId } + ); + + const newParam = { + ...existingParam.attributes, + ...data, }; - const { id } = request.params; - const { share_across_spaces: _shareAcrossSpaces, ...data } = - request.body as SyntheticsParamRequest & { - id: string; - }; - const { value } = data; + // value from data since we aren't using encrypted client + const { value } = existingParam.attributes; const { id: responseId, attributes: { key, tags, description }, namespaces, - } = (await savedObjectsClient.update( + } = (await savedObjectsClient.update( syntheticsParamType, - id, - data + paramId, + newParam )) as SavedObject; return { id: responseId, key, tags, description, namespaces, value }; - } catch (error) { - if (error.output?.statusCode === 404) { - const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; - return response.notFound({ - body: { message: `Kibana space '${spaceId}' does not exist` }, - }); + } catch (getErr) { + if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) { + return response.notFound({ body: { message: 'Param not found' } }); } - - throw error; } }, }); diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/params.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/params.ts index 01f2dd6465dfd..da0a2e250557a 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/params.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/settings/params/params.ts @@ -7,7 +7,7 @@ import { SavedObject, SavedObjectsFindResult } from '@kbn/core-saved-objects-api-server'; import { schema, TypeOf } from '@kbn/config-schema'; -import { SyntheticsRestApiRouteFactory } from '../../types'; +import { RouteContext, SyntheticsRestApiRouteFactory } from '../../types'; import { syntheticsParamType } from '../../../../common/types/saved_objects'; import { SYNTHETICS_API_URLS } from '../../../../common/constants'; import { SyntheticsParams, SyntheticsParamsReadonly } from '../../../../common/runtime_types'; @@ -30,45 +30,13 @@ export const getSyntheticsParamsRoute: SyntheticsRestApiRouteFactory< params: RequestParamsSchema, }, }, - handler: async ({ savedObjectsClient, request, response, server, spaceId }) => { + handler: async (routeContext) => { + const { savedObjectsClient, request, response, spaceId } = routeContext; try { const { id: paramId } = request.params; - const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient(); - - const canSave = - ( - await server.coreStart?.capabilities.resolveCapabilities(request, { - capabilityPath: 'uptime.*', - }) - ).uptime.save ?? false; - - if (canSave) { - if (paramId) { - const savedObject = - await encryptedSavedObjectsClient.getDecryptedAsInternalUser( - syntheticsParamType, - paramId, - { namespace: spaceId } - ); - return toClientResponse(savedObject); - } - - const finder = - await encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( - { - type: syntheticsParamType, - perPage: 1000, - namespaces: [spaceId], - } - ); - - const hits: Array> = []; - for await (const result of finder.find()) { - hits.push(...result.saved_objects); - } - - return hits.map((savedObject) => toClientResponse(savedObject)); + if (await isAnAdminUser(routeContext)) { + return getDecryptedParams(routeContext, paramId); } else { if (paramId) { const savedObject = await savedObjectsClient.get( @@ -78,11 +46,7 @@ export const getSyntheticsParamsRoute: SyntheticsRestApiRouteFactory< return toClientResponse(savedObject); } - const data = await savedObjectsClient.find({ - type: syntheticsParamType, - perPage: 10000, - }); - return data.saved_objects.map((savedObject) => toClientResponse(savedObject)); + return findAllParams(routeContext); } } catch (error) { if (error.output?.statusCode === 404) { @@ -94,6 +58,70 @@ export const getSyntheticsParamsRoute: SyntheticsRestApiRouteFactory< }, }); +const isAnAdminUser = async (routeContext: RouteContext) => { + const { request, server } = routeContext; + const user = server.coreStart.security.authc.getCurrentUser(request); + + const isSuperUser = user?.roles.includes('superuser'); + const isAdmin = user?.roles.includes('kibana_admin'); + + const canSave = + ( + await server.coreStart?.capabilities.resolveCapabilities(request, { + capabilityPath: 'uptime.*', + }) + ).uptime.save ?? false; + + return (isSuperUser || isAdmin) && canSave; +}; + +const getDecryptedParams = async ({ server, spaceId }: RouteContext, paramId?: string) => { + const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient(); + + if (paramId) { + const savedObject = + await encryptedSavedObjectsClient.getDecryptedAsInternalUser( + syntheticsParamType, + paramId, + { namespace: spaceId } + ); + return toClientResponse(savedObject); + } + const finder = + await encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( + { + type: syntheticsParamType, + perPage: 1000, + namespaces: [spaceId], + } + ); + + const hits: Array> = []; + for await (const result of finder.find()) { + hits.push(...result.saved_objects); + } + + void finder.close(); + + return hits.map((savedObject) => toClientResponse(savedObject)); +}; + +const findAllParams = async ({ savedObjectsClient }: RouteContext) => { + const finder = savedObjectsClient.createPointInTimeFinder({ + type: syntheticsParamType, + perPage: 1000, + }); + + const hits: Array> = []; + for await (const result of finder.find()) { + hits.push(...result.saved_objects); + } + + void finder.close(); + + return hits.map((savedObject) => toClientResponse(savedObject)); +}; + const toClientResponse = ( savedObject: SavedObject ) => { diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts index b4dd34952c7a8..227fff690af53 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts @@ -21,6 +21,14 @@ describe('isValidUrl', () => { it('returns true for valid URL', () => { expect(isValidURL('https://elastic.co')).toBeTruthy(); }); + + it('returns skips validation vars', () => { + expect(isValidURL('${urlParam}')).toBeTruthy(); + }); + + it('returns skips validation vars with http', () => { + expect(isValidURL('http://${urlParam}')).toBeTruthy(); + }); }); describe('getUrlsField', () => { diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts index 0a3aa8295a94d..c67e7decbe984 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts @@ -8,6 +8,7 @@ import { omit, uniqBy } from 'lodash'; import { i18n } from '@kbn/i18n'; import { isValidNamespace } from '@kbn/fleet-plugin/common'; +import { hasNoParams } from '../../formatters/formatting_utils'; import { formatLocation } from '../../../../common/utils/location_formatter'; import { BrowserFields, @@ -408,6 +409,10 @@ export const getOptionalListField = (value?: string[] | string): string[] => { * @returns `true` if `new URL` does not throw an error, `false` otherwise */ export const isValidURL = (url: string): boolean => { + if (!hasNoParams(url)) { + // this is done to avoid parsing urls with variables + return true; + } try { new URL(url); return true; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 584b992a0f61d..68aaf7bf9cf04 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -280,6 +280,8 @@ export const PINNED_EVENT_URL = '/api/pinned_event' as const; export const SOURCERER_API_URL = '/internal/security_solution/sourcerer' as const; export const RISK_SCORE_INDEX_STATUS_API_URL = '/internal/risk_score/index_status' as const; +export const EVENT_GRAPH_VISUALIZATION_API = '/internal/cloud_security_posture/graph' as const; + /** * Default signals index key for kibana.dev.yml */ diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index ffb9e9748d9c1..792b6352912b3 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -190,6 +190,11 @@ export const allowedExperimentalValues = Object.freeze({ */ analyzerDatePickersAndSourcererDisabled: false, + /** + * Enables graph visualization in alerts flyout + */ + graphVisualizationInFlyoutEnabled: false, + /** * Enables an ability to customize Elastic prebuilt rules. * diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview.test.tsx new file mode 100644 index 0000000000000..22ac27eaa4e00 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview.test.tsx @@ -0,0 +1,72 @@ +/* + * 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 { render } from '@testing-library/react'; +import React from 'react'; +import { TestProviders } from '../../../../common/mock'; +import { mockContextValue } from '../../shared/mocks/mock_context'; +import { DocumentDetailsContext } from '../../shared/context'; +import { GraphPreview, type GraphPreviewProps } from './graph_preview'; +import { GRAPH_PREVIEW_TEST_ID, GRAPH_PREVIEW_LOADING_TEST_ID } from './test_ids'; + +const mockGraph = () =>
; + +jest.mock('@kbn/cloud-security-posture-graph', () => { + return { Graph: mockGraph }; +}); + +const renderGraphPreview = (contextValue: DocumentDetailsContext, props: GraphPreviewProps) => + render( + + + + + + ); + +const ERROR_MESSAGE = 'An error is preventing this alert from being visualized.'; + +describe('', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('shows graph preview correctly when data is loaded', async () => { + const graphProps = { + isLoading: false, + isError: false, + data: { nodes: [], edges: [] }, + }; + + const { findByTestId } = renderGraphPreview(mockContextValue, graphProps); + + // Using findByTestId to wait for the component to be rendered because it is a lazy loaded component + expect(await findByTestId(GRAPH_PREVIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('shows loading when data is loading', () => { + const graphProps = { + isLoading: true, + isError: false, + }; + + const { getByTestId } = renderGraphPreview(mockContextValue, graphProps); + + expect(getByTestId(GRAPH_PREVIEW_LOADING_TEST_ID)).toBeInTheDocument(); + }); + + it('shows error message when there is an error', () => { + const graphProps = { + isLoading: false, + isError: true, + }; + + const { getByText } = renderGraphPreview(mockContextValue, graphProps); + + expect(getByText(ERROR_MESSAGE)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview.tsx new file mode 100644 index 0000000000000..c3c6d65c7e986 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview.tsx @@ -0,0 +1,102 @@ +/* + * 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, { memo, useMemo } from 'react'; +import { EuiSkeletonText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { + NodeDataModel, + EdgeDataModel, +} from '@kbn/cloud-security-posture-common/types/graph/latest'; +import { GRAPH_PREVIEW_TEST_ID, GRAPH_PREVIEW_LOADING_TEST_ID } from './test_ids'; + +/** + * Props for the GraphPreview component. + */ +export interface GraphPreviewProps { + /** + * Indicates whether the graph is currently loading. + */ + isLoading: boolean; + + /** + * Indicates whether there was an error loading the graph. + */ + isError: boolean; + + /** + * Optional data for the graph, including nodes and edges. + */ + data?: { + /** + * Array of node data models. + */ + nodes: NodeDataModel[]; + + /** + * Array of edge data models. + */ + edges: EdgeDataModel[]; + }; +} + +const GraphLazy = React.lazy(() => + import('@kbn/cloud-security-posture-graph').then((module) => ({ default: module.Graph })) +); + +const LoadingComponent = () => ( + +); + +/** + * Graph preview under Overview, Visualizations. It shows a graph without abilities to expand. + */ +export const GraphPreview: React.FC = memo( + ({ isLoading, isError, data }: GraphPreviewProps) => { + const memoizedNodes = useMemo(() => data?.nodes ?? [], [data?.nodes]); + const memoizedEdges = useMemo(() => data?.edges ?? [], [data?.edges]); + + return isLoading ? ( + + ) : isError ? ( + + ) : ( + }> + + + ); + } +); + +GraphPreview.displayName = 'GraphPreview'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx new file mode 100644 index 0000000000000..6b30e2127a2f8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.test.tsx @@ -0,0 +1,110 @@ +/* + * 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 { render } from '@testing-library/react'; +import { TestProviders } from '../../../../common/mock'; +import React from 'react'; +import { DocumentDetailsContext } from '../../shared/context'; +import { mockContextValue } from '../../shared/mocks/mock_context'; +import { GraphPreviewContainer } from './graph_preview_container'; +import { GRAPH_PREVIEW_TEST_ID } from './test_ids'; +import { useGraphPreview } from '../hooks/use_graph_preview'; +import { useFetchGraphData } from '../hooks/use_fetch_graph_data'; + +import { + EXPANDABLE_PANEL_CONTENT_TEST_ID, + EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID, + EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID, + EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, + EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, +} from '@kbn/security-solution-common'; + +jest.mock('../hooks/use_graph_preview'); +jest.mock('../hooks/use_fetch_graph_data', () => ({ + useFetchGraphData: jest.fn(), +})); +const mockUseFetchGraphData = useFetchGraphData as jest.Mock; + +const mockUseUiSetting = jest.fn().mockReturnValue([false]); +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + ...original, + useUiSetting$: () => mockUseUiSetting(), + }; +}); + +const mockGraph = () =>
; + +jest.mock('@kbn/cloud-security-posture-graph', () => { + return { Graph: mockGraph }; +}); + +const renderGraphPreview = (context = mockContextValue) => + render( + + + + + + ); + +describe('', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render component and link in header', async () => { + mockUseFetchGraphData.mockReturnValue({ + isLoading: false, + isError: false, + data: { nodes: [], edges: [] }, + }); + + (useGraphPreview as jest.Mock).mockReturnValue({ + isAuditLog: true, + }); + + const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview(); + + // Using findByTestId to wait for the component to be rendered because it is a lazy loaded component + expect(await findByTestId(GRAPH_PREVIEW_TEST_ID)).toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + }); + + it('should render error message and text in header', () => { + mockUseFetchGraphData.mockReturnValue({ + isLoading: false, + isError: false, + data: undefined, + }); + + (useGraphPreview as jest.Mock).mockReturnValue({ + isAuditLog: false, + }); + + const { getByTestId } = renderGraphPreview(); + + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx new file mode 100644 index 0000000000000..1bc6a8dd7e547 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx @@ -0,0 +1,72 @@ +/* + * 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 { FormattedMessage } from '@kbn/i18n-react'; +import { ExpandablePanel } from '@kbn/security-solution-common'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { GRAPH_PREVIEW_TEST_ID } from './test_ids'; +import { GraphPreview } from './graph_preview'; +import { useFetchGraphData } from '../hooks/use_fetch_graph_data'; +import { useGraphPreview } from '../hooks/use_graph_preview'; + +const DEFAULT_FROM = 'now-60d/d'; +const DEFAULT_TO = 'now/d'; + +/** + * Graph preview under Overview, Visualizations. It shows a graph representation of entities. + */ +export const GraphPreviewContainer: React.FC = () => { + const { dataAsNestedObject, getFieldsData } = useDocumentDetailsContext(); + + const { eventIds } = useGraphPreview({ + getFieldsData, + ecsData: dataAsNestedObject, + }); + + // TODO: default start and end might not capture the original event + const graphFetchQuery = useFetchGraphData({ + req: { + query: { + actorIds: [], + eventIds, + start: DEFAULT_FROM, + end: DEFAULT_TO, + }, + }, + }); + + return ( + + ), + iconType: 'indexMapping', + }} + data-test-subj={GRAPH_PREVIEW_TEST_ID} + content={ + !graphFetchQuery.isLoading && !graphFetchQuery.isError + ? { + paddingSize: 'none', + } + : undefined + } + > + + + ); +}; + +GraphPreviewContainer.displayName = 'GraphPreviewContainer'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts index e0d8bc6db0f5c..e649c578bf487 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts @@ -178,6 +178,9 @@ export const SESSION_PREVIEW_TEST_ID = `${PREFIX}SessionPreview` as const; export const SESSION_PREVIEW_RULE_DETAILS_LINK_TEST_ID = `${SESSION_PREVIEW_TEST_ID}RuleDetailsLink` as const; +export const GRAPH_PREVIEW_TEST_ID = `${PREFIX}GraphPreview` as const; +export const GRAPH_PREVIEW_LOADING_TEST_ID = `${GRAPH_PREVIEW_TEST_ID}Loading` as const; + /* Response section */ const RESPONSE_TEST_ID = `${PREFIX}Response` as const; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx index 9af61e21fb67d..3aeb7d30f8e48 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx @@ -11,6 +11,7 @@ import { render } from '@testing-library/react'; import { ANALYZER_PREVIEW_TEST_ID, SESSION_PREVIEW_TEST_ID, + GRAPH_PREVIEW_TEST_ID, VISUALIZATIONS_SECTION_CONTENT_TEST_ID, VISUALIZATIONS_SECTION_HEADER_TEST_ID, } from './test_ids'; @@ -24,6 +25,9 @@ import { TestProvider } from '@kbn/expandable-flyout/src/test/provider'; import { useExpandSection } from '../hooks/use_expand_section'; import { useInvestigateInTimeline } from '../../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline'; import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { useGraphPreview } from '../hooks/use_graph_preview'; +import { useFetchGraphData } from '../hooks/use_fetch_graph_data'; jest.mock('../hooks/use_expand_section'); jest.mock('../../shared/hooks/use_alert_prevalence_from_process_tree', () => ({ @@ -49,6 +53,11 @@ jest.mock( jest.mock( '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver' ); +jest.mock('../../../../common/hooks/use_experimental_features', () => ({ + useIsExperimentalFeatureEnabled: jest.fn(), +})); + +const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock; const mockUseUiSetting = jest.fn().mockReturnValue([false]); jest.mock('@kbn/kibana-react-plugin/public', () => { @@ -58,6 +67,15 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { useUiSetting$: () => mockUseUiSetting(), }; }); +jest.mock('../hooks/use_graph_preview'); + +const mockUseGraphPreview = useGraphPreview as jest.Mock; + +jest.mock('../hooks/use_fetch_graph_data', () => ({ + useFetchGraphData: jest.fn(), +})); + +const mockUseFetchGraphData = useFetchGraphData as jest.Mock; const panelContextValue = { ...mockContextValue, @@ -84,6 +102,17 @@ describe('', () => { alertIds: undefined, statsNodes: undefined, }); + mockUseGraphPreview.mockReturnValue({ + isAuditLog: true, + }); + mockUseFetchGraphData.mockReturnValue({ + isLoading: false, + isError: false, + data: { + nodes: [], + edges: [], + }, + }); }); it('should render visualizations component', () => { @@ -107,11 +136,22 @@ describe('', () => { }); (useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); (useExpandSection as jest.Mock).mockReturnValue(true); + useIsExperimentalFeatureEnabledMock.mockReturnValue(false); - const { getByTestId } = renderVisualizationsSection(); + const { getByTestId, queryByTestId } = renderVisualizationsSection(); expect(getByTestId(VISUALIZATIONS_SECTION_CONTENT_TEST_ID)).toBeVisible(); expect(getByTestId(`${SESSION_PREVIEW_TEST_ID}LeftSection`)).toBeInTheDocument(); expect(getByTestId(`${ANALYZER_PREVIEW_TEST_ID}LeftSection`)).toBeInTheDocument(); + expect(queryByTestId(`${GRAPH_PREVIEW_TEST_ID}LeftSection`)).not.toBeInTheDocument(); + }); + + it('should render the graph preview component if the feature is enabled', () => { + (useExpandSection as jest.Mock).mockReturnValue(true); + useIsExperimentalFeatureEnabledMock.mockReturnValue(true); + + const { getByTestId } = renderVisualizationsSection(); + + expect(getByTestId(`${GRAPH_PREVIEW_TEST_ID}LeftSection`)).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx index afb828ebdb4e8..c328036eece43 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.tsx @@ -13,6 +13,10 @@ import { AnalyzerPreviewContainer } from './analyzer_preview_container'; import { SessionPreviewContainer } from './session_preview_container'; import { ExpandableSection } from './expandable_section'; import { VISUALIZATIONS_TEST_ID } from './test_ids'; +import { GraphPreviewContainer } from './graph_preview_container'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { useDocumentDetailsContext } from '../../shared/context'; +import { useGraphPreview } from '../hooks/use_graph_preview'; const KEY = 'visualizations'; @@ -21,6 +25,17 @@ const KEY = 'visualizations'; */ export const VisualizationsSection = memo(() => { const expanded = useExpandSection({ title: KEY, defaultValue: false }); + const graphVisualizationInFlyoutEnabled = useIsExperimentalFeatureEnabled( + 'graphVisualizationInFlyoutEnabled' + ); + + const { dataAsNestedObject, getFieldsData } = useDocumentDetailsContext(); + + // Decide whether to show the graph preview or not + const { isAuditLog: isGraphPreviewEnabled } = useGraphPreview({ + getFieldsData, + ecsData: dataAsNestedObject, + }); return ( { + {graphVisualizationInFlyoutEnabled && isGraphPreviewEnabled && ( + <> + + + + )} ); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.ts new file mode 100644 index 0000000000000..2304cfb8d4fd2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.ts @@ -0,0 +1,83 @@ +/* + * 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 { useQuery } from '@tanstack/react-query'; +import type { + GraphRequest, + GraphResponse, +} from '@kbn/cloud-security-posture-common/types/graph/latest'; +import { EVENT_GRAPH_VISUALIZATION_API } from '../../../../../common/constants'; +import { useHttp } from '../../../../common/lib/kibana'; + +/** + * Interface for the input parameters of the useFetchGraphData hook. + */ +export interface UseFetchGraphDataParams { + /** + * The request object containing the query parameters for the graph data. + */ + req: GraphRequest; + /** + * Optional configuration options for the query. + */ + options?: { + /** + * If false, the query will not automatically run. + * Defaults to true. + */ + enabled?: boolean; + }; +} + +/** + * Interface for the result of the useFetchGraphData hook. + */ +export interface UseFetchGraphDataResult { + /** + * Indicates if the query is currently loading. + */ + isLoading: boolean; + /** + * Indicates if there was an error during the query. + */ + isError: boolean; + /** + * The data returned from the query. + */ + data?: GraphResponse; +} + +/** + * Hook to fetch event's graph visualization data. + * + * @param params - The input parameters for the hook. + * @returns The result of the hook. + */ +export const useFetchGraphData = ({ + req, + options, +}: UseFetchGraphDataParams): UseFetchGraphDataResult => { + const { actorIds, eventIds, start, end } = req.query; + const http = useHttp(); + + const { isLoading, isError, data } = useQuery( + ['useFetchGraphData', actorIds, eventIds, start, end], + () => { + return http.post(EVENT_GRAPH_VISUALIZATION_API, { + version: '1', + body: JSON.stringify(req), + }); + }, + options + ); + + return { + isLoading, + isError, + data, + }; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_graph_preview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_graph_preview.test.tsx new file mode 100644 index 0000000000000..ff6118ec9b743 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_graph_preview.test.tsx @@ -0,0 +1,137 @@ +/* + * 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 type { RenderHookResult } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; +import type { UseGraphPreviewParams, UseGraphPreviewResult } from './use_graph_preview'; +import { useGraphPreview } from './use_graph_preview'; +import type { GetFieldsData } from '../../shared/hooks/use_get_fields_data'; +import { mockFieldData } from '../../shared/mocks/mock_get_fields_data'; + +describe('useGraphPreview', () => { + let hookResult: RenderHookResult; + + it(`should return false when missing actor`, () => { + const getFieldsData: GetFieldsData = (field: string) => { + if (field === 'kibana.alert.original_event.id') { + return field; + } + return mockFieldData[field]; + }; + + hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + initialProps: { + getFieldsData, + ecsData: { + _id: 'id', + event: { + action: ['action'], + }, + }, + }, + }); + + expect(hookResult.result.current.isAuditLog).toEqual(false); + }); + + it(`should return false when missing event.action`, () => { + const getFieldsData: GetFieldsData = (field: string) => { + if (field === 'kibana.alert.original_event.id') { + return 'eventId'; + } else if (field === 'actor.entity.id') { + return 'actorId'; + } + return mockFieldData[field]; + }; + + hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + initialProps: { + getFieldsData, + ecsData: { + _id: 'id', + }, + }, + }); + + expect(hookResult.result.current.isAuditLog).toEqual(false); + }); + + it(`should return false when missing original_event.id`, () => { + const getFieldsData: GetFieldsData = (field: string) => { + if (field === 'actor.entity.id') { + return 'actorId'; + } + return mockFieldData[field]; + }; + + hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + initialProps: { + getFieldsData, + ecsData: { + _id: 'id', + event: { + action: ['action'], + }, + }, + }, + }); + + expect(hookResult.result.current.isAuditLog).toEqual(false); + }); + + it(`should return true when alert is has graph preview`, () => { + const getFieldsData: GetFieldsData = (field: string) => { + if (field === 'kibana.alert.original_event.id') { + return 'eventId'; + } else if (field === 'actor.entity.id') { + return 'actorId'; + } + + return mockFieldData[field]; + }; + + hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + initialProps: { + getFieldsData, + ecsData: { + _id: 'id', + event: { + action: ['action'], + }, + }, + }, + }); + + expect(hookResult.result.current.isAuditLog).toEqual(true); + }); + + it(`should return true when alert is has graph preview with multiple values`, () => { + const getFieldsData: GetFieldsData = (field: string) => { + if (field === 'kibana.alert.original_event.id') { + return ['id1', 'id2']; + } else if (field === 'actor.entity.id') { + return ['actorId1', 'actorId2']; + } + + return mockFieldData[field]; + }; + + hookResult = renderHook((props: UseGraphPreviewParams) => useGraphPreview(props), { + initialProps: { + getFieldsData, + ecsData: { + _id: 'id', + event: { + action: ['action1', 'action2'], + }, + }, + }, + }); + + expect(hookResult.result.current.isAuditLog).toEqual(true); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_graph_preview.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_graph_preview.ts new file mode 100644 index 0000000000000..d833c0aa86dbc --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_graph_preview.ts @@ -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 type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { get } from 'lodash/fp'; +import type { GetFieldsData } from '../../shared/hooks/use_get_fields_data'; +import { getFieldArray } from '../../shared/utils'; + +export interface UseGraphPreviewParams { + /** + * Retrieves searchHit values for the provided field + */ + getFieldsData: GetFieldsData; + + /** + * An object with top level fields from the ECS object + */ + ecsData: Ecs; +} +/** + * Interface for the result of the useGraphPreview hook + */ +export interface UseGraphPreviewResult { + /** + * Array of event IDs associated with the alert + */ + eventIds: string[]; + + /** + * Array of actor entity IDs associated with the alert + */ + actorIds: string[]; + + /** + * Action associated with the event + */ + action: string | undefined; + + /** + * Boolean indicating if the event is an audit log (contains event ids, actor ids and action) + */ + isAuditLog: boolean; +} + +/** + * Hook that returns the graph view configuration if the graph view is available for the alert + */ +export const useGraphPreview = ({ + getFieldsData, + ecsData, +}: UseGraphPreviewParams): UseGraphPreviewResult => { + const originalEventId = getFieldsData('kibana.alert.original_event.id'); + const eventId = getFieldsData('event.id'); + const eventIds = originalEventId ? getFieldArray(originalEventId) : getFieldArray(eventId); + + const actorIds = getFieldArray(getFieldsData('actor.entity.id')); + const action = get(['event', 'action'], ecsData); + const isAuditLog = actorIds.length > 0 && action?.length > 0 && eventIds.length > 0; + + return { eventIds, actorIds, action, isAuditLog }; +}; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 2b11529ea4d72..15d082d32edda 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -220,6 +220,7 @@ "@kbn/cloud-security-posture", "@kbn/security-solution-distribution-bar", "@kbn/cloud-security-posture-common", + "@kbn/cloud-security-posture-graph", "@kbn/presentation-publishing", "@kbn/entityManager-plugin", "@kbn/entities-schema", diff --git a/x-pack/test/api_integration/apis/synthetics/add_edit_params.ts b/x-pack/test/api_integration/apis/synthetics/add_edit_params.ts index 4de02eb80b30c..7b27aaa621f46 100644 --- a/x-pack/test/api_integration/apis/synthetics/add_edit_params.ts +++ b/x-pack/test/api_integration/apis/synthetics/add_edit_params.ts @@ -10,6 +10,7 @@ import { pick } from 'lodash'; import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants'; import expect from '@kbn/expect'; import { syntheticsParamType } from '@kbn/synthetics-plugin/common/types/saved_objects'; +import { SyntheticsMonitorTestService } from './services/synthetics_monitor_test_service'; import { FtrProviderContext } from '../../ftr_provider_context'; import { PrivateLocationTestService } from './services/private_location_test_service'; @@ -21,12 +22,15 @@ export default function ({ getService }: FtrProviderContext) { describe('AddEditParams', function () { this.tags('skipCloud'); const supertestAPI = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const kServer = getService('kibanaServer'); const testParam = { key: 'test', value: 'test', }; const testPrivateLocations = new PrivateLocationTestService(getService); + const monitorTestService = new SyntheticsMonitorTestService(getService); before(async () => { await testPrivateLocations.installSyntheticsPackage(); @@ -93,6 +97,12 @@ export default function ({ getService }: FtrProviderContext) { const param = getResponse.body[0]; assertHas(param, testParam); + await supertestAPI + .put(SYNTHETICS_API_URLS.PARAMS + '/' + param.id) + .set('kbn-xsrf', 'true') + .send({}) + .expect(400); + await supertestAPI .put(SYNTHETICS_API_URLS.PARAMS + '/' + param.id) .set('kbn-xsrf', 'true') @@ -107,6 +117,55 @@ export default function ({ getService }: FtrProviderContext) { assertHas(actualUpdatedParam, expectedUpdatedParam); }); + it('handles partial editing a param', async () => { + const newParam = { + key: 'testUpdated', + value: 'testUpdated', + tags: ['a tag'], + description: 'test description', + }; + + const response = await supertestAPI + .post(SYNTHETICS_API_URLS.PARAMS) + .set('kbn-xsrf', 'true') + .send(newParam) + .expect(200); + const paramId = response.body.id; + + const getResponse = await supertestAPI + .get(SYNTHETICS_API_URLS.PARAMS + '/' + paramId) + .set('kbn-xsrf', 'true') + .expect(200); + assertHas(getResponse.body, newParam); + + await supertestAPI + .put(SYNTHETICS_API_URLS.PARAMS + '/' + paramId) + .set('kbn-xsrf', 'true') + .send({ + key: 'testUpdated', + }) + .expect(200); + + await supertestAPI + .put(SYNTHETICS_API_URLS.PARAMS + '/' + paramId) + .set('kbn-xsrf', 'true') + .send({ + key: 'testUpdatedAgain', + value: 'testUpdatedAgain', + }) + .expect(200); + + const updatedGetResponse = await supertestAPI + .get(SYNTHETICS_API_URLS.PARAMS + '/' + paramId) + .set('kbn-xsrf', 'true') + .expect(200); + assertHas(updatedGetResponse.body, { + ...newParam, + key: 'testUpdatedAgain', + value: 'testUpdatedAgain', + }); + }); + it('handles spaces', async () => { const SPACE_ID = `test-space-${uuidv4()}`; const SPACE_NAME = `test-space-name ${uuidv4()}`; @@ -277,5 +336,22 @@ export default function ({ getService }: FtrProviderContext) { expect(getResponse.body[0].namespaces).eql(['*']); assertHas(getResponse.body[0], testParam); }); + + it('should not return values for non admin user', async () => { + const { username, password } = await monitorTestService.addsNewSpace(); + const resp = await supertestWithoutAuth + .get(`${SYNTHETICS_API_URLS.PARAMS}`) + .auth(username, password) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + const params = resp.body; + expect(params.length).to.eql(6); + params.forEach((param: any) => { + expect(param.value).to.eql(undefined); + expect(param.key).to.not.empty(); + }); + }); }); } diff --git a/x-pack/test/cloud_security_posture_functional/config.ts b/x-pack/test/cloud_security_posture_functional/config.ts index 28283fe427949..7e80788ffccfe 100644 --- a/x-pack/test/cloud_security_posture_functional/config.ts +++ b/x-pack/test/cloud_security_posture_functional/config.ts @@ -38,6 +38,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { * 1. release a new package to EPR * 2. merge the updated version number change to kibana */ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'graphVisualizationInFlyoutEnabled', + ])}`, `--xpack.fleet.packages.0.name=cloud_security_posture`, `--xpack.fleet.packages.0.version=${CLOUD_SECURITY_PLUGIN_VERSION}`, // `--xpack.fleet.registryUrl=https://localhost:8080`, diff --git a/x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit/data.json b/x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit/data.json new file mode 100644 index 0000000000000..5e3d4cdfdffd5 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit/data.json @@ -0,0 +1,634 @@ +{ + "type": "doc", + "value": { + "data_stream": "logs-gcp.audit-default", + "id": "1", + "index": ".ds-logs-gcp.audit-default-2024.10.07-000001", + "source": { + "@timestamp": "2024-09-01T12:34:56.789Z", + "actor": { + "entity": { + "id": "admin@example.com" + } + }, + "client": { + "user": { + "email": "admin@example.com" + } + }, + "cloud": { + "project": { + "id": "your-project-id" + }, + "provider": "gcp" + }, + "ecs": { + "version": "8.11.0" + }, + "event": { + "action": "google.iam.admin.v1.CreateRole", + "agent_id_status": "missing", + "category": [ + "session", + "network", + "configuration" + ], + "id": "kabcd1234efgh5678", + "ingested": "2024-10-07T17:47:35Z", + "kind": "event", + "outcome": "success", + "provider": "activity", + "type": [ + "end", + "access", + "allowed" + ] + }, + "gcp": { + "audit": { + "authorization_info": [ + { + "granted": true, + "permission": "iam.roles.create", + "resource": "projects/your-project-id" + } + ], + "logentry_operation": { + "id": "operation-0987654321" + }, + "request": { + "@type": "type.googleapis.com/google.iam.admin.v1.CreateRoleRequest", + "parent": "projects/your-project-id", + "role": { + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "title": "Custom Role" + }, + "roleId": "customRole" + }, + "resource_name": "projects/your-project-id/roles/customRole", + "response": { + "@type": "type.googleapis.com/google.iam.admin.v1.Role", + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "stage": "GA", + "title": "Custom Role" + }, + "type": "type.googleapis.com/google.cloud.audit.AuditLog" + } + }, + "log": { + "level": "NOTICE", + "logger": "projects/your-project-id/logs/cloudaudit.googleapis.com%2Factivity" + }, + "related": { + "ip": [ + "10.0.0.1" + ], + "user": [ + "admin@example.com" + ] + }, + "service": { + "name": "iam.googleapis.com" + }, + "source": { + "ip": "10.0.0.1" + }, + "tags": [ + "_geoip_database_unavailable_GeoLite2-City.mmdb", + "_geoip_database_unavailable_GeoLite2-ASN.mmdb" + ], + "target": { + "entity": { + "id": "projects/your-project-id/roles/customRole" + } + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "Other", + "original": "google-cloud-sdk/324.0.0" + } + } + } +} + +{ + "type": "doc", + "value": { + "data_stream": "logs-gcp.audit-default", + "id": "2", + "index": ".ds-logs-gcp.audit-default-2024.10.07-000001", + "source": { + "@timestamp": "2024-09-01T12:34:56.789Z", + "actor": { + "entity": { + "id": "admin2@example.com" + } + }, + "client": { + "user": { + "email": "admin2@example.com" + } + }, + "cloud": { + "project": { + "id": "your-project-id" + }, + "provider": "gcp" + }, + "ecs": { + "version": "8.11.0" + }, + "event": { + "action": "google.iam.admin.v1.CreateRole", + "agent_id_status": "missing", + "category": [ + "session", + "network", + "configuration" + ], + "id": "failed-event", + "ingested": "2024-10-07T17:47:35Z", + "kind": "event", + "outcome": "failed", + "provider": "activity", + "type": [ + "end", + "access", + "allowed" + ] + }, + "gcp": { + "audit": { + "authorization_info": [ + { + "granted": true, + "permission": "iam.roles.create", + "resource": "projects/your-project-id" + } + ], + "logentry_operation": { + "id": "operation-0987654321" + }, + "request": { + "@type": "type.googleapis.com/google.iam.admin.v1.CreateRoleRequest", + "parent": "projects/your-project-id", + "role": { + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "title": "Custom Role" + }, + "roleId": "customRole" + }, + "resource_name": "projects/your-project-id/roles/customRole", + "response": { + "@type": "type.googleapis.com/google.iam.admin.v1.Role", + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "stage": "GA", + "title": "Custom Role" + }, + "type": "type.googleapis.com/google.cloud.audit.AuditLog" + } + }, + "log": { + "level": "NOTICE", + "logger": "projects/your-project-id/logs/cloudaudit.googleapis.com%2Factivity" + }, + "related": { + "ip": [ + "10.0.0.1" + ], + "user": [ + "admin2@example.com" + ] + }, + "service": { + "name": "iam.googleapis.com" + }, + "source": { + "ip": "10.0.0.1" + }, + "tags": [ + "_geoip_database_unavailable_GeoLite2-City.mmdb", + "_geoip_database_unavailable_GeoLite2-ASN.mmdb" + ], + "target": { + "entity": { + "id": "projects/your-project-id/roles/customRole" + } + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "Other", + "original": "google-cloud-sdk/324.0.0" + } + } + } +} + +{ + "type": "doc", + "value": { + "data_stream": "logs-gcp.audit-default", + "id": "3", + "index": ".ds-logs-gcp.audit-default-2024.10.07-000001", + "source": { + "@timestamp": "2024-09-01T12:34:56.789Z", + "actor": { + "entity": { + "id": "admin3@example.com" + } + }, + "client": { + "user": { + "email": "admin3@example.com" + } + }, + "cloud": { + "project": { + "id": "your-project-id" + }, + "provider": "gcp" + }, + "ecs": { + "version": "8.11.0" + }, + "event": { + "action": "google.iam.admin.v1.CreateRole", + "agent_id_status": "missing", + "category": [ + "session", + "network", + "configuration" + ], + "id": "grouped-event1", + "ingested": "2024-10-07T17:47:35Z", + "kind": "event", + "outcome": "failed", + "provider": "activity", + "type": [ + "end", + "access", + "allowed" + ] + }, + "gcp": { + "audit": { + "authorization_info": [ + { + "granted": true, + "permission": "iam.roles.create", + "resource": "projects/your-project-id" + } + ], + "logentry_operation": { + "id": "operation-0987654321" + }, + "request": { + "@type": "type.googleapis.com/google.iam.admin.v1.CreateRoleRequest", + "parent": "projects/your-project-id", + "role": { + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "title": "Custom Role" + }, + "roleId": "customRole" + }, + "resource_name": "projects/your-project-id/roles/customRole", + "response": { + "@type": "type.googleapis.com/google.iam.admin.v1.Role", + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "stage": "GA", + "title": "Custom Role" + }, + "type": "type.googleapis.com/google.cloud.audit.AuditLog" + } + }, + "log": { + "level": "NOTICE", + "logger": "projects/your-project-id/logs/cloudaudit.googleapis.com%2Factivity" + }, + "related": { + "ip": [ + "10.0.0.1" + ], + "user": [ + "admin3@example.com" + ] + }, + "service": { + "name": "iam.googleapis.com" + }, + "source": { + "ip": "10.0.0.1" + }, + "tags": [ + "_geoip_database_unavailable_GeoLite2-City.mmdb", + "_geoip_database_unavailable_GeoLite2-ASN.mmdb" + ], + "target": { + "entity": { + "id": "projects/your-project-id/roles/customRole" + } + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "Other", + "original": "google-cloud-sdk/324.0.0" + } + } + } +} + +{ + "type": "doc", + "value": { + "data_stream": "logs-gcp.audit-default", + "id": "4", + "index": ".ds-logs-gcp.audit-default-2024.10.07-000001", + "source": { + "@timestamp": "2024-09-01T12:34:56.789Z", + "actor": { + "entity": { + "id": "admin3@example.com" + } + }, + "client": { + "user": { + "email": "admin3@example.com" + } + }, + "cloud": { + "project": { + "id": "your-project-id" + }, + "provider": "gcp" + }, + "ecs": { + "version": "8.11.0" + }, + "event": { + "action": "google.iam.admin.v1.CreateRole", + "agent_id_status": "missing", + "category": [ + "session", + "network", + "configuration" + ], + "id": "grouped-event2", + "ingested": "2024-10-07T17:47:35Z", + "kind": "event", + "outcome": "success", + "provider": "activity", + "type": [ + "end", + "access", + "allowed" + ] + }, + "gcp": { + "audit": { + "authorization_info": [ + { + "granted": true, + "permission": "iam.roles.create", + "resource": "projects/your-project-id" + } + ], + "logentry_operation": { + "id": "operation-0987654321" + }, + "request": { + "@type": "type.googleapis.com/google.iam.admin.v1.CreateRoleRequest", + "parent": "projects/your-project-id", + "role": { + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "title": "Custom Role" + }, + "roleId": "customRole" + }, + "resource_name": "projects/your-project-id/roles/customRole", + "response": { + "@type": "type.googleapis.com/google.iam.admin.v1.Role", + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "stage": "GA", + "title": "Custom Role" + }, + "type": "type.googleapis.com/google.cloud.audit.AuditLog" + } + }, + "log": { + "level": "NOTICE", + "logger": "projects/your-project-id/logs/cloudaudit.googleapis.com%2Factivity" + }, + "related": { + "ip": [ + "10.0.0.1" + ], + "user": [ + "admin3@example.com" + ] + }, + "service": { + "name": "iam.googleapis.com" + }, + "source": { + "ip": "10.0.0.1" + }, + "tags": [ + "_geoip_database_unavailable_GeoLite2-City.mmdb", + "_geoip_database_unavailable_GeoLite2-ASN.mmdb" + ], + "target": { + "entity": { + "id": "projects/your-project-id/roles/customRole" + } + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "Other", + "original": "google-cloud-sdk/324.0.0" + } + } + } +} + +{ + "type": "doc", + "value": { + "data_stream": "logs-gcp.audit-default", + "id": "5", + "index": ".ds-logs-gcp.audit-default-2024.10.07-000001", + "source": { + "@timestamp": "2024-09-01T12:34:56.789Z", + "actor": { + "entity": { + "id": "admin4@example.com" + } + }, + "client": { + "user": { + "email": "admin4@example.com" + } + }, + "cloud": { + "project": { + "id": "your-project-id" + }, + "provider": "gcp" + }, + "ecs": { + "version": "8.11.0" + }, + "event": { + "action": "google.iam.admin.v1.CreateRole", + "agent_id_status": "missing", + "category": [ + "session", + "network", + "configuration" + ], + "id": "host-event", + "ingested": "2024-10-07T17:47:35Z", + "kind": "event", + "outcome": "success", + "provider": "activity", + "type": [ + "end", + "access", + "allowed" + ] + }, + "gcp": { + "audit": { + "authorization_info": [ + { + "granted": true, + "permission": "iam.roles.create", + "resource": "projects/your-project-id" + } + ], + "logentry_operation": { + "id": "operation-0987654321" + }, + "request": { + "@type": "type.googleapis.com/google.iam.admin.v1.CreateRoleRequest", + "parent": "projects/your-project-id", + "role": { + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "title": "Custom Role" + }, + "roleId": "customRole" + }, + "resource_name": "projects/your-project-id/roles/customRole", + "response": { + "@type": "type.googleapis.com/google.iam.admin.v1.Role", + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "stage": "GA", + "title": "Custom Role" + }, + "type": "type.googleapis.com/google.cloud.audit.AuditLog" + } + }, + "host": { + "hostname": "host1.example.com", + "name": "host1.example.com", + "ip": "192.168.1.1", + "os": { + "name": "Linux", + "version": "5.4.0-42-generic" + } + }, + "log": { + "level": "NOTICE", + "logger": "projects/your-project-id/logs/cloudaudit.googleapis.com%2Factivity" + }, + "related": { + "ip": [ + "10.0.0.1" + ], + "user": [ + "admin4@example.com" + ] + }, + "service": { + "name": "iam.googleapis.com" + }, + "source": { + "ip": "10.0.0.1" + }, + "tags": [ + "_geoip_database_unavailable_GeoLite2-City.mmdb", + "_geoip_database_unavailable_GeoLite2-ASN.mmdb" + ], + "target": { + "entity": { + "id": "projects/your-project-id/roles/customRole" + } + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "Other", + "original": "google-cloud-sdk/324.0.0" + } + } + } +} + diff --git a/x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit/mappings.json b/x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit/mappings.json new file mode 100644 index 0000000000000..085fc6d11d475 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit/mappings.json @@ -0,0 +1,628 @@ +{ + "type": "data_stream", + "value": { + "data_stream": "logs-gcp.audit-default", + "template": { + "_meta": { + "managed": true, + "managed_by": "fleet", + "package": { + "name": "gcp" + } + }, + "data_stream": { + "allow_custom_routing": false, + "hidden": false + }, + "ignore_missing_component_templates": [ + "logs-gcp.audit@custom" + ], + "index_patterns": [ + "logs-gcp.audit-*" + ], + "name": "logs-gcp.audit", + "priority": 200, + "template": { + "mappings": { + "_meta": { + "managed": true, + "managed_by": "fleet", + "package": { + "name": "gcp" + } + }, + "date_detection": false, + "dynamic_templates": [ + { + "ecs_message_match_only_text": { + "mapping": { + "type": "match_only_text" + }, + "path_match": [ + "message", + "*.message" + ], + "unmatch_mapping_type": "object" + } + }, + { + "ecs_non_indexed_keyword": { + "mapping": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "path_match": [ + "*event.original" + ] + } + }, + { + "ecs_non_indexed_long": { + "mapping": { + "doc_values": false, + "index": false, + "type": "long" + }, + "path_match": [ + "*.x509.public_key_exponent" + ] + } + }, + { + "ecs_ip": { + "mapping": { + "type": "ip" + }, + "match_mapping_type": "string", + "path_match": [ + "ip", + "*.ip", + "*_ip" + ] + } + }, + { + "ecs_wildcard": { + "mapping": { + "type": "wildcard" + }, + "path_match": [ + "*.io.text", + "*.message_id", + "*registry.data.strings", + "*url.path" + ], + "unmatch_mapping_type": "object" + } + }, + { + "ecs_path_match_wildcard_and_match_only_text": { + "mapping": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "path_match": [ + "*.body.content", + "*url.full", + "*url.original" + ], + "unmatch_mapping_type": "object" + } + }, + { + "ecs_match_wildcard_and_match_only_text": { + "mapping": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "match": [ + "*command_line", + "*stack_trace" + ], + "unmatch_mapping_type": "object" + } + }, + { + "ecs_path_match_keyword_and_match_only_text": { + "mapping": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "keyword" + }, + "path_match": [ + "*.title", + "*.executable", + "*.name", + "*.working_directory", + "*.full_name", + "*file.path", + "*file.target_path", + "*os.full", + "*email.subject", + "*vulnerability.description", + "*user_agent.original" + ], + "unmatch_mapping_type": "object" + } + }, + { + "ecs_date": { + "mapping": { + "type": "date" + }, + "path_match": [ + "*.timestamp", + "*_timestamp", + "*.not_after", + "*.not_before", + "*.accessed", + "created", + "*.created", + "*.installed", + "*.creation_date", + "*.ctime", + "*.mtime", + "ingested", + "*.ingested", + "*.start", + "*.end", + "*.indicator.first_seen", + "*.indicator.last_seen", + "*.indicator.modified_at", + "*threat.enrichments.matched.occurred" + ], + "unmatch_mapping_type": "object" + } + }, + { + "ecs_path_match_float": { + "mapping": { + "type": "float" + }, + "path_match": [ + "*.score.*", + "*_score*" + ], + "path_unmatch": "*.version", + "unmatch_mapping_type": "object" + } + }, + { + "ecs_usage_double_scaled_float": { + "mapping": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "match_mapping_type": [ + "double", + "long", + "string" + ], + "path_match": "*.usage" + } + }, + { + "ecs_geo_point": { + "mapping": { + "type": "geo_point" + }, + "path_match": [ + "*.geo.location" + ] + } + }, + { + "ecs_flattened": { + "mapping": { + "type": "flattened" + }, + "match_mapping_type": "object", + "path_match": [ + "*structured_data", + "*exports", + "*imports" + ] + } + }, + { + "all_strings_to_keywords": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "ignore_malformed": false, + "type": "date" + }, + "cloud": { + "properties": { + "image": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "constant_keyword" + }, + "namespace": { + "type": "constant_keyword" + }, + "type": { + "type": "constant_keyword" + } + } + }, + "data_stream.dataset": { + "type": "constant_keyword" + }, + "data_stream.namespace": { + "type": "constant_keyword" + }, + "data_stream.type": { + "type": "constant_keyword", + "value": "logs" + }, + "event": { + "properties": { + "agent_id_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "dataset": { + "type": "constant_keyword", + "value": "gcp.audit" + }, + "ingested": { + "format": "strict_date_time_no_millis||strict_date_optional_time||epoch_millis", + "ignore_malformed": false, + "type": "date" + }, + "module": { + "type": "constant_keyword", + "value": "gcp" + } + } + }, + "gcp": { + "properties": { + "audit": { + "properties": { + "authentication_info": { + "properties": { + "authority_selector": { + "ignore_above": 1024, + "type": "keyword" + }, + "principal_email": { + "ignore_above": 1024, + "type": "keyword" + }, + "principal_subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "service_account_delegation_info": { + "type": "flattened" + }, + "service_account_key_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "third_party_principal": { + "type": "flattened" + } + } + }, + "authorization_info": { + "properties": { + "granted": { + "type": "boolean" + }, + "permission": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource_attributes": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "flattened": { + "type": "flattened" + }, + "labels": { + "type": "flattened" + }, + "logentry_operation": { + "properties": { + "first": { + "type": "boolean" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "last": { + "type": "boolean" + }, + "producer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "metadata": { + "type": "flattened" + }, + "method_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "num_response_items": { + "type": "long" + }, + "policy_violation_info": { + "properties": { + "payload": { + "type": "flattened" + }, + "resource_tags": { + "type": "flattened" + }, + "resource_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "violations": { + "properties": { + "checkedValue": { + "ignore_above": 1024, + "type": "keyword" + }, + "constraint": { + "ignore_above": 1024, + "type": "keyword" + }, + "errorMessage": { + "ignore_above": 1024, + "type": "keyword" + }, + "policyType": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + } + } + }, + "request": { + "type": "flattened" + }, + "request_metadata": { + "properties": { + "caller_ip": { + "type": "ip" + }, + "caller_supplied_user_agent": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw": { + "properties": { + "caller_ip": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "resource_location": { + "properties": { + "current_locations": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resource_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "response": { + "type": "flattened" + }, + "service_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "properties": { + "code": { + "type": "long" + }, + "details": { + "type": "flattened" + }, + "message": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "instance": { + "properties": { + "project_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vpc": { + "properties": { + "project_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "subnetwork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "vpc_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "source": { + "properties": { + "instance": { + "properties": { + "project_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vpc": { + "properties": { + "project_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "subnetwork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "vpc_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "host": { + "properties": { + "containerized": { + "type": "boolean" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "input": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "log": { + "properties": { + "offset": { + "type": "long" + } + } + } + } + }, + "settings": { + "index": { + "codec": "best_compression", + "final_pipeline": ".fleet_final_pipeline-1", + "mapping": { + "ignore_malformed": "true", + "total_fields": { + "ignore_dynamic_beyond_limit": "true", + "limit": "1000" + } + } + } + } + } + } + } +} diff --git a/x-pack/test/cloud_security_posture_functional/es_archives/security_alerts/data.json.gz b/x-pack/test/cloud_security_posture_functional/es_archives/security_alerts/data.json.gz new file mode 100644 index 0000000000000..93b2c20b81c86 Binary files /dev/null and b/x-pack/test/cloud_security_posture_functional/es_archives/security_alerts/data.json.gz differ diff --git a/x-pack/test/cloud_security_posture_functional/es_archives/security_alerts/mappings.json b/x-pack/test/cloud_security_posture_functional/es_archives/security_alerts/mappings.json new file mode 100644 index 0000000000000..d13b6edbd768f --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/es_archives/security_alerts/mappings.json @@ -0,0 +1,8667 @@ +{ + "type": "index", + "value": { + "aliases": { + ".alerts-security.alerts-default": { + "is_write_index": true + }, + ".siem-signals-default": { + "is_write_index": false + } + }, + "index": ".internal.alerts-security.alerts-default-000001", + "mappings": { + "_meta": { + "kibana": { + "version": "9.0.0" + }, + "managed": true, + "namespace": "default" + }, + "dynamic": "false", + "properties": { + "@timestamp": { + "ignore_malformed": false, + "type": "date" + }, + "agent": { + "properties": { + "build": { + "properties": { + "original": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "origin": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "target": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "container": { + "properties": { + "cpu": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "disk": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "hash": { + "properties": { + "all": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "memory": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "egress": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "ingress": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + }, + "security_context": { + "properties": { + "privileged": { + "type": "boolean" + } + } + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "device": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "manufacturer": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "properties": { + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "type": "keyword" + } + } + }, + "email": { + "properties": { + "attachments": { + "properties": { + "file": { + "properties": { + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + } + } + } + }, + "type": "nested" + }, + "bcc": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cc": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "content_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "delivery_timestamp": { + "type": "date" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "from": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "local_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message_id": { + "type": "wildcard" + }, + "origination_timestamp": { + "type": "date" + }, + "reply_to": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "sender": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "subject": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "to": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x_mailer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "type": "match_only_text" + }, + "stack_trace": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "agent_id_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "faas": { + "properties": { + "coldstart": { + "type": "boolean" + }, + "execution": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "var_entropy": { + "type": "long" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "macho": { + "properties": { + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "symhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "asset": { + "properties": { + "criticality": { + "type": "keyword" + } + } + }, + "boot": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cpu": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "disk": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "egress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + }, + "ingress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pid_ns_ino": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "calculated_score": { + "type": "float" + }, + "calculated_score_norm": { + "type": "float" + }, + "static_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "static_score": { + "type": "float" + }, + "static_score_norm": { + "type": "float" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + } + } + }, + "bytes": { + "type": "long" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + } + } + }, + "bytes": { + "type": "long" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kibana": { + "properties": { + "alert": { + "properties": { + "action_group": { + "type": "keyword" + }, + "ancestors": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "building_block_type": { + "type": "keyword" + }, + "case_ids": { + "type": "keyword" + }, + "consecutive_matches": { + "type": "long" + }, + "depth": { + "type": "long" + }, + "duration": { + "properties": { + "us": { + "type": "long" + } + } + }, + "end": { + "type": "date" + }, + "flapping": { + "type": "boolean" + }, + "flapping_history": { + "type": "boolean" + }, + "group": { + "properties": { + "id": { + "type": "keyword" + }, + "index": { + "type": "integer" + } + } + }, + "host": { + "properties": { + "criticality_level": { + "type": "keyword" + } + } + }, + "instance": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "intended_timestamp": { + "type": "date" + }, + "last_detected": { + "type": "date" + }, + "maintenance_window_ids": { + "type": "keyword" + }, + "new_terms": { + "type": "keyword" + }, + "original_event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "agent_id_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "ignore_above": 1024, + "type": "keyword" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original_time": { + "type": "date" + }, + "previous_action_group": { + "type": "keyword" + }, + "reason": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "rule": { + "properties": { + "author": { + "type": "keyword" + }, + "building_block_type": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "enabled": { + "type": "keyword" + }, + "exceptions_list": { + "type": "object" + }, + "execution": { + "properties": { + "timestamp": { + "type": "date" + }, + "uuid": { + "type": "keyword" + } + } + }, + "false_positives": { + "type": "keyword" + }, + "from": { + "type": "keyword" + }, + "immutable": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "license": { + "type": "keyword" + }, + "max_signals": { + "type": "long" + }, + "name": { + "type": "keyword" + }, + "note": { + "type": "keyword" + }, + "parameters": { + "ignore_above": 4096, + "type": "flattened" + }, + "producer": { + "type": "keyword" + }, + "references": { + "type": "keyword" + }, + "revision": { + "type": "long" + }, + "rule_id": { + "type": "keyword" + }, + "rule_name_override": { + "type": "keyword" + }, + "rule_type_id": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + }, + "subtechnique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } + } + } + }, + "timeline_id": { + "type": "keyword" + }, + "timeline_title": { + "type": "keyword" + }, + "timestamp_override": { + "type": "keyword" + }, + "to": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "uuid": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "severity": { + "type": "keyword" + }, + "severity_improving": { + "type": "boolean" + }, + "start": { + "type": "date" + }, + "status": { + "type": "keyword" + }, + "suppression": { + "properties": { + "docs_count": { + "type": "long" + }, + "end": { + "type": "date" + }, + "start": { + "type": "date" + }, + "terms": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + } + } + }, + "system_status": { + "type": "keyword" + }, + "threshold_result": { + "properties": { + "cardinality": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + }, + "count": { + "type": "long" + }, + "from": { + "type": "date" + }, + "terms": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + } + } + }, + "time_range": { + "format": "epoch_millis||strict_date_optional_time", + "type": "date_range" + }, + "url": { + "ignore_above": 2048, + "index": false, + "type": "keyword" + }, + "user": { + "properties": { + "criticality_level": { + "type": "keyword" + } + } + }, + "uuid": { + "type": "keyword" + }, + "workflow_assignee_ids": { + "type": "keyword" + }, + "workflow_reason": { + "type": "keyword" + }, + "workflow_status": { + "type": "keyword" + }, + "workflow_status_updated_at": { + "type": "date" + }, + "workflow_tags": { + "type": "keyword" + }, + "workflow_user": { + "type": "keyword" + } + } + }, + "space_ids": { + "type": "keyword" + }, + "version": { + "type": "version" + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "file": { + "properties": { + "path": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "syslog": { + "properties": { + "appname": { + "ignore_above": 1024, + "type": "keyword" + }, + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "msgid": { + "ignore_above": 1024, + "type": "keyword" + }, + "priority": { + "type": "long" + }, + "procid": { + "ignore_above": 1024, + "type": "keyword" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "structured_data": { + "type": "flattened" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "message": { + "type": "match_only_text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "inner": { + "properties": { + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "observer": { + "properties": { + "egress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "orchestrator": { + "properties": { + "api_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "cluster": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource": { + "properties": { + "annotation": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "label": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "var_entropy": { + "type": "long" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "end": { + "type": "date" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "entry_leader": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "attested_groups": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "attested_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "entry_meta": { + "properties": { + "source": { + "properties": { + "ip": { + "type": "ip" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "session_leader": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "vpid": { + "type": "long" + } + } + }, + "start": { + "type": "date" + }, + "vpid": { + "type": "long" + } + } + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "same_as_process": { + "type": "boolean" + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vpid": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "env_vars": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group_leader": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "same_as_process": { + "type": "boolean" + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vpid": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "io": { + "properties": { + "bytes_skipped": { + "properties": { + "length": { + "type": "long" + }, + "offset": { + "type": "long" + } + } + }, + "max_bytes_per_process_exceeded": { + "type": "boolean" + }, + "text": { + "type": "wildcard" + }, + "total_bytes_captured": { + "type": "long" + }, + "total_bytes_skipped": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "macho": { + "properties": { + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "symhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "var_entropy": { + "type": "long" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "end": { + "type": "date" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group_leader": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "vpid": { + "type": "long" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "macho": { + "properties": { + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "symhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "thread": { + "properties": { + "capabilities": { + "properties": { + "effective": { + "ignore_above": 1024, + "type": "keyword" + }, + "permitted": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vpid": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "previous": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "session_leader": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "session_leader": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "vpid": { + "type": "long" + } + } + }, + "start": { + "type": "date" + }, + "vpid": { + "type": "long" + } + } + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "same_as_process": { + "type": "boolean" + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vpid": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "thread": { + "properties": { + "capabilities": { + "properties": { + "effective": { + "ignore_above": 1024, + "type": "keyword" + }, + "permitted": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + }, + "columns": { + "type": "long" + }, + "rows": { + "type": "long" + } + } + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vpid": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "author": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "origin": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "target": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "signal": { + "properties": { + "ancestors": { + "properties": { + "depth": { + "path": "kibana.alert.ancestors.depth", + "type": "alias" + }, + "id": { + "path": "kibana.alert.ancestors.id", + "type": "alias" + }, + "index": { + "path": "kibana.alert.ancestors.index", + "type": "alias" + }, + "type": { + "path": "kibana.alert.ancestors.type", + "type": "alias" + } + } + }, + "depth": { + "path": "kibana.alert.depth", + "type": "alias" + }, + "group": { + "properties": { + "id": { + "path": "kibana.alert.group.id", + "type": "alias" + }, + "index": { + "path": "kibana.alert.group.index", + "type": "alias" + } + } + }, + "original_event": { + "properties": { + "action": { + "path": "kibana.alert.original_event.action", + "type": "alias" + }, + "category": { + "path": "kibana.alert.original_event.category", + "type": "alias" + }, + "code": { + "path": "kibana.alert.original_event.code", + "type": "alias" + }, + "created": { + "path": "kibana.alert.original_event.created", + "type": "alias" + }, + "dataset": { + "path": "kibana.alert.original_event.dataset", + "type": "alias" + }, + "duration": { + "path": "kibana.alert.original_event.duration", + "type": "alias" + }, + "end": { + "path": "kibana.alert.original_event.end", + "type": "alias" + }, + "hash": { + "path": "kibana.alert.original_event.hash", + "type": "alias" + }, + "id": { + "path": "kibana.alert.original_event.id", + "type": "alias" + }, + "kind": { + "path": "kibana.alert.original_event.kind", + "type": "alias" + }, + "module": { + "path": "kibana.alert.original_event.module", + "type": "alias" + }, + "outcome": { + "path": "kibana.alert.original_event.outcome", + "type": "alias" + }, + "provider": { + "path": "kibana.alert.original_event.provider", + "type": "alias" + }, + "reason": { + "path": "kibana.alert.original_event.reason", + "type": "alias" + }, + "risk_score": { + "path": "kibana.alert.original_event.risk_score", + "type": "alias" + }, + "risk_score_norm": { + "path": "kibana.alert.original_event.risk_score_norm", + "type": "alias" + }, + "sequence": { + "path": "kibana.alert.original_event.sequence", + "type": "alias" + }, + "severity": { + "path": "kibana.alert.original_event.severity", + "type": "alias" + }, + "start": { + "path": "kibana.alert.original_event.start", + "type": "alias" + }, + "timezone": { + "path": "kibana.alert.original_event.timezone", + "type": "alias" + }, + "type": { + "path": "kibana.alert.original_event.type", + "type": "alias" + } + } + }, + "original_time": { + "path": "kibana.alert.original_time", + "type": "alias" + }, + "reason": { + "path": "kibana.alert.reason", + "type": "alias" + }, + "rule": { + "properties": { + "author": { + "path": "kibana.alert.rule.author", + "type": "alias" + }, + "building_block_type": { + "path": "kibana.alert.building_block_type", + "type": "alias" + }, + "created_at": { + "path": "kibana.alert.rule.created_at", + "type": "alias" + }, + "created_by": { + "path": "kibana.alert.rule.created_by", + "type": "alias" + }, + "description": { + "path": "kibana.alert.rule.description", + "type": "alias" + }, + "enabled": { + "path": "kibana.alert.rule.enabled", + "type": "alias" + }, + "false_positives": { + "path": "kibana.alert.rule.false_positives", + "type": "alias" + }, + "from": { + "path": "kibana.alert.rule.from", + "type": "alias" + }, + "id": { + "path": "kibana.alert.rule.uuid", + "type": "alias" + }, + "immutable": { + "path": "kibana.alert.rule.immutable", + "type": "alias" + }, + "interval": { + "path": "kibana.alert.rule.interval", + "type": "alias" + }, + "license": { + "path": "kibana.alert.rule.license", + "type": "alias" + }, + "max_signals": { + "path": "kibana.alert.rule.max_signals", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.name", + "type": "alias" + }, + "note": { + "path": "kibana.alert.rule.note", + "type": "alias" + }, + "references": { + "path": "kibana.alert.rule.references", + "type": "alias" + }, + "risk_score": { + "path": "kibana.alert.risk_score", + "type": "alias" + }, + "rule_id": { + "path": "kibana.alert.rule.rule_id", + "type": "alias" + }, + "rule_name_override": { + "path": "kibana.alert.rule.rule_name_override", + "type": "alias" + }, + "severity": { + "path": "kibana.alert.severity", + "type": "alias" + }, + "tags": { + "path": "kibana.alert.rule.tags", + "type": "alias" + }, + "threat": { + "properties": { + "framework": { + "path": "kibana.alert.rule.threat.framework", + "type": "alias" + }, + "tactic": { + "properties": { + "id": { + "path": "kibana.alert.rule.threat.tactic.id", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.threat.tactic.name", + "type": "alias" + }, + "reference": { + "path": "kibana.alert.rule.threat.tactic.reference", + "type": "alias" + } + } + }, + "technique": { + "properties": { + "id": { + "path": "kibana.alert.rule.threat.technique.id", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.threat.technique.name", + "type": "alias" + }, + "reference": { + "path": "kibana.alert.rule.threat.technique.reference", + "type": "alias" + }, + "subtechnique": { + "properties": { + "id": { + "path": "kibana.alert.rule.threat.technique.subtechnique.id", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.threat.technique.subtechnique.name", + "type": "alias" + }, + "reference": { + "path": "kibana.alert.rule.threat.technique.subtechnique.reference", + "type": "alias" + } + } + } + } + } + } + }, + "timeline_id": { + "path": "kibana.alert.rule.timeline_id", + "type": "alias" + }, + "timeline_title": { + "path": "kibana.alert.rule.timeline_title", + "type": "alias" + }, + "timestamp_override": { + "path": "kibana.alert.rule.timestamp_override", + "type": "alias" + }, + "to": { + "path": "kibana.alert.rule.to", + "type": "alias" + }, + "type": { + "path": "kibana.alert.rule.type", + "type": "alias" + }, + "updated_at": { + "path": "kibana.alert.rule.updated_at", + "type": "alias" + }, + "updated_by": { + "path": "kibana.alert.rule.updated_by", + "type": "alias" + }, + "version": { + "path": "kibana.alert.rule.version", + "type": "alias" + } + } + }, + "status": { + "path": "kibana.alert.workflow_status", + "type": "alias" + }, + "threshold_result": { + "properties": { + "cardinality": { + "properties": { + "field": { + "path": "kibana.alert.threshold_result.cardinality.field", + "type": "alias" + }, + "value": { + "path": "kibana.alert.threshold_result.cardinality.value", + "type": "alias" + } + } + }, + "count": { + "path": "kibana.alert.threshold_result.count", + "type": "alias" + }, + "from": { + "path": "kibana.alert.threshold_result.from", + "type": "alias" + }, + "terms": { + "properties": { + "field": { + "path": "kibana.alert.threshold_result.terms.field", + "type": "alias" + }, + "value": { + "path": "kibana.alert.threshold_result.terms.value", + "type": "alias" + } + } + } + } + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "span": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "enrichments": { + "properties": { + "indicator": { + "properties": { + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "confidence": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "var_entropy": { + "type": "long" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "first_seen": { + "type": "date" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "last_seen": { + "type": "date" + }, + "marking": { + "properties": { + "tlp": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlp_version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "modified_at": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "scanner_stats": { + "type": "long" + }, + "sightings": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "type": "wildcard" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "matched": { + "properties": { + "atomic": { + "ignore_above": 1024, + "type": "keyword" + }, + "field": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "index": { + "ignore_above": 1024, + "type": "keyword" + }, + "occurred": { + "type": "date" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "feed": { + "properties": { + "dashboard_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "indicator": { + "properties": { + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "confidence": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "var_entropy": { + "type": "long" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "go_imports": { + "type": "flattened" + }, + "go_imports_names_entropy": { + "type": "long" + }, + "go_imports_names_var_entropy": { + "type": "long" + }, + "go_stripped": { + "type": "boolean" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "imports": { + "type": "flattened" + }, + "imports_names_entropy": { + "type": "long" + }, + "imports_names_var_entropy": { + "type": "long" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "sections": { + "properties": { + "entropy": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "var_entropy": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "first_seen": { + "type": "date" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "last_seen": { + "type": "date" + }, + "marking": { + "properties": { + "tlp": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlp_version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "modified_at": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "scanner_stats": { + "type": "long" + }, + "sightings": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "type": "wildcard" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "software": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platforms": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "subtechnique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "type": "wildcard" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "asset": { + "properties": { + "criticality": { + "type": "keyword" + } + } + }, + "changes": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "effective": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "calculated_score": { + "type": "float" + }, + "calculated_score_norm": { + "type": "float" + }, + "static_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "static_score": { + "type": "float" + }, + "static_score_norm": { + "type": "float" + } + } + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + }, + "target": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "hidden": "true", + "lifecycle": { + "name": ".alerts-ilm-policy", + "rollover_alias": ".alerts-security.alerts-default" + }, + "mapping": { + "ignore_malformed": "true", + "total_fields": { + "limit": "2500" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/alerts_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/alerts_page.ts new file mode 100644 index 0000000000000..f3a9f7b1448a8 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/page_objects/alerts_page.ts @@ -0,0 +1,107 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrService } from '../../functional/ftr_provider_context'; + +const ALERT_TABLE_ROW_CSS_SELECTOR = '[data-test-subj="alertsTable"] .euiDataGridRow'; +const VISUALIZATIONS_SECTION_HEADER_TEST_ID = 'securitySolutionFlyoutVisualizationsHeader'; +const GRAPH_PREVIEW_TEST_ID = 'securitySolutionFlyoutGraphPreview'; +const GRAPH_PREVIEW_LOADING_TEST_ID = 'securitySolutionFlyoutGraphPreviewLoading'; + +export class AlertsPageObject extends FtrService { + private readonly retry = this.ctx.getService('retry'); + private readonly pageObjects = this.ctx.getPageObjects(['common', 'header']); + private readonly testSubjects = this.ctx.getService('testSubjects'); + private readonly defaultTimeoutMs = this.ctx.getService('config').get('timeouts.waitFor'); + + async navigateToAlertsPage(urlQueryParams: string = ''): Promise { + await this.pageObjects.common.navigateToUrlWithBrowserHistory( + 'securitySolution', + '/alerts', + `${urlQueryParams && `?${urlQueryParams}`}`, + { + ensureCurrentUrl: false, + } + ); + await this.pageObjects.header.waitUntilLoadingHasFinished(); + } + + getAbsoluteTimerangeFilter(from: string, to: string) { + return `timerange=(global:(linkTo:!(),timerange:(from:%27${from}%27,kind:absolute,to:%27${to}%27)))`; + } + + getFlyoutFilter(alertId: string) { + return `flyout=(preview:!(),right:(id:document-details-right,params:(id:%27${alertId}%27,indexName:.internal.alerts-security.alerts-default-000001,scopeId:alerts-page)))`; + } + + /** + * Clicks the refresh button on the Alerts page and waits for it to complete + */ + async clickRefresh(): Promise { + await this.ensureOnAlertsPage(); + await this.testSubjects.click('querySubmitButton'); + + // wait for refresh to complete + await this.retry.waitFor( + 'Alerts pages refresh button to be enabled', + async (): Promise => { + const refreshButton = await this.testSubjects.find('querySubmitButton'); + + return (await refreshButton.isDisplayed()) && (await refreshButton.isEnabled()); + } + ); + } + + async ensureOnAlertsPage(): Promise { + await this.testSubjects.existOrFail('detectionsAlertsPage'); + } + + async waitForListToHaveAlerts(timeoutMs?: number): Promise { + const allEventRows = await this.testSubjects.findService.allByCssSelector( + ALERT_TABLE_ROW_CSS_SELECTOR + ); + + if (!Boolean(allEventRows.length)) { + await this.retry.waitForWithTimeout( + 'waiting for alerts to show up on alerts page', + timeoutMs ?? this.defaultTimeoutMs, + async (): Promise => { + await this.clickRefresh(); + + const allEventRowsInner = await this.testSubjects.findService.allByCssSelector( + ALERT_TABLE_ROW_CSS_SELECTOR + ); + + return Boolean(allEventRowsInner.length); + } + ); + } + } + + flyout = { + expandVisualizations: async (): Promise => { + await this.testSubjects.click(VISUALIZATIONS_SECTION_HEADER_TEST_ID); + }, + + assertGraphPreviewVisible: async () => { + return await this.testSubjects.existOrFail(GRAPH_PREVIEW_TEST_ID); + }, + + assertGraphNodesNumber: async (expected: number) => { + await this.flyout.waitGraphIsLoaded(); + const graph = await this.testSubjects.find(GRAPH_PREVIEW_TEST_ID); + await graph.scrollIntoView(); + const nodes = await graph.findAllByCssSelector('.react-flow__nodes .react-flow__node'); + expect(nodes.length).to.be(expected); + }, + + waitGraphIsLoaded: async () => { + await this.testSubjects.missingOrFail(GRAPH_PREVIEW_LOADING_TEST_ID, { timeout: 10000 }); + }, + }; +} diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts index 704f6310cdbb2..b7c20632e82f5 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts @@ -13,8 +13,10 @@ import { VulnerabilityDashboardPageProvider } from './vulnerability_dashboard_pa import { BenchmarkPagePageProvider } from './benchmark_page'; import { CspSecurityCommonProvider } from './security_common'; import { RulePagePageProvider } from './rule_page'; +import { AlertsPageObject } from './alerts_page'; export const cloudSecurityPosturePageObjects = { + alerts: AlertsPageObject, findings: FindingsPageProvider, cloudPostureDashboard: CspDashboardPageProvider, cisAddIntegration: AddCisIntegrationFormPageProvider, diff --git a/x-pack/test/cloud_security_posture_functional/pages/alerts_flyout.ts b/x-pack/test/cloud_security_posture_functional/pages/alerts_flyout.ts new file mode 100644 index 0000000000000..3095f6a663968 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/pages/alerts_flyout.ts @@ -0,0 +1,62 @@ +/* + * 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 { waitForPluginInitialized } from '../../cloud_security_posture_api/utils'; +import type { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const retry = getService('retry'); + const logger = getService('log'); + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const pageObjects = getPageObjects(['common', 'header', 'alerts']); + const alertsPage = pageObjects.alerts; + + describe('Security Alerts Page - Graph visualization', function () { + this.tags(['cloud_security_posture_graph_viz']); + + before(async () => { + await esArchiver.load( + 'x-pack/test/cloud_security_posture_functional/es_archives/security_alerts' + ); + await esArchiver.load( + 'x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit' + ); + + await waitForPluginInitialized({ retry, supertest, logger }); + + // Setting the timerange to fit the data and open the flyout for a specific alert + await alertsPage.navigateToAlertsPage( + `${alertsPage.getAbsoluteTimerangeFilter( + '2024-10-13T00:00:00.000Z', + '2024-10-14T00:00:00.000Z' + )}&${alertsPage.getFlyoutFilter( + '589e086d7ceec7d4b353340578bd607e96fbac7eab9e2926f110990be15122f1' + )}` + ); + + await alertsPage.waitForListToHaveAlerts(); + + await alertsPage.flyout.expandVisualizations(); + }); + + after(async () => { + await esArchiver.unload( + 'x-pack/test/cloud_security_posture_functional/es_archives/security_alerts' + ); + await esArchiver.unload( + 'x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit' + ); + }); + + it('should render graph visualization', async () => { + await alertsPage.flyout.assertGraphPreviewVisible(); + await alertsPage.flyout.assertGraphNodesNumber(3); + }); + }); +} diff --git a/x-pack/test/cloud_security_posture_functional/pages/index.ts b/x-pack/test/cloud_security_posture_functional/pages/index.ts index 1edf38dc41ec9..0114b6a8ce4dc 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/index.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/index.ts @@ -36,5 +36,6 @@ export default function ({ getPageObjects, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./vulnerabilities')); loadTestFile(require.resolve('./vulnerabilities_grouping')); loadTestFile(require.resolve('./benchmark')); + loadTestFile(require.resolve('./alerts_flyout')); }); } diff --git a/x-pack/test/functional/apps/infra/node_details.ts b/x-pack/test/functional/apps/infra/node_details.ts index afacc8d63c3e3..0f1de8cb9c8bf 100644 --- a/x-pack/test/functional/apps/infra/node_details.ts +++ b/x-pack/test/functional/apps/infra/node_details.ts @@ -404,8 +404,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.assetDetails.clickProcessesTab(); const processesTotalValue = await pageObjects.assetDetails.getProcessesTabContentTotalValue(); - const processValue = await processesTotalValue.getVisibleText(); - expect(processValue).to.eql('N/A'); + await retry.tryForTime(5000, async () => { + expect(await processesTotalValue.getVisibleText()).to.eql('N/A'); + }); }); }); }); @@ -510,8 +511,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should render processes tab and with Total Value summary', async () => { const processesTotalValue = await pageObjects.assetDetails.getProcessesTabContentTotalValue(); - const processValue = await processesTotalValue.getVisibleText(); - expect(processValue).to.eql('313'); + await retry.tryForTime(5000, async () => { + expect(await processesTotalValue.getVisibleText()).to.eql('313'); + }); }); it('should expand processes table row', async () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/create_runtime_field.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/create_runtime_field.cy.ts index a96762e530bdb..b8a7bed3c22b9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/create_runtime_field.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/create_runtime_field.cy.ts @@ -8,7 +8,10 @@ import { login } from '../../../tasks/login'; import { visitWithTimeRange } from '../../../tasks/navigation'; import { openTimelineUsingToggle } from '../../../tasks/security_main'; -import { openTimelineFieldsBrowser, populateTimeline } from '../../../tasks/timeline'; +import { + createRuntimeFieldFromTimelne as createRuntimeFieldFromTimeline, + populateTimeline, +} from '../../../tasks/timeline'; import { hostsUrl, ALERTS_URL } from '../../../urls/navigation'; @@ -20,14 +23,13 @@ import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; import { createField } from '../../../tasks/create_runtime_field'; import { openAlertsFieldBrowser } from '../../../tasks/alerts'; import { GET_DATA_GRID_HEADER } from '../../../screens/common/data_grid'; -import { GET_TIMELINE_HEADER } from '../../../screens/timeline'; import { deleteRuntimeField } from '../../../tasks/api_calls/sourcerer'; +import { SAVE_FIELD_BUTTON } from '../../../screens/create_runtime_field'; const alertRunTimeField = 'field.name.alert.page'; const timelineRuntimeField = 'field.name.timeline'; -// FLAKY: https://github.com/elastic/kibana/issues/183104 -describe.skip('Create DataView runtime field', { tags: ['@ess', '@serverless'] }, () => { +describe('Create DataView runtime field', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); deleteRuntimeField('security-solution-default', alertRunTimeField); @@ -48,9 +50,9 @@ describe.skip('Create DataView runtime field', { tags: ['@ess', '@serverless'] } visitWithTimeRange(hostsUrl('allHosts')); openTimelineUsingToggle(); populateTimeline(); - openTimelineFieldsBrowser(); - - createField(timelineRuntimeField); - cy.get(GET_TIMELINE_HEADER(timelineRuntimeField)).should('exist'); + createRuntimeFieldFromTimeline(timelineRuntimeField); + // NOTE: the field creation dialog should be closed now + // meaning that the field creation has been successful + cy.get(SAVE_FIELD_BUTTON).should('not.exist'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts index bb4010a6db4f6..9d2e1ac2e11a5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts @@ -62,8 +62,7 @@ describe('Timeline scope', { tags: ['@ess', '@serverless', '@skipInServerless'] isNotSourcererOption(`${DEFAULT_ALERTS_INDEX}-default`); }); - // FLAKY: https://github.com/elastic/kibana/issues/173854 - describe.skip('Modified badge', () => { + describe('Modified badge', () => { it('Selecting new data view does not add a modified badge', () => { openTimelineUsingToggle(); cy.get(SOURCERER.badgeModified).should(`not.exist`); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts index 66fe11bb2dfd3..a4aac97091af2 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts @@ -166,6 +166,9 @@ export const TIMELINE_EVENTS_COUNT_PREV_PAGE = export const TIMELINE_FIELDS_BUTTON = '[data-test-subj="timeline"] [data-test-subj="show-field-browser"]'; +export const TIMELINE_DISCOVER_FIELDS_BUTTON = + '[data-test-subj="timeline"] [data-test-subj="dataView-add-field_btn"]'; + export const TIMELINE_FILTER = (filter: TimelineFilter) => `[data-test-subj~="filter"][data-test-subj~="filter-enabled"][data-test-subj~="filter-key-${ filter.field @@ -307,10 +310,6 @@ export const HOVER_ACTIONS = { export const TIMELINE_FILTER_BADGE_ENABLED = '[data-test-subj~="filter-enabled"]'; -export const GET_TIMELINE_HEADER = (fieldName: string) => { - return `[data-test-subj="timeline"] [data-test-subj="header-text-${fieldName}"]`; -}; - export const ESQL_TAB = getDataTestSubjectSelector('timelineTabs-esql'); export const TIMELINE_DATE_PICKER_CONTAINER = getDataTestSubjectSelector( diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts index 6c9f7f50d259c..5866011ec1cbf 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -90,6 +90,7 @@ import { TIMELINE_FULL_SCREEN_BUTTON, QUERY_EVENT_COUNT, TIMELINE_ENABLE_DISABLE_ALL_ROW_RENDERER, + TIMELINE_DISCOVER_FIELDS_BUTTON, } from '../screens/timeline'; import { REFRESH_BUTTON, TIMELINE, TIMELINES_TAB_TEMPLATE } from '../screens/timelines'; @@ -99,6 +100,7 @@ import { closeFieldsBrowser, filterFieldsBrowser } from './fields_browser'; import { TIMELINE_CONTEXT_MENU_BTN } from '../screens/alerts'; import { LOADING_INDICATOR } from '../screens/security_header'; import { TOASTER } from '../screens/alerts_detection_rules'; +import { RUNTIME_FIELD_INPUT, SAVE_FIELD_BUTTON } from '../screens/create_runtime_field'; const hostExistsQuery = 'host.name: *'; @@ -380,6 +382,18 @@ export const openTimelineFieldsBrowser = () => { cy.get(TIMELINE_FIELDS_BUTTON).first().click(); }; +export const openTimelineDiscoverAddField = () => { + cy.get(TIMELINE_DISCOVER_FIELDS_BUTTON).first().click(); +}; + +export const createRuntimeFieldFromTimelne = ( + fieldName: string +): Cypress.Chainable> => { + openTimelineDiscoverAddField(); + cy.get(RUNTIME_FIELD_INPUT).type(fieldName); + return cy.get(SAVE_FIELD_BUTTON).click(); +}; + export const openTimelineInspectButton = () => { cy.get(TIMELINE_INSPECT_BUTTON).should('not.be.disabled'); cy.get(TIMELINE_INSPECT_BUTTON).click();