Skip to content

Commit 5bb24da

Browse files
committed
add support multiple target fields with multiple values
enhanced the graph visualization API to handle multiple target
1 parent d1529d6 commit 5bb24da

File tree

10 files changed

+708
-180
lines changed

10 files changed

+708
-180
lines changed

x-pack/solutions/security/plugins/cloud_security_posture/server/routes/graph/fetch_graph.ts

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -242,54 +242,47 @@ ${
242242
| ENRICH ${enrichPolicyName} ON targetEntityId WITH targetEntityName = entity.name, targetEntityType = entity.type, targetEntitySubType = entity.sub_type, targetHostIp = host.ip
243243
244244
// Construct actor and target entities data
245-
// If enrichment found a match (actorEntityType is not null), create full entity data
246-
// Otherwise, create minimal data with just id, type, and sourceNamespaceField
247-
| EVAL actorDocData = CASE(
248-
actorEntityName IS NOT NULL,
249-
CONCAT("{",
250-
"\\"id\\":\\"", actorEntityId, "\\"",
251-
",\\"type\\":\\"", "${DOCUMENT_TYPE_ENTITY}", "\\"",
252-
",\\"sourceNamespaceField\\":\\"", actorEntityFieldHint, "\\"",
253-
",\\"entity\\":", "{",
254-
"\\"name\\":\\"", COALESCE(actorEntityName, ""), "\\"",
255-
",\\"type\\":\\"", COALESCE(actorEntityType, ""), "\\"",
256-
",\\"sub_type\\":\\"", COALESCE(actorEntitySubType, ""), "\\"",
257-
CASE (
258-
actorHostIp IS NOT NULL,
259-
CONCAT(",\\"host\\":", "{", "\\"ip\\":\\"", TO_STRING(actorHostIp), "\\"", "}"),
260-
""
261-
),
262-
"}",
245+
// Build entity field conditionally - only include if any enrichment field exists
246+
| EVAL actorEntityField = CASE(
247+
actorEntityName IS NOT NULL OR actorEntityType IS NOT NULL OR actorEntitySubType IS NOT NULL,
248+
CONCAT(",\\"entity\\":", "{",
249+
"\\"name\\":\\"", COALESCE(actorEntityName, ""), "\\"",
250+
",\\"type\\":\\"", COALESCE(actorEntityType, ""), "\\"",
251+
",\\"sub_type\\":\\"", COALESCE(actorEntitySubType, ""), "\\"",
252+
CASE(
253+
actorHostIp IS NOT NULL,
254+
CONCAT(",\\"host\\":", "{", "\\"ip\\":\\"", TO_STRING(actorHostIp), "\\"", "}"),
255+
""
256+
),
263257
"}"),
264-
CONCAT("{",
265-
"\\"id\\":\\"", actorEntityId, "\\"",
266-
",\\"type\\":\\"", "${DOCUMENT_TYPE_ENTITY}", "\\"",
267-
",\\"sourceNamespaceField\\":\\"", actorEntityFieldHint, "\\"",
268-
"}")
258+
""
269259
)
270-
| EVAL targetDocData = CASE(
271-
targetEntityType IS NOT NULL,
272-
CONCAT("{",
273-
"\\"id\\":\\"", COALESCE(targetEntityId, ""), "\\"",
274-
",\\"type\\":\\"", "${DOCUMENT_TYPE_ENTITY}", "\\"",
275-
",\\"sourceNamespaceField\\":\\"", targetEntityFieldHint, "\\"",
276-
",\\"entity\\":", "{",
277-
"\\"name\\":\\"", COALESCE(targetEntityName, ""), "\\"",
278-
",\\"type\\":\\"", COALESCE(targetEntityType, ""), "\\"",
279-
",\\"sub_type\\":\\"", COALESCE(targetEntitySubType, ""), "\\"",
280-
CASE (
281-
targetHostIp IS NOT NULL,
282-
CONCAT(",\\"host\\":", "{", "\\"ip\\":\\"", TO_STRING(targetHostIp), "\\"", "}"),
283-
""
284-
),
285-
"}",
260+
| EVAL targetEntityField = CASE(
261+
targetEntityName IS NOT NULL OR targetEntityType IS NOT NULL OR targetEntitySubType IS NOT NULL,
262+
CONCAT(",\\"entity\\":", "{",
263+
"\\"name\\":\\"", COALESCE(targetEntityName, ""), "\\"",
264+
",\\"type\\":\\"", COALESCE(targetEntityType, ""), "\\"",
265+
",\\"sub_type\\":\\"", COALESCE(targetEntitySubType, ""), "\\"",
266+
CASE(
267+
targetHostIp IS NOT NULL,
268+
CONCAT(",\\"host\\":", "{", "\\"ip\\":\\"", TO_STRING(targetHostIp), "\\"", "}"),
269+
""
270+
),
286271
"}"),
287-
CONCAT("{",
288-
"\\"id\\":\\"", COALESCE(targetEntityId, ""), "\\"",
289-
",\\"type\\":\\"", "${DOCUMENT_TYPE_ENTITY}", "\\"",
290-
",\\"sourceNamespaceField\\":\\"", targetEntityFieldHint, "\\"",
291-
"}")
272+
""
292273
)
274+
| EVAL actorDocData = CONCAT("{",
275+
"\\"id\\":\\"", actorEntityId, "\\"",
276+
",\\"type\\":\\"", "${DOCUMENT_TYPE_ENTITY}", "\\"",
277+
",\\"sourceNamespaceField\\":\\"", actorEntityFieldHint, "\\"",
278+
actorEntityField,
279+
"}")
280+
| EVAL targetDocData = CONCAT("{",
281+
"\\"id\\":\\"", COALESCE(targetEntityId, ""), "\\"",
282+
",\\"type\\":\\"", "${DOCUMENT_TYPE_ENTITY}", "\\"",
283+
",\\"sourceNamespaceField\\":\\"", targetEntityFieldHint, "\\"",
284+
targetEntityField,
285+
"}")
293286
`
294287
: `
295288
// Fallback to null string with non-enriched entity metadata

x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/graph_visualization.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export const GraphVisualization: React.FC = memo(() => {
146146
docMode === 'single-alert' ? ALERT_PREVIEW_BANNER : EVENT_PREVIEW_BANNER,
147147
singleDocumentData.index
148148
);
149-
} else if (docMode === 'single-entity' && singleDocumentData) {
149+
} else if (docMode === 'single-entity' && singleDocumentData?.entity) {
150150
showEntityPreview(singleDocumentData);
151151
} else if (docMode === 'grouped-entities' && documentsData.length > 0) {
152152
openPreviewPanel({

x-pack/solutions/security/test/cloud_security_posture_api/es_archives/entity_store/data.json

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,186 @@
266266
}
267267
}
268268
}
269+
270+
{
271+
"type": "doc",
272+
"value": {
273+
"id": "7",
274+
"index": ".entities.v1.latest.security_generic_default",
275+
"source": {
276+
"@timestamp": "2025-07-20T17:26:09.361Z",
277+
"cloud": {
278+
"account": {
279+
"id": "multi-target-project-id",
280+
"name": "multi-target-project-name"
281+
},
282+
"provider": "gcp",
283+
"region": "us-west1",
284+
"service": {
285+
"name": "GCP IAM"
286+
}
287+
},
288+
"entity": {
289+
"EngineMetadata": {
290+
"Type": "generic"
291+
},
292+
293+
"name": "MultiTargetUser",
294+
"source": ".ds-logs-cloud_asset_inventory.asset_inventory-identity_gcp_iam_user-default-2025.07.16-000005",
295+
"sub_type": "GCP IAM User",
296+
"type": "Identity"
297+
},
298+
"event": {
299+
"ingested": "2025-07-20T17:27:13.583Z"
300+
},
301+
"user": {
302+
303+
"name": "MultiTargetUser"
304+
}
305+
}
306+
}
307+
}
308+
309+
{
310+
"type": "doc",
311+
"value": {
312+
"id": "8",
313+
"index": ".entities.v1.latest.security_generic_default",
314+
"source": {
315+
"@timestamp": "2025-07-20T17:26:09.361Z",
316+
"cloud": {
317+
"account": {
318+
"id": "multi-target-project-id",
319+
"name": "multi-target-project-name"
320+
},
321+
"provider": "gcp",
322+
"region": "us-west1",
323+
"service": {
324+
"name": "GCP Storage"
325+
}
326+
},
327+
"entity": {
328+
"EngineMetadata": {
329+
"Type": "generic"
330+
},
331+
"id": "projects/multi-target-project-id/buckets/target-bucket-a",
332+
"name": "TargetBucketA",
333+
"source": ".ds-logs-cloud_asset_inventory.asset_inventory-storage_gcp_storage_bucket-default-2025.07.16-000005",
334+
"sub_type": "GCP Storage Bucket",
335+
"type": "Storage"
336+
},
337+
"event": {
338+
"ingested": "2025-07-20T17:27:13.583Z"
339+
}
340+
}
341+
}
342+
}
343+
344+
{
345+
"type": "doc",
346+
"value": {
347+
"id": "9",
348+
"index": ".entities.v1.latest.security_generic_default",
349+
"source": {
350+
"@timestamp": "2025-07-20T17:26:09.361Z",
351+
"cloud": {
352+
"account": {
353+
"id": "multi-target-project-id",
354+
"name": "multi-target-project-name"
355+
},
356+
"provider": "gcp",
357+
"region": "us-west1",
358+
"service": {
359+
"name": "GCP Storage"
360+
}
361+
},
362+
"entity": {
363+
"EngineMetadata": {
364+
"Type": "generic"
365+
},
366+
"id": "projects/multi-target-project-id/buckets/target-bucket-b",
367+
"name": "TargetBucketB",
368+
"source": ".ds-logs-cloud_asset_inventory.asset_inventory-storage_gcp_storage_bucket-default-2025.07.16-000005",
369+
"sub_type": "GCP Storage Bucket",
370+
"type": "Storage"
371+
},
372+
"event": {
373+
"ingested": "2025-07-20T17:27:13.583Z"
374+
}
375+
}
376+
}
377+
}
378+
379+
{
380+
"type": "doc",
381+
"value": {
382+
"id": "10",
383+
"index": ".entities.v1.latest.security_generic_default",
384+
"source": {
385+
"@timestamp": "2025-07-20T17:26:09.361Z",
386+
"cloud": {
387+
"account": {
388+
"id": "multi-target-project-id",
389+
"name": "multi-target-project-name"
390+
},
391+
"provider": "gcp",
392+
"region": "us-west1",
393+
"service": {
394+
"name": "GCP Storage"
395+
}
396+
},
397+
"entity": {
398+
"EngineMetadata": {
399+
"Type": "generic"
400+
},
401+
"id": "projects/multi-target-project-id/buckets/target-bucket-c",
402+
"name": "TargetBucketC",
403+
"source": ".ds-logs-cloud_asset_inventory.asset_inventory-storage_gcp_storage_bucket-default-2025.07.16-000005",
404+
"sub_type": "GCP Storage Bucket",
405+
"type": "Storage"
406+
},
407+
"event": {
408+
"ingested": "2025-07-20T17:27:13.583Z"
409+
}
410+
}
411+
}
412+
}
413+
414+
{
415+
"type": "doc",
416+
"value": {
417+
"id": "11",
418+
"index": ".entities.v1.latest.security_generic_default",
419+
"source": {
420+
"@timestamp": "2025-07-20T17:26:09.361Z",
421+
"cloud": {
422+
"account": {
423+
"id": "multi-target-project-id",
424+
"name": "multi-target-project-name"
425+
},
426+
"provider": "gcp",
427+
"region": "us-west1",
428+
"service": {
429+
"name": "GCP IAM"
430+
}
431+
},
432+
"entity": {
433+
"EngineMetadata": {
434+
"Type": "generic"
435+
},
436+
"id": "projects/multi-target-project-id/serviceAccounts/target-sa-different@multi-target-project-id.iam.gserviceaccount.com",
437+
"name": "TargetServiceDifferent",
438+
"source": ".ds-logs-cloud_asset_inventory.asset_inventory-identity_gcp_service_account-default-2025.07.16-000005",
439+
"sub_type": "GCP Service Account",
440+
"type": "Service"
441+
},
442+
"event": {
443+
"ingested": "2025-07-20T17:27:13.583Z"
444+
},
445+
"service": {
446+
"id": "projects/multi-target-project-id/serviceAccounts/target-sa-different@multi-target-project-id.iam.gserviceaccount.com",
447+
"name": "TargetServiceDifferent"
448+
}
449+
}
450+
}
451+
}

x-pack/solutions/security/test/cloud_security_posture_api/es_archives/logs_gcp_audit_ecs_only/data.json

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,107 @@
308308
}
309309
}
310310
}
311+
}
312+
313+
{
314+
"type": "doc",
315+
"value": {
316+
"data_stream": "logs-gcp.audit-default",
317+
"id": "10",
318+
"index": ".ds-logs-gcp.audit-default-2024.09.11-000001",
319+
"source": {
320+
"@timestamp": "2024-09-11T10:00:00.000Z",
321+
"user": {
322+
"entity": {
323+
324+
}
325+
},
326+
"client": {
327+
"user": {
328+
"email": "[email protected]"
329+
}
330+
},
331+
"cloud": {
332+
"project": {
333+
"id": "multi-target-project-id"
334+
},
335+
"provider": "gcp"
336+
},
337+
"ecs": {
338+
"version": "8.11.0"
339+
},
340+
"event": {
341+
"action": "google.cloud.multi.target.action",
342+
"agent_id_status": "missing",
343+
"category": [
344+
"configuration"
345+
],
346+
"id": "multi-target-mixed-event-id",
347+
"ingested": "2024-09-11T10:00:00.000Z",
348+
"kind": "event",
349+
"outcome": "success",
350+
"provider": "activity",
351+
"type": [
352+
"change"
353+
]
354+
},
355+
"gcp": {
356+
"audit": {
357+
"authorization_info": [
358+
{
359+
"granted": true,
360+
"permission": "storage.buckets.update",
361+
"resource": "projects/multi-target-project-id"
362+
}
363+
],
364+
"logentry_operation": {
365+
"id": "multi-target-operation-id"
366+
},
367+
"type": "type.googleapis.com/google.cloud.audit.AuditLog"
368+
}
369+
},
370+
"log": {
371+
"level": "NOTICE",
372+
"logger": "projects/multi-target-project-id/logs/cloudaudit.googleapis.com%2Factivity"
373+
},
374+
"related": {
375+
"ip": [
376+
"10.0.0.15"
377+
],
378+
"user": [
379+
380+
]
381+
},
382+
"service": {
383+
"name": "storage.googleapis.com",
384+
"target": {
385+
"entity": {
386+
"id": [
387+
"projects/multi-target-project-id/buckets/target-bucket-c",
388+
"projects/multi-target-project-id/serviceAccounts/target-sa-different@multi-target-project-id.iam.gserviceaccount.com"
389+
]
390+
}
391+
}
392+
},
393+
"source": {
394+
"ip": "10.0.0.15"
395+
},
396+
"entity": {
397+
"target": {
398+
"id": [
399+
"projects/multi-target-project-id/buckets/target-bucket-a",
400+
"projects/multi-target-project-id/buckets/target-bucket-b"
401+
]
402+
}
403+
},
404+
"tags": [],
405+
"user_agent": {
406+
"device": {
407+
"name": "Other"
408+
},
409+
"name": "Other",
410+
"original": "google-cloud-sdk/380.0.0"
411+
}
412+
}
413+
}
311414
}

0 commit comments

Comments
 (0)