Skip to content

Commit

Permalink
[Cloud Security] Graph visualization and API (elastic#195307)
Browse files Browse the repository at this point in the history
## Summary

This PR adds:
- Graph visualization component using `xyflow`, and layouts the graph
using `dagre`.
- API that supports the graph visualization
- API tests
- Serverless API tests

**List of open issues (will be tracked in a different ticket):**
- Identify if `related.hosts`, `related.ip` and `related.user` are
mapped before the query. (can be fixed by
elastic/elasticsearch#112912)
- Update nodes rendering to match recent figma changes
- Return 404 when feature is not enabled
- Add keyboard accessibility
- Resolve axe failures (run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))

### How to test

You can view the graph using storybook's
[playground](https://supreme-adventure-8qjmlp1.pages.github.io/graph-storybook/?path=/story/components-graph-components-dagree-layout-graph--graph-stacked-edge-cases).

To test this PR you can run

```
yarn storybook cloud_security_posture_packages
```

To test the API you can use the mocked data

```bash
node scripts/es_archiver load x-pack/test/cloud_security_posture_api/es_archives/logs_gcp_audit \ 
--es-url http://elastic:changeme@localhost:9200 \
--kibana-url http://elastic:changeme@localhost:5601
```

And through dev tools:

```
POST kbn:/internal/cloud_security_posture/graph?apiVersion=1
{
  "query": {
    "actorIds": ["admin@example.com"],
    "eventIds": [""],
    "start": "now-1y/y",
    "end": "now/d"
  }
}
```

### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
kfirpeled and kibanamachine authored Oct 12, 2024
1 parent 2be7a20 commit be0eadf
Show file tree
Hide file tree
Showing 66 changed files with 4,611 additions and 27 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ x-pack/plugins/cloud_integrations/cloud_links @elastic/kibana-core
x-pack/plugins/cloud @elastic/kibana-core
x-pack/packages/kbn-cloud-security-posture @elastic/kibana-cloud-security-posture
x-pack/packages/kbn-cloud-security-posture-common @elastic/kibana-cloud-security-posture
x-pack/packages/kbn-cloud-security-posture/graph @elastic/kibana-cloud-security-posture
x-pack/plugins/cloud_security_posture @elastic/kibana-cloud-security-posture
packages/shared-ux/code_editor/impl @elastic/appex-sharedux
packages/shared-ux/code_editor/mocks @elastic/appex-sharedux
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"@aws-crypto/util": "^5.2.0",
"@babel/runtime": "^7.24.7",
"@cfworker/json-schema": "^1.12.7",
"@dagrejs/dagre": "^1.1.4",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
Expand Down Expand Up @@ -219,6 +220,7 @@
"@kbn/cloud-plugin": "link:x-pack/plugins/cloud",
"@kbn/cloud-security-posture": "link:x-pack/packages/kbn-cloud-security-posture",
"@kbn/cloud-security-posture-common": "link:x-pack/packages/kbn-cloud-security-posture-common",
"@kbn/cloud-security-posture-graph": "link:x-pack/packages/kbn-cloud-security-posture/graph",
"@kbn/cloud-security-posture-plugin": "link:x-pack/plugins/cloud_security_posture",
"@kbn/code-editor": "link:packages/shared-ux/code_editor/impl",
"@kbn/code-editor-mock": "link:packages/shared-ux/code_editor/mocks",
Expand Down Expand Up @@ -1054,6 +1056,7 @@
"@turf/length": "^6.0.2",
"@xstate/react": "^3.2.2",
"@xstate5/react": "npm:@xstate/react@^4.1.2",
"@xyflow/react": "^12.3.0",
"adm-zip": "^0.5.9",
"ai": "^2.2.33",
"ajv": "^8.12.0",
Expand Down Expand Up @@ -1309,6 +1312,7 @@
"@babel/plugin-transform-class-properties": "^7.24.7",
"@babel/plugin-transform-logical-assignment-operators": "^7.24.7",
"@babel/plugin-transform-numeric-separator": "^7.24.7",
"@babel/plugin-transform-optional-chaining": "^7.24.8",
"@babel/plugin-transform-runtime": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.24.7",
Expand Down
18 changes: 18 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,24 @@
"labels": ["Team:Cloud Security", "release_note:skip", "backport:skip"],
"minimumReleaseAge": "7 days",
"enabled": true
},
{
"groupName": "@xyflow/react",
"matchPackageNames": ["@xyflow/react"],
"reviewers": ["team:kibana-cloud-security-posture"],
"matchBaseBranches": ["main"],
"labels": ["Team:Cloud Security", "release_note:skip", "backport:skip"],
"minimumReleaseAge": "7 days",
"enabled": true
},
{
"groupName": "@dagrejs/dagre",
"matchPackageNames": ["@dagrejs/dagre"],
"reviewers": ["team:kibana-cloud-security-posture"],
"matchBaseBranches": ["main"],
"labels": ["Team:Cloud Security", "release_note:skip", "backport:skip"],
"minimumReleaseAge": "7 days",
"enabled": true
}
],
"customManagers": [
Expand Down
1 change: 1 addition & 0 deletions src/dev/storybook/aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const storybookAliases = {
canvas: 'x-pack/plugins/canvas/storybook',
cases: 'packages/kbn-cases-components/.storybook',
cell_actions: 'packages/kbn-cell-actions/.storybook',
cloud_security_posture_packages: 'x-pack/packages/kbn-cloud-security-posture/storybook/config',
cloud: 'packages/cloud/.storybook',
coloring: 'packages/kbn-coloring/.storybook',
language_documentation_popover: 'packages/kbn-language-documentation/.storybook',
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@
"@kbn/cloud-security-posture/*": ["x-pack/packages/kbn-cloud-security-posture/*"],
"@kbn/cloud-security-posture-common": ["x-pack/packages/kbn-cloud-security-posture-common"],
"@kbn/cloud-security-posture-common/*": ["x-pack/packages/kbn-cloud-security-posture-common/*"],
"@kbn/cloud-security-posture-graph": ["x-pack/packages/kbn-cloud-security-posture/graph"],
"@kbn/cloud-security-posture-graph/*": ["x-pack/packages/kbn-cloud-security-posture/graph/*"],
"@kbn/cloud-security-posture-plugin": ["x-pack/plugins/cloud_security_posture"],
"@kbn/cloud-security-posture-plugin/*": ["x-pack/plugins/cloud_security_posture/*"],
"@kbn/code-editor": ["packages/shared-ux/code_editor/impl"],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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 * as graphV1 from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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 * from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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 { schema } from '@kbn/config-schema';

export const graphRequestSchema = schema.object({
query: schema.object({
actorIds: schema.arrayOf(schema.string()),
eventIds: schema.arrayOf(schema.string()),
// TODO: use zod for range validation instead of config schema
start: schema.oneOf([schema.number(), schema.string()]),
end: schema.oneOf([schema.number(), schema.string()]),
}),
});

export const graphResponseSchema = () =>
schema.object({
nodes: schema.arrayOf(
schema.oneOf([entityNodeDataSchema, groupNodeDataSchema, labelNodeDataSchema])
),
edges: schema.arrayOf(edgeDataSchema),
});

export const colorSchema = schema.oneOf([
schema.literal('primary'),
schema.literal('danger'),
schema.literal('warning'),
]);

export const nodeShapeSchema = schema.oneOf([
schema.literal('hexagon'),
schema.literal('pentagon'),
schema.literal('ellipse'),
schema.literal('rectangle'),
schema.literal('diamond'),
schema.literal('label'),
schema.literal('group'),
]);

export const nodeBaseDataSchema = schema.object({
id: schema.string(),
label: schema.maybe(schema.string()),
icon: schema.maybe(schema.string()),
});

export const entityNodeDataSchema = schema.allOf([
nodeBaseDataSchema,
schema.object({
color: colorSchema,
shape: schema.oneOf([
schema.literal('hexagon'),
schema.literal('pentagon'),
schema.literal('ellipse'),
schema.literal('rectangle'),
schema.literal('diamond'),
]),
}),
]);

export const groupNodeDataSchema = schema.allOf([
nodeBaseDataSchema,
schema.object({
shape: schema.literal('group'),
}),
]);

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,
}),
]);

