Skip to content

Commit

Permalink
[Cloud Security] Added filter support to graph API (elastic#199048)
Browse files Browse the repository at this point in the history
## Summary

Enhances the graph API to support filtering by bool query.

Graph API is an internal API that hasn't been released yet to ESS, and
is not available yet on serverless (behind a feature-flag in
kibana.config) due to the above I don't consider it a breaking change.

Previous API request body:

```js
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()]),
```

New API request body:

```js
  nodesLimit: schema.maybe(schema.number()), // Maximum number of nodes in the graph (currently the graph doesn't handle very well graph with over 100 nodes)
  showUnknownTarget: schema.maybe(schema.boolean()), // Whether or not to return events that miss target.entity.id
  query: schema.object({
    eventIds: schema.arrayOf(schema.string()), // Event ids that triggered the alert, would be marked in red
    // TODO: use zod for range validation instead of config schema
    start: schema.oneOf([schema.number(), schema.string()]),
    end: schema.oneOf([schema.number(), schema.string()]),
    esQuery: schema.maybe( // elasticsearch's dsl bool query
      schema.object({
        bool: schema.object({
          filter: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
          must: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
          should: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
          must_not: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
        }),
      })
```

New field to the graph API response (pseudo):

```js
messages?: ApiMessageCode[]

enum ApiMessageCode {
  ReachedNodesLimit = 'REACHED_NODES_LIMIT',
}
```

### How to test

Toggle feature flag in kibana.dev.yml

```yaml
xpack.securitySolution.enableExperimental: ['graphVisualizationInFlyoutEnabled']
```

To test through the UI you can use the mocked data

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

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

1. Go to the alerts page
2. Change the query time range to show alerts from the 13th of October
2024 (**IMPORTANT**)
3. Open the alerts flyout
5. Scroll to see the graph visualization : D

To test **only** 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": {
    "eventIds": [],
    "start": "now-1y/y",
    "end": "now/d",
    "esQuery": {
      "bool": {
        "filter": [
        {
          "match_phrase": {
            "actor.entity.id": "[email protected]"
          }
        }
        ]
      }
    }
  }
}
```

### Related PRs

- elastic#196034
- elastic#195307

### Checklist

- [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

---------

Co-authored-by: kibanamachine <[email protected]>
(cherry picked from commit 160e626)
  • Loading branch information
kfirpeled committed Nov 11, 2024
1 parent 4e89a6c commit 9cd525b
Show file tree
Hide file tree
Showing 13 changed files with 693 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,26 @@
*/

import { schema } from '@kbn/config-schema';
import { ApiMessageCode } from '../../types/graph/v1';

export const graphRequestSchema = schema.object({
nodesLimit: schema.maybe(schema.number()),
showUnknownTarget: schema.maybe(schema.boolean()),
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()]),
esQuery: schema.maybe(
schema.object({
bool: schema.object({
filter: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
must: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
should: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
must_not: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))),
}),
})
),
}),
});

Expand All @@ -23,6 +35,9 @@ export const graphResponseSchema = () =>
schema.oneOf([entityNodeDataSchema, groupNodeDataSchema, labelNodeDataSchema])
),
edges: schema.arrayOf(edgeDataSchema),
messages: schema.maybe(
schema.arrayOf(schema.oneOf([schema.literal(ApiMessageCode.ReachedNodesLimit)]))
),
});

export const colorSchema = schema.oneOf([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
"@kbn/i18n",
"@kbn/analytics",
"@kbn/usage-collection-plugin",
"@kbn/es-query",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import type { TypeOf } from '@kbn/config-schema';
import type { BoolQuery } from '@kbn/es-query';
import {
colorSchema,
edgeDataSchema,
Expand All @@ -17,13 +18,21 @@ import {
nodeShapeSchema,
} from '../../schema/graph/v1';

export type GraphRequest = TypeOf<typeof graphRequestSchema>;
export type GraphResponse = TypeOf<typeof graphResponseSchema>;
export type GraphRequest = Omit<TypeOf<typeof graphRequestSchema>, 'query.esQuery'> & {
query: { esQuery?: { bool: Partial<BoolQuery> } };
};
export type GraphResponse = Omit<TypeOf<typeof graphResponseSchema>, 'messages'> & {
messages?: ApiMessageCode[];
};

export type Color = typeof colorSchema.type;

export type NodeShape = TypeOf<typeof nodeShapeSchema>;

export enum ApiMessageCode {
ReachedNodesLimit = 'REACHED_NODES_LIMIT',
}

export type EntityNodeDataModel = TypeOf<typeof entityNodeDataSchema>;

export type GroupNodeDataModel = TypeOf<typeof groupNodeDataSchema>;
Expand Down
20 changes: 12 additions & 8 deletions x-pack/plugins/cloud_security_posture/server/routes/graph/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
graphResponseSchema,
} from '@kbn/cloud-security-posture-common/schema/graph/latest';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { GraphRequest } from '@kbn/cloud-security-posture-common/types/graph/v1';
import { GRAPH_ROUTE_PATH } from '../../../common/constants';
import { CspRouter } from '../../types';
import { getGraph as getGraphV1 } from './v1';
Expand Down Expand Up @@ -39,26 +40,29 @@ export const defineGraphRoute = (router: CspRouter) =>
},
},
async (context, request, response) => {
const { actorIds, eventIds, start, end } = request.body.query;
const { nodesLimit, showUnknownTarget = false } = request.body;
const { eventIds, start, end, esQuery } = request.body.query as GraphRequest['query'];
const cspContext = await context.csp;
const spaceId = (await cspContext.spaces?.spacesService?.getActiveSpace(request))?.id;

try {
const { nodes, edges } = await getGraphV1(
{
const resp = await getGraphV1({
services: {
logger: cspContext.logger,
esClient: cspContext.esClient,
},
{
actorIds,
query: {
eventIds,
spaceId,
start,
end,
}
);
esQuery,
},
showUnknownTarget,
nodesLimit,
});

return response.ok({ body: { nodes, edges } });
return response.ok({ body: resp });
} catch (err) {
const error = transformError(err);
cspContext.logger.error(`Failed to fetch graph ${err}`);
Expand Down
23 changes: 0 additions & 23 deletions x-pack/plugins/cloud_security_posture/server/routes/graph/types.ts

This file was deleted.

Loading

0 comments on commit 9cd525b

Please sign in to comment.