export const edgeDataSchema = schema.object({
id: schema.string(),
source: schema.string(),
sourceShape: nodeShapeSchema,
target: schema.string(),
targetShape: nodeShapeSchema,
color: colorSchema,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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 * as graphV1 from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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 * from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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 { TypeOf } from '@kbn/config-schema';
import {
colorSchema,
edgeDataSchema,
entityNodeDataSchema,
graphRequestSchema,
graphResponseSchema,
groupNodeDataSchema,
labelNodeDataSchema,
nodeShapeSchema,
} from '../../schema/graph/v1';

export type GraphRequest = TypeOf<typeof graphRequestSchema>;
export type GraphResponse = TypeOf<typeof graphResponseSchema>;

export type Color = typeof colorSchema.type;

export type NodeShape = TypeOf<typeof nodeShapeSchema>;

export type EntityNodeDataModel = TypeOf<typeof entityNodeDataSchema>;

export type GroupNodeDataModel = TypeOf<typeof groupNodeDataSchema>;

export type LabelNodeDataModel = TypeOf<typeof labelNodeDataSchema>;

export type EdgeDataModel = TypeOf<typeof edgeDataSchema>;

export type NodeDataModel = EntityNodeDataModel | GroupNodeDataModel | LabelNodeDataModel;
7 changes: 7 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ This package includes
- Hooks that's used on Flyout component that's used in Alerts page on Security Solution Plugins as well as components on CSP plugin
- Utilities and types thats used for the Hooks above as well as in CSP plugins

## Storybook

General look of the component can be checked visually running the following storybook:
`yarn storybook cloud_security_posture_graph`

Note that all the interactions are mocked.

## Maintainers

Maintained by the Cloud Security Team
25 changes: 25 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture/graph/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Cloud Security Posture's Graph

## Motivation

The idea behind this package is to have a reusable graph component, embedding the features available to alerts flyout in
security solution plugin.

## How to use this

Standalone examples will follow. In the meantime checkout storybook to view the graphs progress.

## The most important public api members

- GraphComponent itself (comming soon..)

### Extras

Be sure to check out provided helpers

## Storybook

General look of the component can be checked visually running the following storybook:
`yarn storybook cloud_security_posture_packages`

Note that all the interactions are mocked.
6 changes: 6 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture/graph/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* 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.
*/
12 changes: 12 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture/graph/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

module.exports = {
preset: '@kbn/test',
roots: ['<rootDir>/x-pack/packages/kbn-cloud-security-posture/graph'],
rootDir: '../../../..',
};
5 changes: 5 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture/graph/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "shared-browser",
"id": "@kbn/cloud-security-posture-graph",
"owner": "@elastic/kibana-cloud-security-posture"
}
7 changes: 7 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture/graph/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@kbn/cloud-security-posture-graph",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0",
"sideEffects": false
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit be0eadf

Please sign in to comment.