This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-jbang-examples.git


The following commit(s) were added to refs/heads/main by this push:
     new ef96fba  Add an OCSF Example
ef96fba is described below

commit ef96fbaa9764a129c0fff136814075d4b8673107
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Jan 27 11:18:08 2026 +0100

    Add an OCSF Example
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 ocsf/README.adoc                       |  305 +++++++
 ocsf/application.properties            |   25 +
 ocsf/ocsf.camel.yaml                   | 1400 ++++++++++++++++++++++++++++++++
 ocsf/test/authentication-event.json    |  117 +++
 ocsf/test/authentication_1.json        |  104 +++
 ocsf/test/authentication_2.json        |   87 ++
 ocsf/test/detection-finding.json       |  155 ++++
 ocsf/test/detection-finding_1.json     |  152 ++++
 ocsf/test/detection-finding_2.json     |  169 ++++
 ocsf/test/detection-finding_3.json     |  185 +++++
 ocsf/test/detection-finding_4.json     |  162 ++++
 ocsf/test/detection-finding_5.json     |  167 ++++
 ocsf/test/detection-finding_6.json     |  161 ++++
 ocsf/test/file-activity_1.json         |   78 ++
 ocsf/test/network-activity_1.json      |   78 ++
 ocsf/test/network-activity_2.json      |   89 ++
 ocsf/test/process-activity_1.json      |   85 ++
 ocsf/test/vulnerability-finding_1.json |  105 +++
 ocsf/test/vulnerability-finding_2.json |  104 +++
 19 files changed, 3728 insertions(+)

diff --git a/ocsf/README.adoc b/ocsf/README.adoc
new file mode 100644
index 0000000..b11d55f
--- /dev/null
+++ b/ocsf/README.adoc
@@ -0,0 +1,305 @@
+= OCSF DataFormat Example
+
+This example demonstrates Apache Camel's OCSF (Open Cybersecurity Schema 
Framework) DataFormat. OCSF is an open standard for security event logging, 
used by AWS Security Hub, Splunk, CrowdStrike, and other security tools.
+
+The example exposes REST endpoints that receive OCSF events as JSON, unmarshal 
them to typed Java objects, extract relevant fields, and return a response. 
There's also an optional integration with Ollama for summarizing security 
findings.
+
+== Prerequisites
+
+The `camel-ocsf` module requires Camel 4.18.0 or later.
+
+If you're working with a SNAPSHOT build:
+
+[source,shell]
+----
+cd /path/to/camel
+mvn clean install -pl components/camel-ocsf -am -DskipTests
+mvn clean install -pl dsl/camel-jbang -am -DskipTests
+----
+
+== Running the Example
+
+[source,shell]
+----
+jbang -Dcamel.jbang.version=4.18.0-SNAPSHOT camel@apache/camel run --port 8081 
--properties=application.properties --dep camel:ocsf --dep 
camel:langchain4j-chat --dep=mvn:dev.langchain4j:langchain4j-ollama:1.10.0 
ocsf.camel.yaml
+----
+
+The server starts on port 8081. Check the health endpoint to verify:
+
+[source,shell]
+----
+curl http://localhost:8081/health
+----
+
+== Available Endpoints
+
+|===
+|Endpoint |Description
+
+|`POST /api/ocsf/detection-finding`
+|Security detection findings (alerts from SIEM, EDR, etc.)
+
+|`POST /api/ocsf/detection-finding/summarize`
+|Same as above, but sends the finding to Ollama for analysis (requires Ollama 
running)
+
+|`POST /api/ocsf/authentication`
+|Login/logout events
+
+|`POST /api/ocsf/vulnerability-finding`
+|CVE and vulnerability scan results
+
+|`POST /api/ocsf/network-activity`
+|Network traffic and connection events
+
+|`POST /api/ocsf/file-activity`
+|File system operations (create, modify, delete)
+
+|`POST /api/ocsf/process-activity`
+|Process execution events (launch, terminate)
+
+|`POST /api/ocsf/event`
+|Generic OCSF events
+
+|`GET /health`
+|Health check
+|===
+
+== Testing the Endpoints
+
+The `test/` directory contains sample OCSF events. Here's how to test each 
endpoint:
+
+=== Detection Findings
+
+Detection findings represent security alerts from tools like AWS GuardDuty, 
CrowdStrike, or your SIEM.
+
+[source,shell]
+----
+curl -X POST http://localhost:8081/api/ocsf/detection-finding \
+  -H "Content-Type: application/json" \
+  -d @test/detection-finding.json
+----
+
+The route extracts finding info, severity, cloud context, and remediation 
steps, then logs a formatted summary.
+
+Sample files:
+
+* `detection-finding.json` - Suspicious Secrets Manager API call from external 
IP
+* `detection-finding_1.json` - Crypto mining instances launched from China
+* `detection-finding_2.json` - Brute-force attack on authentication API
+* `detection-finding_3.json` - IAM privilege escalation attempt
+* `detection-finding_4.json` - SQL injection with data exfiltration
+* `detection-finding_5.json` - Lambda supply chain attack
+* `detection-finding_6.json` - Container IMDS credential theft
+
+=== Authentication Events
+
+Authentication events track login attempts, MFA usage, and session activity.
+
+[source,shell]
+----
+curl -X POST http://localhost:8081/api/ocsf/authentication \
+  -H "Content-Type: application/json" \
+  -d @test/authentication_1.json
+----
+
+The route extracts user info, source IP, destination service, MFA status, and 
authentication protocol.
+
+Sample files:
+
+* `authentication_1.json` - Successful login with MFA from corporate IP
+* `authentication_2.json` - Failed login from Tor exit node
+
+=== Vulnerability Findings
+
+Vulnerability findings come from scanners like Amazon Inspector, Qualys, or 
Trivy.
+
+[source,shell]
+----
+curl -X POST http://localhost:8081/api/ocsf/vulnerability-finding \
+  -H "Content-Type: application/json" \
+  -d @test/vulnerability-finding_1.json
+----
+
+The route extracts CVE ID, CVSS score, affected package, and whether a fix is 
available.
+
+Sample files:
+
+* `vulnerability-finding_1.json` - Log4Shell (CVE-2021-44228) in production
+* `vulnerability-finding_2.json` - OpenSSL buffer overflow (CVE-2022-3602)
+
+=== Network Activity
+
+Network activity events capture traffic flows, blocked connections, and 
protocol details.
+
+[source,shell]
+----
+curl -X POST http://localhost:8081/api/ocsf/network-activity \
+  -H "Content-Type: application/json" \
+  -d @test/network-activity_1.json
+----
+
+The route extracts source/destination IPs and ports, traffic volume, TLS info, 
and action taken.
+
+Sample files:
+
+* `network-activity_1.json` - SSH brute force attack blocked
+* `network-activity_2.json` - Outbound C2 beaconing detected
+
+=== File Activity
+
+File activity events track file system operations from EDR tools.
+
+[source,shell]
+----
+curl -X POST http://localhost:8081/api/ocsf/file-activity \
+  -H "Content-Type: application/json" \
+  -d @test/file-activity_1.json
+----
+
+The route extracts file name, path, size, hashes, the process that performed 
the action, and user context.
+
+Sample files:
+
+* `file-activity_1.json` - Mimikatz dropped via encoded PowerShell command
+
+=== Process Activity
+
+Process activity events track process execution, useful for detecting 
malicious behavior.
+
+[source,shell]
+----
+curl -X POST http://localhost:8081/api/ocsf/process-activity \
+  -H "Content-Type: application/json" \
+  -d @test/process-activity_1.json
+----
+
+The route extracts process name, PID, command line, parent process chain, and 
actor information.
+
+Sample files:
+
+* `process-activity_1.json` - Recon commands spawned from malicious Excel macro
+
+== Testing All Events
+
+Run all test files:
+
+[source,shell]
+----
+# Detection findings
+for f in test/detection-finding*.json; do
+  echo "Testing $f"
+  curl -s -X POST http://localhost:8081/api/ocsf/detection-finding \
+    -H "Content-Type: application/json" -d @$f | jq -r '.status'
+done
+
+# Authentication
+for f in test/authentication*.json; do
+  echo "Testing $f"
+  curl -s -X POST http://localhost:8081/api/ocsf/authentication \
+    -H "Content-Type: application/json" -d @$f | jq -r '.status'
+done
+
+# Vulnerability findings
+for f in test/vulnerability-finding*.json; do
+  echo "Testing $f"
+  curl -s -X POST http://localhost:8081/api/ocsf/vulnerability-finding \
+    -H "Content-Type: application/json" -d @$f | jq -r '.status'
+done
+
+# Network activity
+for f in test/network-activity*.json; do
+  echo "Testing $f"
+  curl -s -X POST http://localhost:8081/api/ocsf/network-activity \
+    -H "Content-Type: application/json" -d @$f | jq -r '.status'
+done
+
+# File activity
+curl -s -X POST http://localhost:8081/api/ocsf/file-activity \
+  -H "Content-Type: application/json" -d @test/file-activity_1.json | jq -r 
'.status'
+
+# Process activity
+curl -s -X POST http://localhost:8081/api/ocsf/process-activity \
+  -H "Content-Type: application/json" -d @test/process-activity_1.json | jq -r 
'.status'
+----
+
+== Using the OCSF DataFormat
+
+The OCSF DataFormat unmarshals JSON to typed POJOs:
+
+[source,yaml]
+----
+- unmarshal:
+    ocsf:
+      unmarshalType: org.apache.camel.dataformat.ocsf.model.DetectionFinding
+----
+
+After unmarshalling, you can access fields directly:
+
+[source,yaml]
+----
+- setHeader:
+    name: severity
+    simple: "${body.additionalProperties[severity]}"
+- setHeader:
+    name: findingTitle
+    simple: "${body.findingInfo.title}"
+----
+
+Some fields are direct properties on the Java class (like `findingInfo`, 
`vulnerabilities`), while others are in `additionalProperties` (like 
`severity`, `time_dt`). Check the generated model classes in `camel-ocsf` if 
you need to know which is which.
+
+To marshal back to JSON:
+
+[source,yaml]
+----
+- marshal:
+    ocsf: {}
+----
+
+== Ollama Integration
+
+The `/api/ocsf/detection-finding/summarize` endpoint sends findings to Ollama 
for analysis.
+
+Start Ollama using Camel's infra command:
+
+[source,shell]
+----
+camel infra run ollama 
+----
+
+Configuration in `application.properties`:
+
+[source,properties]
+----
+ollama.base.url=http://localhost:11434
+ollama.model.name=granite4:3b
+----
+
+Test the summarization:
+
+[source,shell]
+----
+curl -X POST http://localhost:8081/api/ocsf/detection-finding/summarize \
+  -H "Content-Type: application/json" \
+  -d @test/detection-finding.json | jq .
+----
+
+== Supported OCSF Event Classes
+
+The DataFormat includes POJOs for most OCSF event classes:
+
+* *Findings*: DetectionFinding, VulnerabilityFinding, ComplianceFinding
+* *IAM*: Authentication, AuthorizeSession, AccountChange, GroupManagement
+* *System Activity*: FileActivity, ProcessActivity, MemoryActivity, 
ModuleActivity
+* *Network Activity*: NetworkActivity, HttpActivity, DnsActivity, SshActivity
+* *Discovery*: DeviceInventoryInfo, UserInventoryInfo
+
+== Links
+
+* https://schema.ocsf.io/[OCSF Schema Browser]
+* https://github.com/ocsf/ocsf-schema[OCSF GitHub]
+* 
https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format-syntax.html[AWS
 Security Hub OCSF]
+* https://ollama.ai[Ollama]
+
+== Help
+
+If you have questions or run into problems, reach out on the 
https://camel.apache.org/community/support/[Camel mailing list] or 
https://camel.zulipchat.com/[Zulip chat].
diff --git a/ocsf/application.properties b/ocsf/application.properties
new file mode 100644
index 0000000..3eecb16
--- /dev/null
+++ b/ocsf/application.properties
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Application Configuration
+camel.main.name = OcsfSecurityEventProcessor
+
+# Ollama Configuration
+# Start Ollama with: camel infra run ollama --model granite4:3b
+# Or manually: ollama run granite4:3b
+ollama.base.url=http://localhost:11434
+ollama.model.name=granite4:3b
diff --git a/ocsf/ocsf.camel.yaml b/ocsf/ocsf.camel.yaml
new file mode 100644
index 0000000..b6065b6
--- /dev/null
+++ b/ocsf/ocsf.camel.yaml
@@ -0,0 +1,1400 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+# Bean Definitions for AI-powered analysis
+- beans:
+  # Configure Ollama Chat Model for security finding summarization
+  - name: chatModel
+    type: "#class:dev.langchain4j.model.ollama.OllamaChatModel"
+    scriptLanguage: groovy
+    script: |
+      import dev.langchain4j.model.ollama.OllamaChatModel
+      import static java.time.Duration.ofSeconds
+
+      return OllamaChatModel.builder()
+        .baseUrl("{{ollama.base.url}}")
+        .modelName("{{ollama.model.name}}")
+        .temperature(0.3)
+        .timeout(ofSeconds(120))
+        .build()
+
+# REST endpoint to receive OCSF DetectionFinding events
+# POST JSON payload to http://localhost:8080/api/ocsf/detection-finding
+- route:
+    id: ocsf-detection-finding-receiver
+    from:
+      uri: "platform-http:/api/ocsf/detection-finding"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received OCSF DetectionFinding event (raw JSON)"
+        - unmarshal:
+            ocsf:
+              unmarshalType: 
org.apache.camel.dataformat.ocsf.model.DetectionFinding
+        - log:
+            message: "Unmarshalled DetectionFinding"
+        # Extract finding info (direct properties)
+        - setHeader:
+            name: findingTitle
+            simple: "${body.findingInfo?.title}"
+        - setHeader:
+            name: findingDesc
+            simple: "${body.findingInfo?.desc}"
+        - setHeader:
+            name: findingUid
+            simple: "${body.findingInfo?.uid}"
+        - setHeader:
+            name: findingTypes
+            simple: "${body.findingInfo?.types}"
+        # Extract remediation info (direct property)
+        - setHeader:
+            name: remediationDesc
+            simple: "${body.remediation?.desc}"
+        # Extract direct properties on DetectionFinding
+        - setHeader:
+            name: riskScore
+            simple: "${body.riskScore}"
+        - setHeader:
+            name: riskLevel
+            simple: "${body.riskLevel}"
+        - setHeader:
+            name: confidence
+            simple: "${body.confidence}"
+        - setHeader:
+            name: isAlert
+            simple: "${body.isAlert}"
+        # Base event fields are in additionalProperties
+        - setHeader:
+            name: severity
+            simple: "${body.additionalProperties[severity]}"
+        - setHeader:
+            name: severityId
+            simple: "${body.additionalProperties[severity_id]}"
+        - setHeader:
+            name: status
+            simple: "${body.additionalProperties[status]}"
+        - setHeader:
+            name: statusId
+            simple: "${body.additionalProperties[status_id]}"
+        - setHeader:
+            name: activityName
+            simple: "${body.additionalProperties[activity_name]}"
+        - setHeader:
+            name: typeName
+            simple: "${body.additionalProperties[type_name]}"
+        - setHeader:
+            name: className
+            simple: "${body.additionalProperties[class_name]}"
+        - setHeader:
+            name: count
+            simple: "${body.additionalProperties[count]}"
+        - setHeader:
+            name: timeDt
+            simple: "${body.additionalProperties[time_dt]}"
+        # Extract cloud info from additionalProperties
+        - setHeader:
+            name: cloudMap
+            simple: "${body.additionalProperties[cloud]}"
+        - setHeader:
+            name: cloudProvider
+            simple: "${header.cloudMap[provider]}"
+        - setHeader:
+            name: cloudRegion
+            simple: "${header.cloudMap[region]}"
+        - setHeader:
+            name: cloudAccountMap
+            simple: "${header.cloudMap[account]}"
+        - setHeader:
+            name: cloudAccountId
+            simple: "${header.cloudAccountMap[uid]}"
+        # Extract metadata from additionalProperties
+        - setHeader:
+            name: metadataMap
+            simple: "${body.additionalProperties[metadata]}"
+        - setHeader:
+            name: productMap
+            simple: "${header.metadataMap[product]}"
+        - setHeader:
+            name: productName
+            simple: "${header.productMap[name]}"
+        - setHeader:
+            name: productVendor
+            simple: "${header.productMap[vendor_name]}"
+        - log:
+            message: |
+              ============================================================
+                           OCSF Detection Finding Received
+              ============================================================
+              Finding Title: ${header.findingTitle}
+              Finding UID: ${header.findingUid}
+              Finding Types: ${header.findingTypes}
+              Description: ${header.findingDesc}
+              ------------------------------------------------------------
+              Class: ${header.className}
+              Severity: ${header.severity} (ID: ${header.severityId})
+              Status: ${header.status} (ID: ${header.statusId})
+              Activity: ${header.activityName}
+              Type: ${header.typeName}
+              Count: ${header.count}
+              Time: ${header.timeDt}
+              ------------------------------------------------------------
+              Cloud Provider: ${header.cloudProvider}
+              Cloud Region: ${header.cloudRegion}
+              Cloud Account ID: ${header.cloudAccountId}
+              ------------------------------------------------------------
+              Product: ${header.productName} (${header.productVendor})
+              Remediation: ${header.remediationDesc}
+              ============================================================
+        # Clean up temporary headers
+        - removeHeaders:
+            pattern: "cloudMap|cloudAccountMap|metadataMap|productMap"
+        - marshal:
+            ocsf: {}
+        - log:
+            message: "Marshalled back to JSON: ${body}"
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - setBody:
+            simple: |
+              {
+                "status": "received",
+                "message": "OCSF DetectionFinding processed successfully",
+                "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
+              }
+
+
+- route:
+    id: ocsf-event-receiver
+    from:
+      uri: "platform-http:/api/ocsf/event"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received generic OCSF event (raw JSON)"
+        - unmarshal:
+            ocsf:
+              unmarshalType: org.apache.camel.dataformat.ocsf.model.OcsfEvent
+        - log:
+            message: "Unmarshalled OcsfEvent - Class UID: ${body.classUid}, 
Severity ID: ${body.severityId}, Time: ${body.time}"
+        - setHeader:
+            name: classUid
+            simple: "${body.classUid}"
+        - setHeader:
+            name: categoryUid
+            simple: "${body.categoryUid}"
+        - setHeader:
+            name: severityId
+            simple: "${body.severityId}"
+        - setHeader:
+            name: activityId
+            simple: "${body.activityId}"
+        - setHeader:
+            name: message
+            simple: "${body.message}"
+        - log:
+            message: |
+              ========== OCSF Event ==========
+              Class UID: ${header.classUid}
+              Category UID: ${header.categoryUid}
+              Severity ID: ${header.severityId}
+              Activity ID: ${header.activityId}
+              Message: ${header.message}
+              =================================
+        - marshal:
+            ocsf: {}
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - setBody:
+            simple: |
+              {
+                "status": "received",
+                "message": "OCSF event processed successfully",
+                "class_uid": "${header.classUid}",
+                "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
+              }
+
+# Health check endpoint
+- route:
+    id: health-check
+    from:
+      uri: "platform-http:/health"
+      steps:
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - setBody:
+            simple: |
+              {
+                "status": "UP",
+                "service": "OCSF Event Receiver",
+                "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
+              }
+
+# AI-powered security finding summarization endpoint
+# Uses Ollama with granite model to analyze and summarize security findings
+# POST JSON payload to 
http://localhost:8080/api/ocsf/detection-finding/summarize
+- route:
+    id: ocsf-ai-summarization
+    from:
+      uri: "platform-http:/api/ocsf/detection-finding/summarize"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received OCSF DetectionFinding for AI summarization"
+        - unmarshal:
+            ocsf:
+              unmarshalType: 
org.apache.camel.dataformat.ocsf.model.DetectionFinding
+        - log:
+            message: "Unmarshalled DetectionFinding for AI analysis"
+        # Store the original finding for later
+        - setProperty:
+            name: originalFinding
+            simple: "${body}"
+        # Use Groovy script to extract all relevant fields including nested 
ones
+        - script:
+            groovy: |
+              def finding = exchange.getMessage().getBody()
+              def props = finding.additionalProperties ?: [:]
+              def msg = exchange.getMessage()
+
+              // Basic finding info
+              msg.setHeader("findingTitle", finding.findingInfo?.title ?: 
"Unknown")
+              msg.setHeader("findingDesc", finding.findingInfo?.desc ?: "No 
description")
+              msg.setHeader("findingTypes", 
finding.findingInfo?.types?.toString() ?: "Unknown")
+              msg.setHeader("riskScore", finding.riskScore?.toString() ?: 
"N/A")
+              msg.setHeader("riskLevel", finding.riskLevel ?: "Unknown")
+              msg.setHeader("confidence", finding.confidence ?: "Unknown")
+              msg.setHeader("remediationDesc", finding.remediation?.desc ?: 
"No remediation provided")
+
+              // Base event fields from additionalProperties
+              msg.setHeader("severity", props.severity ?: "Unknown")
+              msg.setHeader("severityId", props.severity_id?.toString() ?: 
"N/A")
+              msg.setHeader("className", props.class_name ?: "Unknown")
+              msg.setHeader("activityName", props.activity_name ?: "Unknown")
+              msg.setHeader("message", props.message ?: finding.message ?: "No 
message")
+
+              // Cloud information
+              def cloud = props.cloud ?: [:]
+              msg.setHeader("cloudProvider", cloud.provider ?: "Unknown")
+              msg.setHeader("cloudRegion", cloud.region ?: "Unknown")
+              msg.setHeader("cloudAccountId", cloud.account?.uid ?: "Unknown")
+
+              // Device information
+              def device = props.device ?: [:]
+              msg.setHeader("deviceHostname", device.hostname ?: "Unknown")
+              msg.setHeader("deviceIp", device.ip ?: "Unknown")
+              msg.setHeader("deviceType", device.type ?: "Unknown")
+              msg.setHeader("deviceInstanceId", device.instance_uid ?: 
"Unknown")
+              msg.setHeader("deviceOs", device.os?.name ? "${device.os.name} 
${device.os.version ?: ''}" : "Unknown")
+
+              // Source endpoint info from evidences (attacker info)
+              def evidences = props.evidences ?: []
+              def srcEndpoint = evidences.size() > 0 ? 
evidences[0].src_endpoint : null
+              if (srcEndpoint) {
+                msg.setHeader("srcIp", srcEndpoint.ip ?: "Unknown")
+                msg.setHeader("srcIsp", srcEndpoint.isp ?: "Unknown")
+                msg.setHeader("srcIspOrg", srcEndpoint.isp_org ?: "Unknown")
+                def srcLocation = srcEndpoint.location ?: [:]
+                msg.setHeader("srcCity", srcLocation.city ?: "Unknown")
+                msg.setHeader("srcCountry", srcLocation.country ?: "Unknown")
+                msg.setHeader("srcLat", srcLocation.lat?.toString() ?: "N/A")
+                msg.setHeader("srcLong", srcLocation.long?.toString() ?: "N/A")
+                def asn = srcEndpoint.autonomous_system ?: [:]
+                msg.setHeader("srcAsn", asn.number?.toString() ?: "N/A")
+                msg.setHeader("srcAsnName", asn.name ?: "Unknown")
+              } else {
+                msg.setHeader("srcIp", "Unknown")
+                msg.setHeader("srcIsp", "Unknown")
+                msg.setHeader("srcIspOrg", "Unknown")
+                msg.setHeader("srcCity", "Unknown")
+                msg.setHeader("srcCountry", "Unknown")
+                msg.setHeader("srcLat", "N/A")
+                msg.setHeader("srcLong", "N/A")
+                msg.setHeader("srcAsn", "N/A")
+                msg.setHeader("srcAsnName", "Unknown")
+              }
+
+              // API info from evidences
+              def api = evidences.size() > 0 ? evidences[0].api : null
+              if (api) {
+                msg.setHeader("apiOperation", api.operation ?: "Unknown")
+                msg.setHeader("apiService", api.service?.name ?: "Unknown")
+              } else {
+                msg.setHeader("apiOperation", "Unknown")
+                msg.setHeader("apiService", "Unknown")
+              }
+
+              // Malware info
+              def malwareList = props.malware ?: []
+              if (malwareList.size() > 0) {
+                msg.setHeader("malwareName", malwareList[0].name ?: "None")
+                msg.setHeader("malwareId", malwareList[0].uid ?: "N/A")
+              } else {
+                msg.setHeader("malwareName", "None detected")
+                msg.setHeader("malwareId", "N/A")
+              }
+
+              // Resources affected
+              def resources = props.resources ?: []
+              if (resources.size() > 0) {
+                msg.setHeader("resourceType", resources[0].type ?: "Unknown")
+                msg.setHeader("resourceUid", resources[0].uid ?: "Unknown")
+              } else {
+                msg.setHeader("resourceType", "Unknown")
+                msg.setHeader("resourceUid", "Unknown")
+              }
+        # Build AI prompt with comprehensive security context
+        - setBody:
+            simple: |
+              You are a security analyst assistant. Analyze this security 
detection finding and provide a concise summary.
+
+              == SECURITY FINDING DETAILS ==
+              Title: ${header.findingTitle}
+              Description: ${header.findingDesc}
+              Types: ${header.findingTypes}
+              Message: ${header.message}
+
+              == CLASSIFICATION ==
+              Class: ${header.className}
+              Activity: ${header.activityName}
+              Severity: ${header.severity} (ID: ${header.severityId})
+              Risk Score: ${header.riskScore}
+              Risk Level: ${header.riskLevel}
+              Confidence: ${header.confidence}
+
+              == SOURCE/ATTACKER INFORMATION ==
+              Source IP: ${header.srcIp}
+              ISP: ${header.srcIsp} (${header.srcIspOrg})
+              ASN: ${header.srcAsn} (${header.srcAsnName})
+              Location: ${header.srcCity}, ${header.srcCountry}
+              Coordinates: ${header.srcLat}, ${header.srcLong}
+
+              == TARGET DEVICE ==
+              Hostname: ${header.deviceHostname}
+              Device IP: ${header.deviceIp}
+              Instance ID: ${header.deviceInstanceId}
+              Device Type: ${header.deviceType}
+              OS: ${header.deviceOs}
+
+              == CLOUD CONTEXT ==
+              Provider: ${header.cloudProvider}
+              Region: ${header.cloudRegion}
+              Account ID: ${header.cloudAccountId}
+
+              == API ACTIVITY ==
+              Operation: ${header.apiOperation}
+              Service: ${header.apiService}
+
+              == AFFECTED RESOURCES ==
+              Resource Type: ${header.resourceType}
+              Resource ID: ${header.resourceUid}
+
+              == MALWARE INDICATORS ==
+              Malware Name: ${header.malwareName}
+              Malware ID: ${header.malwareId}
+
+              == RECOMMENDED REMEDIATION ==
+              ${header.remediationDesc}
+
+              Please provide:
+              1. A one-sentence summary of the threat
+              2. Impact assessment (what could happen if not addressed)
+              3. Priority level (Critical/High/Medium/Low) with justification
+              4. Recommended immediate actions (3-5 bullet points)
+              5. IOCs (Indicators of Compromise) to block or monitor
+        - log:
+            message: "Sending finding to AI model for analysis..."
+        # Send to LangChain4j Chat for AI analysis
+        - to:
+            uri: langchain4j-chat:security-analyst
+            parameters:
+              chatModel: "#chatModel"
+        # Store AI analysis
+        - setProperty:
+            name: aiSummary
+            simple: "${body}"
+        - log:
+            message: "AI analysis completed"
+        # Build response
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - script:
+            groovy: |
+              def aiSummary = exchange.getProperty("aiSummary", String.class) 
?: ""
+              def findingTitle = 
exchange.getMessage().getHeader("findingTitle", String.class) ?: "Unknown"
+              def severity = exchange.getMessage().getHeader("severity", 
String.class) ?: "Unknown"
+              def dateStr = new 
java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new java.util.Date())
+              def modelName = "granite3:8b"
+
+              // Escape quotes in AI summary for JSON
+              def escapedSummary = aiSummary.replace("\\", 
"\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r")
+
+              // Build JSON response as regular String (not GString)
+              StringBuilder sb = new StringBuilder()
+              sb.append("{")
+              sb.append("\"status\": \"analyzed\",")
+              sb.append("\"finding_title\": 
\"").append(findingTitle).append("\",")
+              sb.append("\"severity\": \"").append(severity).append("\",")
+              sb.append("\"ai_summary\": 
\"").append(escapedSummary).append("\",")
+              sb.append("\"model\": \"").append(modelName).append("\",")
+              sb.append("\"timestamp\": \"").append(dateStr).append("\"")
+              sb.append("}")
+
+              exchange.getMessage().setBody(sb.toString())
+        - log:
+            message: |
+              ============================================================
+                         AI Security Finding Summary
+              ============================================================
+              ${exchangeProperty.aiSummary}
+              ============================================================
+
+# Authentication event endpoint
+# POST JSON payload to http://localhost:8080/api/ocsf/authentication
+- route:
+    id: ocsf-authentication-receiver
+    from:
+      uri: "platform-http:/api/ocsf/authentication"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received OCSF Authentication event"
+        - unmarshal:
+            ocsf:
+              unmarshalType: 
org.apache.camel.dataformat.ocsf.model.Authentication
+        - log:
+            message: "Unmarshalled Authentication event"
+        - script:
+            groovy: |
+              def auth = exchange.getMessage().getBody()
+              def props = auth.additionalProperties ?: [:]
+              def msg = exchange.getMessage()
+
+              // Basic authentication info
+              msg.setHeader("activityName", props.activity_name ?: "Unknown")
+              msg.setHeader("className", props.class_name ?: "Authentication")
+              msg.setHeader("severity", props.severity ?: "Unknown")
+              msg.setHeader("severityId", props.severity_id?.toString() ?: 
"N/A")
+              msg.setHeader("status", props.status ?: "Unknown")
+              msg.setHeader("statusId", props.status_id?.toString() ?: "N/A")
+              msg.setHeader("message", props.message ?: "No message")
+              msg.setHeader("timeDt", props.time_dt ?: "Unknown")
+
+              // Authentication details
+              msg.setHeader("authProtocol", props.auth_protocol ?: "Unknown")
+              msg.setHeader("authProtocolId", 
props.auth_protocol_id?.toString() ?: "N/A")
+              msg.setHeader("isMfaEnabled", auth.isMfa?.toString() ?: "false")
+              msg.setHeader("logonType", props.logon_type ?: "Unknown")
+              msg.setHeader("logonTypeId", props.logon_type_id?.toString() ?: 
"N/A")
+
+              // User info
+              def user = props.user ?: [:]
+              msg.setHeader("userName", user.name ?: "Unknown")
+              msg.setHeader("userUid", user.uid ?: "Unknown")
+              msg.setHeader("userType", user.type ?: "Unknown")
+              msg.setHeader("userDomain", user.domain ?: "Unknown")
+
+              // Source endpoint
+              def srcEndpoint = props.src_endpoint ?: [:]
+              msg.setHeader("srcIp", srcEndpoint.ip ?: "Unknown")
+              def srcLocation = srcEndpoint.location ?: [:]
+              msg.setHeader("srcCity", srcLocation.city ?: "Unknown")
+              msg.setHeader("srcCountry", srcLocation.country ?: "Unknown")
+
+              // Destination endpoint
+              def dstEndpoint = props.dst_endpoint ?: [:]
+              msg.setHeader("dstHostname", dstEndpoint.hostname ?: "Unknown")
+              msg.setHeader("dstIp", dstEndpoint.ip ?: "Unknown")
+              msg.setHeader("dstPort", dstEndpoint.port?.toString() ?: "N/A")
+              msg.setHeader("dstService", dstEndpoint.svc_name ?: "Unknown")
+
+              // Device info
+              def device = props.device ?: [:]
+              msg.setHeader("deviceHostname", device.hostname ?: "Unknown")
+              msg.setHeader("deviceIp", device.ip ?: "Unknown")
+
+              // Cloud info
+              def cloud = props.cloud ?: [:]
+              msg.setHeader("cloudProvider", cloud.provider ?: "Unknown")
+              msg.setHeader("cloudRegion", cloud.region ?: "Unknown")
+
+              // Product info
+              def metadata = props.metadata ?: [:]
+              def product = metadata.product ?: [:]
+              msg.setHeader("productName", product.name ?: "Unknown")
+              msg.setHeader("productVendor", product.vendor_name ?: "Unknown")
+        - log:
+            message: |
+              ============================================================
+                         OCSF Authentication Event
+              ============================================================
+              Activity: ${header.activityName}
+              Status: ${header.status} | Severity: ${header.severity}
+              Time: ${header.timeDt}
+              Message: ${header.message}
+              ------------------------------------------------------------
+              User: ${header.userName} (${header.userType})
+              User UID: ${header.userUid} | Domain: ${header.userDomain}
+              MFA Enabled: ${header.isMfaEnabled}
+              Logon Type: ${header.logonType}
+              Auth Protocol: ${header.authProtocol}
+              ------------------------------------------------------------
+              Source IP: ${header.srcIp}
+              Source Location: ${header.srcCity}, ${header.srcCountry}
+              ------------------------------------------------------------
+              Destination: ${header.dstHostname}:${header.dstPort}
+              Destination IP: ${header.dstIp}
+              Service: ${header.dstService}
+              ------------------------------------------------------------
+              Device: ${header.deviceHostname} (${header.deviceIp})
+              Cloud: ${header.cloudProvider} / ${header.cloudRegion}
+              Product: ${header.productName} (${header.productVendor})
+              ============================================================
+        - marshal:
+            ocsf: {}
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - setBody:
+            simple: |
+              {
+                "status": "received",
+                "message": "OCSF Authentication event processed successfully",
+                "user": "${header.userName}",
+                "auth_status": "${header.status}",
+                "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
+              }
+
+# Vulnerability Finding event endpoint
+# POST JSON payload to http://localhost:8080/api/ocsf/vulnerability-finding
+- route:
+    id: ocsf-vulnerability-finding-receiver
+    from:
+      uri: "platform-http:/api/ocsf/vulnerability-finding"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received OCSF VulnerabilityFinding event"
+        - unmarshal:
+            ocsf:
+              unmarshalType: 
org.apache.camel.dataformat.ocsf.model.VulnerabilityFinding
+        - log:
+            message: "Unmarshalled VulnerabilityFinding event"
+        - script:
+            groovy: |
+              def vulnFinding = exchange.getMessage().getBody()
+              def props = vulnFinding.additionalProperties ?: [:]
+              def msg = exchange.getMessage()
+
+              // Basic finding info from additionalProperties
+              msg.setHeader("activityName", props.activity_name ?: "Unknown")
+              msg.setHeader("className", props.class_name ?: "Vulnerability 
Finding")
+              msg.setHeader("severity", props.severity ?: "Unknown")
+              msg.setHeader("severityId", props.severity_id?.toString() ?: 
"N/A")
+              msg.setHeader("status", props.status ?: "Unknown")
+              msg.setHeader("message", props.message ?: "No message")
+              msg.setHeader("timeDt", props.time_dt ?: "Unknown")
+
+              // Finding info - use direct property (FindingInfo object)
+              def findingInfo = vulnFinding.findingInfo
+              if (findingInfo) {
+                msg.setHeader("findingTitle", findingInfo.title ?: "Unknown")
+                msg.setHeader("findingDesc", findingInfo.desc ?: "No 
description")
+                msg.setHeader("findingTypes", findingInfo.types?.toString() ?: 
"Unknown")
+                msg.setHeader("findingUid", findingInfo.uid ?: "Unknown")
+              } else {
+                def fi = props.finding_info ?: [:]
+                msg.setHeader("findingTitle", fi.title ?: "Unknown")
+                msg.setHeader("findingDesc", fi.desc ?: "No description")
+                msg.setHeader("findingTypes", fi.types?.toString() ?: 
"Unknown")
+                msg.setHeader("findingUid", fi.uid ?: "Unknown")
+              }
+
+              // Risk info from additionalProperties
+              msg.setHeader("riskLevel", props.risk_level ?: "Unknown")
+              msg.setHeader("riskLevelId", props.risk_level_id?.toString() ?: 
"N/A")
+              msg.setHeader("riskScore", props.risk_score?.toString() ?: "N/A")
+              msg.setHeader("confidence", vulnFinding.confidence ?: 
props.confidence ?: "Unknown")
+
+              // Vulnerability details - use direct property 
(List<Vulnerability>)
+              def vulnerabilities = vulnFinding.vulnerabilities
+              if (vulnerabilities && vulnerabilities.size() > 0) {
+                def firstVuln = vulnerabilities[0]
+                def vulnProps = firstVuln.additionalProperties ?: [:]
+
+                // Cve object has direct properties
+                def cve = firstVuln.cve
+                msg.setHeader("cveId", cve?.uid ?: "N/A")
+
+                // Cvss is on the Cve object, or may be in 
additionalProperties if placed directly on vulnerability
+                def cvss = cve?.cvss
+                if (cvss && cvss.size() > 0) {
+                  msg.setHeader("cvssScore", cvss[0].baseScore?.toString() ?: 
"N/A")
+                  msg.setHeader("cvssVector", cvss[0].vectorString ?: "N/A")
+                } else {
+                  // Fallback to additionalProperties if cvss is on 
vulnerability directly
+                  def cvssProps = vulnProps.cvss
+                  if (cvssProps && cvssProps.size() > 0) {
+                    msg.setHeader("cvssScore", 
cvssProps[0].base_score?.toString() ?: "N/A")
+                    msg.setHeader("cvssVector", cvssProps[0].vector_string ?: 
"N/A")
+                  } else {
+                    msg.setHeader("cvssScore", "N/A")
+                    msg.setHeader("cvssVector", "N/A")
+                  }
+                }
+
+                // Cwe object has direct properties
+                def cwe = firstVuln.cwe
+                msg.setHeader("cweId", cwe?.uid ?: "N/A")
+                msg.setHeader("cweCaption", cwe?.caption ?: "Unknown")
+
+                msg.setHeader("vulnDesc", firstVuln.desc ?: "No description")
+                msg.setHeader("vulnSeverity", firstVuln.severity ?: "Unknown")
+                msg.setHeader("vulnTitle", firstVuln.title ?: "Unknown")
+                msg.setHeader("isFixAvailable", 
firstVuln.isFixAvailable?.toString() ?: "Unknown")
+                msg.setHeader("isExploitAvailable", 
firstVuln.isExploitAvailable?.toString() ?: "Unknown")
+
+                // Packages - try both affectedPackages and packages
+                def packages = firstVuln.affectedPackages
+                if (!packages || packages.size() == 0) {
+                  packages = firstVuln.packages
+                }
+                if (packages && packages.size() > 0) {
+                  msg.setHeader("packageName", packages[0].name ?: "Unknown")
+                  msg.setHeader("packageVersion", packages[0].version ?: 
"Unknown")
+                } else {
+                  msg.setHeader("packageName", "Unknown")
+                  msg.setHeader("packageVersion", "Unknown")
+                }
+              } else {
+                msg.setHeader("cveId", "N/A")
+                msg.setHeader("cvssScore", "N/A")
+                msg.setHeader("cvssVector", "N/A")
+                msg.setHeader("cweId", "N/A")
+                msg.setHeader("cweCaption", "Unknown")
+                msg.setHeader("vulnDesc", "No description")
+                msg.setHeader("vulnSeverity", "Unknown")
+                msg.setHeader("vulnTitle", "Unknown")
+                msg.setHeader("isFixAvailable", "Unknown")
+                msg.setHeader("isExploitAvailable", "Unknown")
+                msg.setHeader("packageName", "Unknown")
+                msg.setHeader("packageVersion", "Unknown")
+              }
+
+              // Resource info - use direct property (ResourceDetails object)
+              def resource = vulnFinding.resource
+              if (resource) {
+                msg.setHeader("resourceType", resource.type ?: "Unknown")
+                msg.setHeader("resourceUid", resource.uid ?: "Unknown")
+                msg.setHeader("resourceName", resource.name ?: "Unknown")
+              } else {
+                def res = props.resource ?: [:]
+                msg.setHeader("resourceType", res.type ?: "Unknown")
+                msg.setHeader("resourceUid", res.uid ?: "Unknown")
+                msg.setHeader("resourceName", res.name ?: "Unknown")
+              }
+
+              // Cloud info from additionalProperties
+              def cloud = props.cloud ?: [:]
+              msg.setHeader("cloudProvider", cloud.provider ?: "Unknown")
+              msg.setHeader("cloudRegion", cloud.region ?: "Unknown")
+              msg.setHeader("cloudAccountId", cloud.account?.uid ?: "Unknown")
+
+              // Product info from additionalProperties
+              def metadata = props.metadata ?: [:]
+              def product = metadata.product ?: [:]
+              msg.setHeader("productName", product.name ?: "Unknown")
+              msg.setHeader("productVendor", product.vendor_name ?: "Unknown")
+        - log:
+            message: |
+              ============================================================
+                         OCSF Vulnerability Finding
+              ============================================================
+              Finding: ${header.findingTitle}
+              Description: ${header.findingDesc}
+              Severity: ${header.severity} | Risk Level: ${header.riskLevel}
+              Risk Score: ${header.riskScore} | Confidence: 
${header.confidence}
+              Time: ${header.timeDt}
+              ------------------------------------------------------------
+              CVE: ${header.cveId}
+              CVSS Score: ${header.cvssScore}
+              CVSS Vector: ${header.cvssVector}
+              CWE: ${header.cweId} - ${header.cweCaption}
+              ------------------------------------------------------------
+              Vulnerable Package: ${header.packageName} 
v${header.packageVersion}
+              Fix Available: ${header.isFixAvailable}
+              Exploit Available: ${header.isExploitAvailable}
+              ------------------------------------------------------------
+              Resource: ${header.resourceType}
+              Resource UID: ${header.resourceUid}
+              Resource Name: ${header.resourceName}
+              ------------------------------------------------------------
+              Cloud: ${header.cloudProvider} / ${header.cloudRegion}
+              Account: ${header.cloudAccountId}
+              Product: ${header.productName} (${header.productVendor})
+              ============================================================
+        - marshal:
+            ocsf: {}
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - setBody:
+            simple: |
+              {
+                "status": "received",
+                "message": "OCSF VulnerabilityFinding processed successfully",
+                "cve": "${header.cveId}",
+                "cvss_score": "${header.cvssScore}",
+                "risk_level": "${header.riskLevel}",
+                "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
+              }
+
+# Network Activity event endpoint
+# POST JSON payload to http://localhost:8080/api/ocsf/network-activity
+- route:
+    id: ocsf-network-activity-receiver
+    from:
+      uri: "platform-http:/api/ocsf/network-activity"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received OCSF NetworkActivity event"
+        - unmarshal:
+            ocsf:
+              unmarshalType: 
org.apache.camel.dataformat.ocsf.model.NetworkActivity
+        - log:
+            message: "Unmarshalled NetworkActivity event"
+        - script:
+            groovy: |
+              def network = exchange.getMessage().getBody()
+              def props = network.additionalProperties ?: [:]
+              def msg = exchange.getMessage()
+
+              // Basic activity info
+              msg.setHeader("activityName", props.activity_name ?: "Unknown")
+              msg.setHeader("className", props.class_name ?: "Network 
Activity")
+              msg.setHeader("severity", props.severity ?: "Unknown")
+              msg.setHeader("severityId", props.severity_id?.toString() ?: 
"N/A")
+              msg.setHeader("status", props.status ?: "Unknown")
+              msg.setHeader("message", props.message ?: "No message")
+              msg.setHeader("timeDt", props.time_dt ?: "Unknown")
+
+              // Action info
+              msg.setHeader("action", props.action ?: "Unknown")
+              msg.setHeader("actionId", props.action_id?.toString() ?: "N/A")
+              msg.setHeader("appName", network.appName ?: props.app_name ?: 
"Unknown")
+
+              // Connection info - use direct property first, fallback to 
additionalProperties
+              def connInfo = network.connectionInfo
+              if (connInfo) {
+                msg.setHeader("direction", 
connInfo.additionalProperties?.direction ?: "Unknown")
+                msg.setHeader("directionId", 
connInfo.additionalProperties?.direction_id?.toString() ?: "N/A")
+                msg.setHeader("boundary", 
connInfo.additionalProperties?.boundary ?: "Unknown")
+                msg.setHeader("protocolName", 
connInfo.additionalProperties?.protocol_name ?: "Unknown")
+                msg.setHeader("protocolNum", 
connInfo.additionalProperties?.protocol_num?.toString() ?: "N/A")
+              } else {
+                def connInfoProps = props.connection_info ?: [:]
+                msg.setHeader("direction", connInfoProps.direction ?: 
"Unknown")
+                msg.setHeader("directionId", 
connInfoProps.direction_id?.toString() ?: "N/A")
+                msg.setHeader("boundary", connInfoProps.boundary ?: "Unknown")
+                msg.setHeader("protocolName", connInfoProps.protocol_name ?: 
"Unknown")
+                msg.setHeader("protocolNum", 
connInfoProps.protocol_num?.toString() ?: "N/A")
+              }
+
+              // Source endpoint - use direct property first (NetworkEndpoint 
object)
+              def srcEndpoint = network.srcEndpoint
+              if (srcEndpoint) {
+                msg.setHeader("srcIp", srcEndpoint.ip ?: "Unknown")
+                msg.setHeader("srcPort", srcEndpoint.port?.toString() ?: "N/A")
+                msg.setHeader("srcHostname", srcEndpoint.hostname ?: "Unknown")
+                msg.setHeader("srcIsp", srcEndpoint.isp ?: "Unknown")
+                def srcLocation = srcEndpoint.location
+                if (srcLocation) {
+                  msg.setHeader("srcCity", srcLocation.city ?: "Unknown")
+                  msg.setHeader("srcCountry", srcLocation.country ?: "Unknown")
+                  msg.setHeader("srcLat", srcLocation.lat?.toString() ?: "N/A")
+                  msg.setHeader("srcLong", 
srcLocation.additionalProperties?.long?.toString() ?: "N/A")
+                } else {
+                  msg.setHeader("srcCity", "Unknown")
+                  msg.setHeader("srcCountry", "Unknown")
+                  msg.setHeader("srcLat", "N/A")
+                  msg.setHeader("srcLong", "N/A")
+                }
+                def srcAsn = srcEndpoint.autonomousSystem
+                if (srcAsn) {
+                  // AutonomousSystem only has additionalProperties
+                  msg.setHeader("srcAsn", 
srcAsn.additionalProperties?.number?.toString() ?: "N/A")
+                  msg.setHeader("srcAsnName", 
srcAsn.additionalProperties?.name ?: "Unknown")
+                } else {
+                  msg.setHeader("srcAsn", "N/A")
+                  msg.setHeader("srcAsnName", "Unknown")
+                }
+              } else {
+                def srcEp = props.src_endpoint ?: [:]
+                msg.setHeader("srcIp", srcEp.ip ?: "Unknown")
+                msg.setHeader("srcPort", srcEp.port?.toString() ?: "N/A")
+                msg.setHeader("srcHostname", srcEp.hostname ?: "Unknown")
+                msg.setHeader("srcIsp", srcEp.isp ?: "Unknown")
+                def srcLoc = srcEp.location ?: [:]
+                msg.setHeader("srcCity", srcLoc.city ?: "Unknown")
+                msg.setHeader("srcCountry", srcLoc.country ?: "Unknown")
+                msg.setHeader("srcLat", srcLoc.lat?.toString() ?: "N/A")
+                msg.setHeader("srcLong", srcLoc.long?.toString() ?: "N/A")
+                def srcAs = srcEp.autonomous_system ?: [:]
+                msg.setHeader("srcAsn", srcAs.number?.toString() ?: "N/A")
+                msg.setHeader("srcAsnName", srcAs.name ?: "Unknown")
+              }
+
+              // Destination endpoint - use direct property first 
(NetworkEndpoint object)
+              def dstEndpoint = network.dstEndpoint
+              if (dstEndpoint) {
+                msg.setHeader("dstIp", dstEndpoint.ip ?: "Unknown")
+                msg.setHeader("dstPort", dstEndpoint.port?.toString() ?: "N/A")
+                msg.setHeader("dstHostname", dstEndpoint.hostname ?: "Unknown")
+                msg.setHeader("dstService", dstEndpoint.svcName ?: "Unknown")
+                def dstLocation = dstEndpoint.location
+                if (dstLocation) {
+                  msg.setHeader("dstCity", dstLocation.city ?: "Unknown")
+                  msg.setHeader("dstCountry", dstLocation.country ?: "Unknown")
+                } else {
+                  msg.setHeader("dstCity", "Unknown")
+                  msg.setHeader("dstCountry", "Unknown")
+                }
+                def dstAsn = dstEndpoint.autonomousSystem
+                if (dstAsn) {
+                  // AutonomousSystem only has additionalProperties
+                  msg.setHeader("dstAsn", 
dstAsn.additionalProperties?.number?.toString() ?: "N/A")
+                  msg.setHeader("dstAsnName", 
dstAsn.additionalProperties?.name ?: "Unknown")
+                } else {
+                  msg.setHeader("dstAsn", "N/A")
+                  msg.setHeader("dstAsnName", "Unknown")
+                }
+              } else {
+                def dstEp = props.dst_endpoint ?: [:]
+                msg.setHeader("dstIp", dstEp.ip ?: "Unknown")
+                msg.setHeader("dstPort", dstEp.port?.toString() ?: "N/A")
+                msg.setHeader("dstHostname", dstEp.hostname ?: "Unknown")
+                msg.setHeader("dstService", dstEp.svc_name ?: "Unknown")
+                def dstLoc = dstEp.location ?: [:]
+                msg.setHeader("dstCity", dstLoc.city ?: "Unknown")
+                msg.setHeader("dstCountry", dstLoc.country ?: "Unknown")
+                def dstAs = dstEp.autonomous_system ?: [:]
+                msg.setHeader("dstAsn", dstAs.number?.toString() ?: "N/A")
+                msg.setHeader("dstAsnName", dstAs.name ?: "Unknown")
+              }
+
+              // Traffic info - Traffic__4 only has additionalProperties
+              def traffic = network.traffic
+              if (traffic) {
+                def trafficProps = traffic.additionalProperties ?: [:]
+                msg.setHeader("bytesIn", trafficProps.bytes_in?.toString() ?: 
"0")
+                msg.setHeader("bytesOut", trafficProps.bytes_out?.toString() 
?: "0")
+                msg.setHeader("packetsIn", trafficProps.packets_in?.toString() 
?: "0")
+                msg.setHeader("packetsOut", 
trafficProps.packets_out?.toString() ?: "0")
+              } else {
+                def trafficProps = props.traffic ?: [:]
+                msg.setHeader("bytesIn", trafficProps.bytes_in?.toString() ?: 
"0")
+                msg.setHeader("bytesOut", trafficProps.bytes_out?.toString() 
?: "0")
+                msg.setHeader("packetsIn", trafficProps.packets_in?.toString() 
?: "0")
+                msg.setHeader("packetsOut", 
trafficProps.packets_out?.toString() ?: "0")
+              }
+
+              // Duration and count
+              msg.setHeader("duration", props.duration?.toString() ?: "0")
+              msg.setHeader("count", props.count?.toString() ?: "1")
+
+              // TLS info - Tls has direct properties (version, cipher, 
certificate)
+              def tls = network.tls
+              if (tls) {
+                msg.setHeader("tlsVersion", tls.version ?: "N/A")
+                msg.setHeader("tlsCipher", tls.cipher ?: "N/A")
+                def tlsCert = tls.certificate
+                msg.setHeader("tlsCertSubject", tlsCert?.subject ?: "N/A")
+              } else {
+                def tlsProps = props.tls ?: [:]
+                msg.setHeader("tlsVersion", tlsProps.version ?: "N/A")
+                msg.setHeader("tlsCipher", tlsProps.cipher ?: "N/A")
+                def tlsCertProps = tlsProps.certificate ?: [:]
+                msg.setHeader("tlsCertSubject", tlsCertProps.subject ?: "N/A")
+              }
+
+              // Cloud info
+              def cloud = props.cloud ?: [:]
+              msg.setHeader("cloudProvider", cloud.provider ?: "Unknown")
+              msg.setHeader("cloudRegion", cloud.region ?: "Unknown")
+              msg.setHeader("cloudAccountId", cloud.account?.uid ?: "Unknown")
+
+              // Product info
+              def metadata = props.metadata ?: [:]
+              def product = metadata.product ?: [:]
+              msg.setHeader("productName", product.name ?: "Unknown")
+              msg.setHeader("productVendor", product.vendor_name ?: "Unknown")
+        - log:
+            message: |
+              ============================================================
+                         OCSF Network Activity
+              ============================================================
+              Activity: ${header.activityName} | Action: ${header.action}
+              Severity: ${header.severity} | Status: ${header.status}
+              Time: ${header.timeDt}
+              Message: ${header.message}
+              ------------------------------------------------------------
+              Direction: ${header.direction} | Protocol: ${header.protocolName}
+              Boundary: ${header.boundary}
+              ------------------------------------------------------------
+              Source: ${header.srcIp}:${header.srcPort}
+              Source Host: ${header.srcHostname}
+              Source Location: ${header.srcCity}, ${header.srcCountry}
+              Source ISP: ${header.srcIsp}
+              Source ASN: ${header.srcAsn} (${header.srcAsnName})
+              ------------------------------------------------------------
+              Destination: ${header.dstIp}:${header.dstPort}
+              Dest Host: ${header.dstHostname}
+              Dest Service: ${header.dstService}
+              Dest Location: ${header.dstCity}, ${header.dstCountry}
+              Dest ASN: ${header.dstAsn} (${header.dstAsnName})
+              ------------------------------------------------------------
+              Traffic: ${header.bytesIn} bytes in, ${header.bytesOut} bytes out
+              Packets: ${header.packetsIn} in, ${header.packetsOut} out
+              Duration: ${header.duration}ms | Count: ${header.count}
+              ------------------------------------------------------------
+              TLS: ${header.tlsVersion} | Cipher: ${header.tlsCipher}
+              Certificate Subject: ${header.tlsCertSubject}
+              ------------------------------------------------------------
+              Cloud: ${header.cloudProvider} / ${header.cloudRegion}
+              Account: ${header.cloudAccountId}
+              Product: ${header.productName} (${header.productVendor})
+              ============================================================
+        - marshal:
+            ocsf: {}
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - setBody:
+            simple: |
+              {
+                "status": "received",
+                "message": "OCSF NetworkActivity processed successfully",
+                "action": "${header.action}",
+                "direction": "${header.direction}",
+                "src_ip": "${header.srcIp}",
+                "dst_ip": "${header.dstIp}",
+                "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
+              }
+
+# File Activity event endpoint
+# POST JSON payload to http://localhost:8080/api/ocsf/file-activity
+- route:
+    id: ocsf-file-activity-receiver
+    from:
+      uri: "platform-http:/api/ocsf/file-activity"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received OCSF FileActivity event"
+        - unmarshal:
+            ocsf:
+              unmarshalType: 
org.apache.camel.dataformat.ocsf.model.FileActivity
+        - log:
+            message: "Unmarshalled FileActivity event"
+        - script:
+            groovy: |
+              def fileActivity = exchange.getMessage().getBody()
+              def props = fileActivity.additionalProperties ?: [:]
+              def msg = exchange.getMessage()
+
+              // Basic activity info from additionalProperties
+              msg.setHeader("activityName", props.activity_name ?: "Unknown")
+              msg.setHeader("className", props.class_name ?: "File Activity")
+              msg.setHeader("severity", props.severity ?: "Unknown")
+              msg.setHeader("severityId", props.severity_id?.toString() ?: 
"N/A")
+              msg.setHeader("status", props.status ?: "Unknown")
+              msg.setHeader("message", props.message ?: "No message")
+              msg.setHeader("timeDt", props.time_dt ?: "Unknown")
+
+              // File info - use direct property (File object)
+              def file = fileActivity.file
+              if (file) {
+                msg.setHeader("fileName", file.name ?: "Unknown")
+                msg.setHeader("filePath", file.path ?: "Unknown")
+                msg.setHeader("fileSize", file.size?.toString() ?: "0")
+                msg.setHeader("fileType", file.type ?: "Unknown")
+
+                // File hashes - List<Fingerprint>
+                def hashes = file.hashes
+                if (hashes && hashes.size() > 0) {
+                  def hashList = hashes.collect { h -> "${h.algorithm ?: 
'Unknown'}: ${h.value ?: 'N/A'}" }
+                  msg.setHeader("fileHashes", hashList.join(", "))
+                } else {
+                  msg.setHeader("fileHashes", "None")
+                }
+
+                // Signature info - Signature only has additionalProperties
+                def signature = file.signature
+                if (signature) {
+                  msg.setHeader("signatureState", 
signature.additionalProperties?.state ?: "Unknown")
+                } else {
+                  msg.setHeader("signatureState", "Unknown")
+                }
+              } else {
+                def fileProps = props.file ?: [:]
+                msg.setHeader("fileName", fileProps.name ?: "Unknown")
+                msg.setHeader("filePath", fileProps.path ?: "Unknown")
+                msg.setHeader("fileSize", fileProps.size?.toString() ?: "0")
+                msg.setHeader("fileType", fileProps.type ?: "Unknown")
+                msg.setHeader("fileHashes", "None")
+                msg.setHeader("signatureState", fileProps.signature?.state ?: 
"Unknown")
+              }
+
+              // Actor info - use direct property (Actor object)
+              def actor = fileActivity.actor
+              if (actor) {
+                def actorProcess = actor.process
+                if (actorProcess) {
+                  msg.setHeader("actorProcessName", actorProcess.name ?: 
"Unknown")
+                  msg.setHeader("actorProcessPid", 
actorProcess.pid?.toString() ?: "N/A")
+                  msg.setHeader("actorProcessCmd", actorProcess.cmdLine ?: 
"Unknown")
+                  def actorFile = actorProcess.file
+                  msg.setHeader("actorProcessPath", actorFile?.path ?: 
"Unknown")
+                } else {
+                  msg.setHeader("actorProcessName", "Unknown")
+                  msg.setHeader("actorProcessPid", "N/A")
+                  msg.setHeader("actorProcessCmd", "Unknown")
+                  msg.setHeader("actorProcessPath", "Unknown")
+                }
+
+                def actorUser = actor.user
+                if (actorUser) {
+                  msg.setHeader("actorUserName", actorUser.name ?: "Unknown")
+                  msg.setHeader("actorUserUid", actorUser.uid ?: "Unknown")
+                  msg.setHeader("actorUserType", actorUser.type ?: "Unknown")
+                } else {
+                  msg.setHeader("actorUserName", "Unknown")
+                  msg.setHeader("actorUserUid", "Unknown")
+                  msg.setHeader("actorUserType", "Unknown")
+                }
+              } else {
+                def actorProps = props.actor ?: [:]
+                def actorProcessProps = actorProps.process ?: [:]
+                msg.setHeader("actorProcessName", actorProcessProps.name ?: 
"Unknown")
+                msg.setHeader("actorProcessPid", 
actorProcessProps.pid?.toString() ?: "N/A")
+                msg.setHeader("actorProcessCmd", actorProcessProps.cmd_line ?: 
"Unknown")
+                msg.setHeader("actorProcessPath", actorProcessProps.file?.path 
?: "Unknown")
+                def actorUserProps = actorProps.user ?: [:]
+                msg.setHeader("actorUserName", actorUserProps.name ?: 
"Unknown")
+                msg.setHeader("actorUserUid", actorUserProps.uid ?: "Unknown")
+                msg.setHeader("actorUserType", actorUserProps.type ?: 
"Unknown")
+              }
+
+              // Device info - use direct property (Device object)
+              def device = fileActivity.device
+              if (device) {
+                msg.setHeader("deviceHostname", device.hostname ?: "Unknown")
+                msg.setHeader("deviceIp", device.ip ?: "Unknown")
+                msg.setHeader("deviceType", device.type ?: "Unknown")
+                def deviceOs = device.os
+                if (deviceOs) {
+                  msg.setHeader("deviceOs", deviceOs.name ? "${deviceOs.name} 
${deviceOs.version ?: ''}" : "Unknown")
+                } else {
+                  msg.setHeader("deviceOs", "Unknown")
+                }
+              } else {
+                def deviceProps = props.device ?: [:]
+                msg.setHeader("deviceHostname", deviceProps.hostname ?: 
"Unknown")
+                msg.setHeader("deviceIp", deviceProps.ip ?: "Unknown")
+                msg.setHeader("deviceType", deviceProps.type ?: "Unknown")
+                def deviceOsProps = deviceProps.os ?: [:]
+                msg.setHeader("deviceOs", deviceOsProps.name ? 
"${deviceOsProps.name} ${deviceOsProps.version ?: ''}" : "Unknown")
+              }
+
+              // Product info from additionalProperties
+              def metadata = props.metadata ?: [:]
+              def product = metadata.product ?: [:]
+              msg.setHeader("productName", product.name ?: "Unknown")
+              msg.setHeader("productVendor", product.vendor_name ?: "Unknown")
+        - log:
+            message: |
+              ============================================================
+                         OCSF File Activity
+              ============================================================
+              Activity: ${header.activityName}
+              Severity: ${header.severity} | Status: ${header.status}
+              Time: ${header.timeDt}
+              Message: ${header.message}
+              ------------------------------------------------------------
+              File: ${header.fileName}
+              Path: ${header.filePath}
+              Size: ${header.fileSize} bytes
+              Type: ${header.fileType}
+              Signature: ${header.signatureState}
+              Hashes: ${header.fileHashes}
+              ------------------------------------------------------------
+              Actor Process: ${header.actorProcessName} (PID: 
${header.actorProcessPid})
+              Process Path: ${header.actorProcessPath}
+              Command Line: ${header.actorProcessCmd}
+              Actor User: ${header.actorUserName} (${header.actorUserType})
+              ------------------------------------------------------------
+              Device: ${header.deviceHostname} (${header.deviceIp})
+              Device Type: ${header.deviceType}
+              OS: ${header.deviceOs}
+              Product: ${header.productName} (${header.productVendor})
+              ============================================================
+        - marshal:
+            ocsf: {}
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - script:
+            groovy: |
+              def msg = exchange.getMessage()
+              def activity = msg.getHeader("activityName", String.class) ?: 
"Unknown"
+              def fileName = msg.getHeader("fileName", String.class) ?: 
"Unknown"
+              def filePath = msg.getHeader("filePath", String.class) ?: 
"Unknown"
+              def actorProcess = msg.getHeader("actorProcessName", 
String.class) ?: "Unknown"
+              def timestamp = new 
java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new java.util.Date())
+
+              // Escape backslashes for JSON
+              filePath = filePath.replace('\\', '/')
+
+              StringBuilder sb = new StringBuilder()
+              sb.append("{")
+              sb.append("\"status\": \"received\",")
+              sb.append("\"message\": \"OCSF FileActivity processed 
successfully\",")
+              sb.append("\"activity\": \"").append(activity).append("\",")
+              sb.append("\"file_name\": \"").append(fileName).append("\",")
+              sb.append("\"file_path\": \"").append(filePath).append("\",")
+              sb.append("\"actor_process\": 
\"").append(actorProcess).append("\",")
+              sb.append("\"timestamp\": \"").append(timestamp).append("\"")
+              sb.append("}")
+              msg.setBody(sb.toString())
+
+# Process Activity event endpoint
+# POST JSON payload to http://localhost:8080/api/ocsf/process-activity
+- route:
+    id: ocsf-process-activity-receiver
+    from:
+      uri: "platform-http:/api/ocsf/process-activity"
+      parameters:
+        httpMethodRestrict: POST
+      steps:
+        - log:
+            message: "Received OCSF ProcessActivity event"
+        - unmarshal:
+            ocsf:
+              unmarshalType: 
org.apache.camel.dataformat.ocsf.model.ProcessActivity
+        - log:
+            message: "Unmarshalled ProcessActivity event"
+        - script:
+            groovy: |
+              def processActivity = exchange.getMessage().getBody()
+              def props = processActivity.additionalProperties ?: [:]
+              def msg = exchange.getMessage()
+
+              // Basic activity info from additionalProperties
+              msg.setHeader("activityName", props.activity_name ?: "Unknown")
+              msg.setHeader("className", props.class_name ?: "Process 
Activity")
+              msg.setHeader("severity", props.severity ?: "Unknown")
+              msg.setHeader("severityId", props.severity_id?.toString() ?: 
"N/A")
+              msg.setHeader("status", props.status ?: "Unknown")
+              msg.setHeader("message", props.message ?: "No message")
+              msg.setHeader("timeDt", props.time_dt ?: "Unknown")
+
+              // Process info - use direct property (Process object)
+              def process = processActivity.process
+              if (process) {
+                msg.setHeader("processName", process.name ?: "Unknown")
+                msg.setHeader("processPid", process.pid?.toString() ?: "N/A")
+                msg.setHeader("processTid", process.tid?.toString() ?: "N/A")
+                msg.setHeader("processUid", process.uid ?: "Unknown")
+                msg.setHeader("processCmd", process.cmdLine ?: "Unknown")
+                def processFile = process.file
+                msg.setHeader("processPath", processFile?.path ?: "Unknown")
+
+                // Parent process
+                def parentProcess = process.parentProcess
+                if (parentProcess) {
+                  msg.setHeader("parentProcessName", parentProcess.name ?: 
"Unknown")
+                  msg.setHeader("parentProcessPid", 
parentProcess.pid?.toString() ?: "N/A")
+                  def parentFile = parentProcess.file
+                  msg.setHeader("parentProcessPath", parentFile?.path ?: 
"Unknown")
+                } else {
+                  msg.setHeader("parentProcessName", "Unknown")
+                  msg.setHeader("parentProcessPid", "N/A")
+                  msg.setHeader("parentProcessPath", "Unknown")
+                }
+
+                // Process user
+                def processUser = process.user
+                if (processUser) {
+                  msg.setHeader("processUserName", processUser.name ?: 
"Unknown")
+                  msg.setHeader("processUserType", processUser.type ?: 
"Unknown")
+                } else {
+                  msg.setHeader("processUserName", "Unknown")
+                  msg.setHeader("processUserType", "Unknown")
+                }
+              } else {
+                def processProps = props.process ?: [:]
+                msg.setHeader("processName", processProps.name ?: "Unknown")
+                msg.setHeader("processPid", processProps.pid?.toString() ?: 
"N/A")
+                msg.setHeader("processTid", processProps.tid?.toString() ?: 
"N/A")
+                msg.setHeader("processUid", processProps.uid ?: "Unknown")
+                msg.setHeader("processCmd", processProps.cmd_line ?: "Unknown")
+                msg.setHeader("processPath", processProps.file?.path ?: 
"Unknown")
+                def parentProcessProps = processProps.parent_process ?: [:]
+                msg.setHeader("parentProcessName", parentProcessProps.name ?: 
"Unknown")
+                msg.setHeader("parentProcessPid", 
parentProcessProps.pid?.toString() ?: "N/A")
+                msg.setHeader("parentProcessPath", 
parentProcessProps.file?.path ?: "Unknown")
+                def processUserProps = processProps.user ?: [:]
+                msg.setHeader("processUserName", processUserProps.name ?: 
"Unknown")
+                msg.setHeader("processUserType", processUserProps.type ?: 
"Unknown")
+              }
+
+              // Actor info - use direct property (Actor object)
+              def actor = processActivity.actor
+              if (actor) {
+                def actorProcess = actor.process
+                if (actorProcess) {
+                  msg.setHeader("actorProcessName", actorProcess.name ?: 
"Unknown")
+                  msg.setHeader("actorProcessPid", 
actorProcess.pid?.toString() ?: "N/A")
+                  msg.setHeader("actorProcessCmd", actorProcess.cmdLine ?: 
"Unknown")
+                  def actorParent = actorProcess.parentProcess
+                  if (actorParent) {
+                    msg.setHeader("actorParentName", actorParent.name ?: 
"Unknown")
+                    msg.setHeader("actorParentPid", 
actorParent.pid?.toString() ?: "N/A")
+                    msg.setHeader("actorParentCmd", actorParent.cmdLine ?: 
"Unknown")
+                  } else {
+                    msg.setHeader("actorParentName", "Unknown")
+                    msg.setHeader("actorParentPid", "N/A")
+                    msg.setHeader("actorParentCmd", "Unknown")
+                  }
+                } else {
+                  msg.setHeader("actorProcessName", "Unknown")
+                  msg.setHeader("actorProcessPid", "N/A")
+                  msg.setHeader("actorProcessCmd", "Unknown")
+                  msg.setHeader("actorParentName", "Unknown")
+                  msg.setHeader("actorParentPid", "N/A")
+                  msg.setHeader("actorParentCmd", "Unknown")
+                }
+
+                def actorUser = actor.user
+                if (actorUser) {
+                  msg.setHeader("actorUserName", actorUser.name ?: "Unknown")
+                  msg.setHeader("actorUserUid", actorUser.uid ?: "Unknown")
+                  msg.setHeader("actorUserType", actorUser.type ?: "Unknown")
+                } else {
+                  msg.setHeader("actorUserName", "Unknown")
+                  msg.setHeader("actorUserUid", "Unknown")
+                  msg.setHeader("actorUserType", "Unknown")
+                }
+              } else {
+                def actorProps = props.actor ?: [:]
+                def actorProcessProps = actorProps.process ?: [:]
+                msg.setHeader("actorProcessName", actorProcessProps.name ?: 
"Unknown")
+                msg.setHeader("actorProcessPid", 
actorProcessProps.pid?.toString() ?: "N/A")
+                msg.setHeader("actorProcessCmd", actorProcessProps.cmd_line ?: 
"Unknown")
+                def actorParentProps = actorProcessProps.parent_process ?: [:]
+                msg.setHeader("actorParentName", actorParentProps.name ?: 
"Unknown")
+                msg.setHeader("actorParentPid", 
actorParentProps.pid?.toString() ?: "N/A")
+                msg.setHeader("actorParentCmd", actorParentProps.cmd_line ?: 
"Unknown")
+                def actorUserProps = actorProps.user ?: [:]
+                msg.setHeader("actorUserName", actorUserProps.name ?: 
"Unknown")
+                msg.setHeader("actorUserUid", actorUserProps.uid ?: "Unknown")
+                msg.setHeader("actorUserType", actorUserProps.type ?: 
"Unknown")
+              }
+
+              // Device info - use direct property (Device object)
+              def device = processActivity.device
+              if (device) {
+                msg.setHeader("deviceHostname", device.hostname ?: "Unknown")
+                msg.setHeader("deviceIp", device.ip ?: "Unknown")
+                msg.setHeader("deviceType", device.type ?: "Unknown")
+                def deviceOs = device.os
+                if (deviceOs) {
+                  msg.setHeader("deviceOs", deviceOs.name ? "${deviceOs.name} 
${deviceOs.version ?: ''}" : "Unknown")
+                } else {
+                  msg.setHeader("deviceOs", "Unknown")
+                }
+              } else {
+                def deviceProps = props.device ?: [:]
+                msg.setHeader("deviceHostname", deviceProps.hostname ?: 
"Unknown")
+                msg.setHeader("deviceIp", deviceProps.ip ?: "Unknown")
+                msg.setHeader("deviceType", deviceProps.type ?: "Unknown")
+                def deviceOsProps = deviceProps.os ?: [:]
+                msg.setHeader("deviceOs", deviceOsProps.name ? 
"${deviceOsProps.name} ${deviceOsProps.version ?: ''}" : "Unknown")
+              }
+
+              // Product info from additionalProperties
+              def metadata = props.metadata ?: [:]
+              def product = metadata.product ?: [:]
+              msg.setHeader("productName", product.name ?: "Unknown")
+              msg.setHeader("productVendor", product.vendor_name ?: "Unknown")
+        - log:
+            message: |
+              ============================================================
+                         OCSF Process Activity
+              ============================================================
+              Activity: ${header.activityName}
+              Severity: ${header.severity} | Status: ${header.status}
+              Time: ${header.timeDt}
+              Message: ${header.message}
+              ------------------------------------------------------------
+              Process: ${header.processName} (PID: ${header.processPid}, TID: 
${header.processTid})
+              Process Path: ${header.processPath}
+              Command Line: ${header.processCmd}
+              Process User: ${header.processUserName} 
(${header.processUserType})
+              ------------------------------------------------------------
+              Parent Process: ${header.parentProcessName} (PID: 
${header.parentProcessPid})
+              Parent Path: ${header.parentProcessPath}
+              ------------------------------------------------------------
+              Actor Process: ${header.actorProcessName} (PID: 
${header.actorProcessPid})
+              Actor Command: ${header.actorProcessCmd}
+              Actor's Parent: ${header.actorParentName} (PID: 
${header.actorParentPid})
+              Actor's Parent Cmd: ${header.actorParentCmd}
+              Actor User: ${header.actorUserName} (${header.actorUserType})
+              ------------------------------------------------------------
+              Device: ${header.deviceHostname} (${header.deviceIp})
+              Device Type: ${header.deviceType}
+              OS: ${header.deviceOs}
+              Product: ${header.productName} (${header.productVendor})
+              ============================================================
+        - marshal:
+            ocsf: {}
+        - setHeader:
+            name: Content-Type
+            constant: application/json
+        - setBody:
+            simple: |
+              {
+                "status": "received",
+                "message": "OCSF ProcessActivity processed successfully",
+                "activity": "${header.activityName}",
+                "process_name": "${header.processName}",
+                "process_pid": "${header.processPid}",
+                "parent_process": "${header.parentProcessName}",
+                "actor_process": "${header.actorProcessName}",
+                "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
+              }
diff --git a/ocsf/test/authentication-event.json 
b/ocsf/test/authentication-event.json
new file mode 100644
index 0000000..82afee0
--- /dev/null
+++ b/ocsf/test/authentication-event.json
@@ -0,0 +1,117 @@
+{
+  "activity_id": 1,
+  "activity_name": "Logon",
+  "category_name": "Identity & Access Management",
+  "category_uid": 3,
+  "class_name": "Authentication",
+  "class_uid": 3002,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "987654321098"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "us-west-2"
+  },
+  "dst_endpoint": {
+    "hostname": "signin.aws.amazon.com",
+    "ip": "52.94.236.45",
+    "port": 443,
+    "svc_name": "AWS Management Console"
+  },
+  "http_request": {
+    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) 
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
+  },
+  "is_mfa": true,
+  "logon_type": "Interactive",
+  "logon_type_id": 2,
+  "message": "User successfully authenticated to AWS Management Console with 
MFA",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "CloudTrail"
+      },
+      "name": "AWS CloudTrail",
+      "uid": 
"arn:aws:cloudtrail:us-west-2:987654321098:trail/management-events",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "datetime"
+    ],
+    "uid": "auth-event-uuid-67890",
+    "version": "1.6.0"
+  },
+  "service": {
+    "name": "AWS IAM Identity Center",
+    "uid": "arn:aws:sso:::instance/ssoins-12345"
+  },
+  "session": {
+    "created_time": 1769425200000,
+    "created_time_dt": "2026-01-26T11:00:00.000Z",
+    "credential_uid": "ASIA3LNAP5JEXAMPLE",
+    "expiration_time": 1769468400000,
+    "expiration_time_dt": "2026-01-26T23:00:00.000Z",
+    "is_remote": true,
+    "issuer": "arn:aws:iam::987654321098:saml-provider/ADFS",
+    "uid": "session-12345-abcde"
+  },
+  "severity": "Informational",
+  "severity_id": 1,
+  "src_endpoint": {
+    "autonomous_system": {
+      "name": "Comcast Cable Communications",
+      "number": 7922
+    },
+    "ip": "73.45.123.89",
+    "isp": "Comcast",
+    "isp_org": "Comcast Cable Communications LLC",
+    "location": {
+      "city": "San Francisco",
+      "country": "United States",
+      "lat": 37.7749,
+      "long": -122.4194,
+      "region": "California"
+    }
+  },
+  "status": "Success",
+  "status_id": 1,
+  "time": 1769425200000,
+  "time_dt": "2026-01-26T11:00:00.000Z",
+  "type_name": "Authentication: Logon",
+  "type_uid": 300201,
+  "user": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "987654321098"
+    },
+    "email_addr": "[email protected]",
+    "groups": [
+      {
+        "name": "Developers",
+        "type": "IAM Group",
+        "uid": "arn:aws:iam::987654321098:group/Developers"
+      },
+      {
+        "name": "ReadOnlyAccess",
+        "type": "IAM Group",
+        "uid": "arn:aws:iam::987654321098:group/ReadOnlyAccess"
+      }
+    ],
+    "name": "john.smith",
+    "type": "User",
+    "type_id": 1,
+    "uid": "arn:aws:iam::987654321098:user/john.smith",
+    "uid_alt": "AIDA3LNAP5JEXAMPLE"
+  }
+}
diff --git a/ocsf/test/authentication_1.json b/ocsf/test/authentication_1.json
new file mode 100644
index 0000000..0530130
--- /dev/null
+++ b/ocsf/test/authentication_1.json
@@ -0,0 +1,104 @@
+{
+  "activity_id": 1,
+  "activity_name": "Logon",
+  "category_name": "Identity & Access Management",
+  "category_uid": 3,
+  "class_name": "Authentication",
+  "class_uid": 3002,
+  "actor": {
+    "user": {
+      "name": "john.doe",
+      "uid": "[email protected]",
+      "type": "User",
+      "type_id": 1,
+      "account": {
+        "name": "john.doe",
+        "uid": "123456",
+        "type": "AWS IAM User",
+        "type_id": 3
+      }
+    },
+    "session": {
+      "uid": "sess-abc123def456",
+      "created_time": 1769480000000
+    }
+  },
+  "auth_protocol": "SAML",
+  "auth_protocol_id": 5,
+  "auth_factors": [
+    {
+      "factor_type": "Password",
+      "factor_type_id": 1
+    },
+    {
+      "factor_type": "TOTP",
+      "factor_type_id": 3
+    }
+  ],
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "123456789012"
+    },
+    "provider": "AWS",
+    "region": "us-east-1"
+  },
+  "dst_endpoint": {
+    "hostname": "console.aws.amazon.com",
+    "ip": "52.94.236.2",
+    "port": 443,
+    "svc_name": "AWS Console"
+  },
+  "http_request": {
+    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) 
AppleWebKit/537.36"
+  },
+  "is_cleartext": false,
+  "is_mfa": true,
+  "is_new_logon": true,
+  "is_remote": true,
+  "logon_type": "Interactive",
+  "logon_type_id": 2,
+  "message": "Successful MFA login to AWS Console from corporate network",
+  "metadata": {
+    "product": {
+      "name": "AWS CloudTrail",
+      "vendor_name": "AWS",
+      "version": "1.0"
+    },
+    "version": "1.6.0"
+  },
+  "service": {
+    "name": "AWS IAM Identity Center",
+    "uid": "sso.amazonaws.com"
+  },
+  "severity": "Informational",
+  "severity_id": 1,
+  "src_endpoint": {
+    "ip": "203.0.113.50",
+    "location": {
+      "city": "San Francisco",
+      "country": "United States",
+      "lat": 37.7749,
+      "long": -122.4194
+    },
+    "isp": "Comcast",
+    "autonomous_system": {
+      "name": "Comcast Cable Communications",
+      "number": 7922
+    }
+  },
+  "status": "Success",
+  "status_id": 1,
+  "time": 1769480000000,
+  "time_dt": "2026-01-27T02:13:20.000Z",
+  "type_name": "Authentication: Logon",
+  "type_uid": 300201,
+  "user": {
+    "name": "john.doe",
+    "email": "[email protected]",
+    "uid": "jdoe",
+    "type": "User",
+    "type_id": 1
+  }
+}
diff --git a/ocsf/test/authentication_2.json b/ocsf/test/authentication_2.json
new file mode 100644
index 0000000..16ccce0
--- /dev/null
+++ b/ocsf/test/authentication_2.json
@@ -0,0 +1,87 @@
+{
+  "activity_id": 2,
+  "activity_name": "Logoff",
+  "category_name": "Identity & Access Management",
+  "category_uid": 3,
+  "class_name": "Authentication",
+  "class_uid": 3002,
+  "actor": {
+    "user": {
+      "name": "suspicious.user",
+      "uid": "[email protected]",
+      "type": "User",
+      "type_id": 1
+    }
+  },
+  "auth_protocol": "Kerberos",
+  "auth_protocol_id": 2,
+  "auth_factors": [
+    {
+      "factor_type": "Password",
+      "factor_type_id": 1
+    }
+  ],
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "987654321098"
+    },
+    "provider": "AWS",
+    "region": "eu-west-1"
+  },
+  "dst_endpoint": {
+    "hostname": "prod-dc01.internal.corp",
+    "ip": "10.0.1.5",
+    "port": 88,
+    "svc_name": "Active Directory"
+  },
+  "is_cleartext": false,
+  "is_mfa": false,
+  "is_new_logon": false,
+  "is_remote": true,
+  "logon_type": "Network",
+  "logon_type_id": 3,
+  "message": "Failed login attempt - account locked after 5 failed attempts 
from Tor exit node",
+  "metadata": {
+    "product": {
+      "name": "Windows Security",
+      "vendor_name": "Microsoft",
+      "version": "10.0"
+    },
+    "version": "1.6.0"
+  },
+  "service": {
+    "name": "Windows Logon",
+    "uid": "NTLM"
+  },
+  "severity": "High",
+  "severity_id": 4,
+  "src_endpoint": {
+    "ip": "185.220.101.42",
+    "location": {
+      "city": "Frankfurt",
+      "country": "Germany",
+      "lat": 50.1109,
+      "long": 8.6821
+    },
+    "isp": "Tor Exit Node",
+    "autonomous_system": {
+      "name": "Zwiebelfreunde e.V.",
+      "number": 208294
+    }
+  },
+  "status": "Failure",
+  "status_id": 2,
+  "status_detail": "Account locked out due to excessive failed login attempts",
+  "time": 1769485000000,
+  "time_dt": "2026-01-27T03:36:40.000Z",
+  "type_name": "Authentication: Logon",
+  "type_uid": 300201,
+  "user": {
+    "name": "suspicious.user",
+    "uid": "sususer",
+    "type": "User",
+    "type_id": 1
+  }
+}
diff --git a/ocsf/test/detection-finding.json b/ocsf/test/detection-finding.json
new file mode 100644
index 0000000..6cfc791
--- /dev/null
+++ b/ocsf/test/detection-finding.json
@@ -0,0 +1,155 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Detection Finding",
+  "class_uid": 2004,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "123456789012"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "eu-west-1"
+  },
+  "confidence": "High",
+  "confidence_id": 3,
+  "count": 1,
+  "device": {
+    "hostname": "ip-10-0-1-45.eu-west-1.compute.internal",
+    "ip": "10.0.1.45",
+    "instance_uid": "i-0abc123def456789",
+    "os": {
+      "name": "Amazon Linux",
+      "version": "2023"
+    },
+    "type": "Server",
+    "type_id": 1
+  },
+  "evidences": [
+    {
+      "api": {
+        "operation": "GetSecretValue",
+        "service": {
+          "name": "secretsmanager.amazonaws.com"
+        }
+      },
+      "data": {
+        "resource_role": "TARGET"
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "DigitalOcean LLC",
+          "number": 14061
+        },
+        "ip": "167.99.45.123",
+        "isp": "DigitalOcean",
+        "isp_org": "DigitalOcean LLC",
+        "location": {
+          "city": "Amsterdam",
+          "country": "Netherlands",
+          "lat": 52.3740,
+          "long": 4.8897
+        }
+      }
+    }
+  ],
+  "finding_info": {
+    "analytic": {
+      "type": "Machine Learning",
+      "type_id": 3,
+      "uid": "ml-anomaly-detector-v2"
+    },
+    "created_time": 1769420000000,
+    "created_time_dt": "2026-01-26T09:33:20.000Z",
+    "desc": "An API call to retrieve secrets was made from an unusual external 
IP address 167.99.45.123 that has not been seen in the account before.",
+    "first_seen_time": 1769419500000,
+    "first_seen_time_dt": "2026-01-26T09:25:00.000Z",
+    "last_seen_time": 1769420000000,
+    "last_seen_time_dt": "2026-01-26T09:33:20.000Z",
+    "modified_time": 1769420100000,
+    "modified_time_dt": "2026-01-26T09:35:00.000Z",
+    "product": {
+      "uid": "ml-anomaly-detector-v2"
+    },
+    "title": "Unusual API call to Secrets Manager from external IP",
+    "types": [
+      "Threats",
+      "UnauthorizedAccess:IAMUser/AnomalousBehavior"
+    ],
+    "uid": 
"arn:aws:guardduty:eu-west-1:123456789012:detector/abc123/finding/def456789",
+    "uid_alt": "def456789"
+  },
+  "is_alert": true,
+  "malware": [
+    {
+      "name": "Suspicious.Credential.Exfiltration",
+      "uid": "MALWARE-2026-0142",
+      "classification_ids": [3, 7]
+    }
+  ],
+  "message": "Unusual API call to Secrets Manager detected from suspicious 
external IP",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "AnomalyDetection"
+      },
+      "name": "GuardDuty",
+      "uid": "arn:aws:securityhub:eu-west-1::productv2/aws/guardduty",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "datetime"
+    ],
+    "uid": "finding-metadata-uuid-12345",
+    "version": "1.6.0"
+  },
+  "remediation": {
+    "desc": "Immediately rotate the compromised secrets and review IAM 
policies. Investigate the source IP for any additional malicious activity.",
+    "references": [
+      
"https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-iam.html";,
+      
"https://docs.aws.amazon.com/secretsmanager/latest/userguide/security-incident-response.html";
+    ]
+  },
+  "resources": [
+    {
+      "cloud_partition": "aws",
+      "owner": {
+        "account": {
+          "type": "AWS Account",
+          "type_id": 10,
+          "uid": "123456789012"
+        }
+      },
+      "region": "eu-west-1",
+      "type": "AWS::SecretsManager::Secret",
+      "uid": 
"arn:aws:secretsmanager:eu-west-1:123456789012:secret:prod/database/credentials-AbCdE"
+    }
+  ],
+  "risk_level": "High",
+  "risk_level_id": 3,
+  "risk_score": 85,
+  "severity": "High",
+  "severity_id": 4,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769420100000,
+  "time_dt": "2026-01-26T09:35:00.000Z",
+  "type_name": "Detection Finding: Create",
+  "type_uid": 200401,
+  "vendor_attributes": {
+    "severity": "High",
+    "severity_id": 4
+  }
+}
diff --git a/ocsf/test/detection-finding_1.json 
b/ocsf/test/detection-finding_1.json
new file mode 100644
index 0000000..a1b2bf8
--- /dev/null
+++ b/ocsf/test/detection-finding_1.json
@@ -0,0 +1,152 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Detection Finding",
+  "class_uid": 2004,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "987654321098"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "us-east-1"
+  },
+  "confidence": "High",
+  "confidence_id": 3,
+  "count": 47,
+  "device": {
+    "hostname": "prod-web-server-01.ec2.internal",
+    "ip": "10.0.2.156",
+    "instance_uid": "i-0def789abc123456",
+    "os": {
+      "name": "Ubuntu",
+      "version": "22.04 LTS"
+    },
+    "type": "Server",
+    "type_id": 1
+  },
+  "evidences": [
+    {
+      "api": {
+        "operation": "RunInstances",
+        "service": {
+          "name": "ec2.amazonaws.com"
+        }
+      },
+      "data": {
+        "resource_role": "ACTOR"
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "CHINANET-BACKBONE",
+          "number": 4134
+        },
+        "ip": "218.92.0.123",
+        "isp": "China Telecom",
+        "isp_org": "Chinanet",
+        "location": {
+          "city": "Shanghai",
+          "country": "China",
+          "lat": 31.2222,
+          "long": 121.4581
+        }
+      }
+    }
+  ],
+  "finding_info": {
+    "analytic": {
+      "type": "Rule",
+      "type_id": 1,
+      "uid": "guardduty-unauthorized-access-rule"
+    },
+    "created_time": 1769450000000,
+    "created_time_dt": "2026-01-26T17:53:20.000Z",
+    "desc": "API calls to launch EC2 instances were detected from an IP 
address in China (218.92.0.123) that has never been used in this account. 47 
instances were requested in a crypto-mining configuration.",
+    "first_seen_time": 1769448000000,
+    "first_seen_time_dt": "2026-01-26T17:20:00.000Z",
+    "last_seen_time": 1769450000000,
+    "last_seen_time_dt": "2026-01-26T17:53:20.000Z",
+    "modified_time": 1769450100000,
+    "modified_time_dt": "2026-01-26T17:55:00.000Z",
+    "product": {
+      "uid": "guardduty-threat-detector"
+    },
+    "title": "Cryptocurrency Mining EC2 Instance Launch from Unusual Location",
+    "types": [
+      "TTPs",
+      "Impact:EC2/BitcoinDomainRequest.Reputation",
+      "UnauthorizedAccess:EC2/TorClient"
+    ],
+    "uid": 
"arn:aws:guardduty:us-east-1:987654321098:detector/xyz789/finding/crypto-001",
+    "uid_alt": "crypto-mining-001"
+  },
+  "is_alert": true,
+  "malware": [
+    {
+      "name": "CoinMiner.Generic",
+      "uid": "MALWARE-CRYPTO-2026-001",
+      "classification_ids": [8, 11]
+    }
+  ],
+  "message": "Suspected crypto-mining EC2 instances launched from unauthorized 
foreign IP",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "ThreatDetection"
+      },
+      "name": "GuardDuty",
+      "uid": "arn:aws:securityhub:us-east-1::product/aws/guardduty",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "datetime"
+    ],
+    "uid": "finding-crypto-mining-uuid",
+    "version": "1.6.0"
+  },
+  "remediation": {
+    "desc": "Immediately terminate the unauthorized EC2 instances. Revoke the 
compromised IAM credentials. Enable MFA on all IAM users. Review CloudTrail 
logs for additional unauthorized activity.",
+    "references": [
+      
"https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html";,
+      
"https://aws.amazon.com/blogs/security/how-to-detect-and-automatically-remediate-unintended-crypto-mining/";
+    ]
+  },
+  "resources": [
+    {
+      "cloud_partition": "aws",
+      "owner": {
+        "account": {
+          "type": "AWS Account",
+          "type_id": 10,
+          "uid": "987654321098"
+        }
+      },
+      "region": "us-east-1",
+      "type": "AWS::EC2::Instance",
+      "uid": "arn:aws:ec2:us-east-1:987654321098:instance/i-0unauthorized123"
+    }
+  ],
+  "risk_level": "Critical",
+  "risk_level_id": 4,
+  "risk_score": 95,
+  "severity": "Critical",
+  "severity_id": 5,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769450100000,
+  "time_dt": "2026-01-26T17:55:00.000Z",
+  "type_name": "Detection Finding: Create",
+  "type_uid": 200401
+}
diff --git a/ocsf/test/detection-finding_2.json 
b/ocsf/test/detection-finding_2.json
new file mode 100644
index 0000000..3898bf1
--- /dev/null
+++ b/ocsf/test/detection-finding_2.json
@@ -0,0 +1,169 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Detection Finding",
+  "class_uid": 2004,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "111222333444"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "us-west-2"
+  },
+  "confidence": "Medium",
+  "confidence_id": 2,
+  "count": 1523,
+  "device": {
+    "hostname": "api-gateway-prod.internal",
+    "ip": "10.1.5.89",
+    "instance_uid": "i-0gateway789def123",
+    "os": {
+      "name": "Amazon Linux",
+      "version": "2023"
+    },
+    "type": "Server",
+    "type_id": 1
+  },
+  "evidences": [
+    {
+      "api": {
+        "operation": "Invoke",
+        "service": {
+          "name": "execute-api.amazonaws.com"
+        }
+      },
+      "data": {
+        "resource_role": "TARGET"
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "OVH SAS",
+          "number": 16276
+        },
+        "ip": "51.195.45.67",
+        "isp": "OVH",
+        "isp_org": "OVH Hosting",
+        "location": {
+          "city": "Paris",
+          "country": "France",
+          "lat": 48.8566,
+          "long": 2.3522
+        }
+      }
+    },
+    {
+      "api": {
+        "operation": "Invoke",
+        "service": {
+          "name": "execute-api.amazonaws.com"
+        }
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "Hetzner Online GmbH",
+          "number": 24940
+        },
+        "ip": "95.216.123.89",
+        "isp": "Hetzner",
+        "isp_org": "Hetzner Online GmbH",
+        "location": {
+          "city": "Helsinki",
+          "country": "Finland",
+          "lat": 60.1699,
+          "long": 24.9384
+        }
+      }
+    }
+  ],
+  "finding_info": {
+    "analytic": {
+      "type": "Statistical",
+      "type_id": 2,
+      "uid": "waf-rate-limit-detector"
+    },
+    "created_time": 1769455000000,
+    "created_time_dt": "2026-01-26T19:16:40.000Z",
+    "desc": "A distributed brute-force attack targeting the /api/v1/auth/login 
endpoint was detected. Over 1500 failed authentication attempts from multiple 
IP addresses across Europe in the last 15 minutes. Attack pattern suggests 
credential stuffing using leaked database.",
+    "first_seen_time": 1769454100000,
+    "first_seen_time_dt": "2026-01-26T19:01:40.000Z",
+    "last_seen_time": 1769455000000,
+    "last_seen_time_dt": "2026-01-26T19:16:40.000Z",
+    "modified_time": 1769455100000,
+    "modified_time_dt": "2026-01-26T19:18:20.000Z",
+    "product": {
+      "uid": "aws-waf-security-automations"
+    },
+    "title": "Distributed Brute-Force Attack on Authentication API",
+    "types": [
+      "TTPs",
+      "Credential Access",
+      "Brute Force",
+      "T1110.004"
+    ],
+    "uid": "arn:aws:securityhub:us-west-2:111222333444:finding/bruteforce-002",
+    "uid_alt": "brute-force-auth-002"
+  },
+  "is_alert": true,
+  "message": "Distributed brute-force credential stuffing attack detected on 
authentication API",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "WebApplicationFirewall"
+      },
+      "name": "AWS WAF",
+      "uid": "arn:aws:securityhub:us-west-2::product/aws/waf",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "datetime"
+    ],
+    "uid": "finding-bruteforce-uuid",
+    "version": "1.6.0"
+  },
+  "remediation": {
+    "desc": "Enable account lockout after 5 failed attempts. Implement CAPTCHA 
on login. Add the attacking IP ranges to WAF blocklist. Enable enhanced 
logging. Consider implementing adaptive MFA.",
+    "references": [
+      "https://owasp.org/www-community/attacks/Credential_stuffing";,
+      
"https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html";
+    ]
+  },
+  "resources": [
+    {
+      "cloud_partition": "aws",
+      "owner": {
+        "account": {
+          "type": "AWS Account",
+          "type_id": 10,
+          "uid": "111222333444"
+        }
+      },
+      "region": "us-west-2",
+      "type": "AWS::ApiGateway::RestApi",
+      "uid": 
"arn:aws:execute-api:us-west-2:111222333444:abc123def/prod/POST/api/v1/auth/login"
+    }
+  ],
+  "risk_level": "High",
+  "risk_level_id": 3,
+  "risk_score": 78,
+  "severity": "High",
+  "severity_id": 4,
+  "status": "In Progress",
+  "status_id": 2,
+  "time": 1769455100000,
+  "time_dt": "2026-01-26T19:18:20.000Z",
+  "type_name": "Detection Finding: Create",
+  "type_uid": 200401
+}
diff --git a/ocsf/test/detection-finding_3.json 
b/ocsf/test/detection-finding_3.json
new file mode 100644
index 0000000..17416ca
--- /dev/null
+++ b/ocsf/test/detection-finding_3.json
@@ -0,0 +1,185 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Detection Finding",
+  "class_uid": 2004,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "555666777888"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "eu-central-1"
+  },
+  "confidence": "High",
+  "confidence_id": 3,
+  "count": 1,
+  "device": {
+    "hostname": "bastion-host-prod.eu-central-1.compute.internal",
+    "ip": "10.2.1.10",
+    "instance_uid": "i-0bastion456abc789",
+    "os": {
+      "name": "Amazon Linux",
+      "version": "2023"
+    },
+    "type": "Server",
+    "type_id": 1
+  },
+  "evidences": [
+    {
+      "api": {
+        "operation": "CreateUser",
+        "service": {
+          "name": "iam.amazonaws.com"
+        }
+      },
+      "data": {
+        "resource_role": "ACTOR"
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "M247 Ltd",
+          "number": 9009
+        },
+        "ip": "89.187.185.42",
+        "isp": "M247",
+        "isp_org": "M247 Ltd",
+        "location": {
+          "city": "Bucharest",
+          "country": "Romania",
+          "lat": 44.4268,
+          "long": 26.1025
+        }
+      }
+    },
+    {
+      "api": {
+        "operation": "AttachUserPolicy",
+        "service": {
+          "name": "iam.amazonaws.com"
+        }
+      },
+      "data": {
+        "policy_arn": "arn:aws:iam::aws:policy/AdministratorAccess"
+      },
+      "src_endpoint": {
+        "ip": "89.187.185.42"
+      }
+    },
+    {
+      "api": {
+        "operation": "CreateAccessKey",
+        "service": {
+          "name": "iam.amazonaws.com"
+        }
+      },
+      "src_endpoint": {
+        "ip": "89.187.185.42"
+      }
+    }
+  ],
+  "finding_info": {
+    "analytic": {
+      "type": "Machine Learning",
+      "type_id": 3,
+      "uid": "guardduty-iam-anomaly-detector"
+    },
+    "created_time": 1769460000000,
+    "created_time_dt": "2026-01-26T20:40:00.000Z",
+    "desc": "A new IAM user 'backup-admin-temp' was created and immediately 
granted AdministratorAccess policy. An access key was generated within 30 
seconds. Activity originated from an IP in Romania using a known VPN provider. 
This pattern is consistent with privilege escalation after initial compromise.",
+    "first_seen_time": 1769459900000,
+    "first_seen_time_dt": "2026-01-26T20:38:20.000Z",
+    "last_seen_time": 1769459930000,
+    "last_seen_time_dt": "2026-01-26T20:38:50.000Z",
+    "modified_time": 1769460000000,
+    "modified_time_dt": "2026-01-26T20:40:00.000Z",
+    "product": {
+      "uid": "guardduty-iam-threat-intel"
+    },
+    "title": "IAM Privilege Escalation - Backdoor Admin User Created",
+    "types": [
+      "TTPs",
+      "Persistence",
+      "Create Account",
+      "T1136.003",
+      "Privilege Escalation"
+    ],
+    "uid": 
"arn:aws:guardduty:eu-central-1:555666777888:detector/def456/finding/privesc-003",
+    "uid_alt": "iam-privesc-003"
+  },
+  "is_alert": true,
+  "malware": [
+    {
+      "name": "Backdoor.IAMPersistence",
+      "uid": "THREAT-IAM-2026-003",
+      "classification_ids": [1, 5]
+    }
+  ],
+  "message": "Suspected privilege escalation: new admin IAM user created with 
immediate access key generation",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "IAMThreatDetection"
+      },
+      "name": "GuardDuty",
+      "uid": "arn:aws:securityhub:eu-central-1::product/aws/guardduty",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "datetime"
+    ],
+    "uid": "finding-privesc-uuid",
+    "version": "1.6.0"
+  },
+  "remediation": {
+    "desc": "IMMEDIATELY: 1) Delete the unauthorized IAM user 
'backup-admin-temp'. 2) Deactivate all access keys for the user. 3) Review 
CloudTrail for the source of the compromised credentials. 4) Rotate all admin 
access keys. 5) Enable IAM Access Analyzer. 6) Implement SCP to prevent IAM 
user creation.",
+    "references": [
+      
"https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-iam.html";,
+      "https://attack.mitre.org/techniques/T1136/003/";
+    ]
+  },
+  "resources": [
+    {
+      "cloud_partition": "aws",
+      "owner": {
+        "account": {
+          "type": "AWS Account",
+          "type_id": 10,
+          "uid": "555666777888"
+        }
+      },
+      "region": "global",
+      "type": "AWS::IAM::User",
+      "uid": "arn:aws:iam::555666777888:user/backup-admin-temp"
+    },
+    {
+      "cloud_partition": "aws",
+      "region": "global",
+      "type": "AWS::IAM::AccessKey",
+      "uid": "AKIA5UNAUTHORIZED123"
+    }
+  ],
+  "risk_level": "Critical",
+  "risk_level_id": 4,
+  "risk_score": 99,
+  "severity": "Critical",
+  "severity_id": 5,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769460000000,
+  "time_dt": "2026-01-26T20:40:00.000Z",
+  "type_name": "Detection Finding: Create",
+  "type_uid": 200401
+}
diff --git a/ocsf/test/detection-finding_4.json 
b/ocsf/test/detection-finding_4.json
new file mode 100644
index 0000000..34565b5
--- /dev/null
+++ b/ocsf/test/detection-finding_4.json
@@ -0,0 +1,162 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Detection Finding",
+  "class_uid": 2004,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "999888777666"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "ap-southeast-1"
+  },
+  "confidence": "High",
+  "confidence_id": 3,
+  "count": 3,
+  "device": {
+    "hostname": "db-primary-prod.ap-southeast-1.rds.amazonaws.com",
+    "ip": "10.3.8.201",
+    "instance_uid": "db-instance-prod-mysql-01",
+    "os": {
+      "name": "MySQL",
+      "version": "8.0.35"
+    },
+    "type": "Database",
+    "type_id": 6
+  },
+  "evidences": [
+    {
+      "api": {
+        "operation": "SELECT",
+        "service": {
+          "name": "rds.amazonaws.com"
+        }
+      },
+      "data": {
+        "query": "SELECT * FROM users WHERE 1=1 OR ''=''; SELECT * FROM 
credit_cards; --",
+        "resource_role": "TARGET"
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "Linode LLC",
+          "number": 63949
+        },
+        "ip": "172.105.78.234",
+        "isp": "Linode",
+        "isp_org": "Akamai Technologies",
+        "location": {
+          "city": "Singapore",
+          "country": "Singapore",
+          "lat": 1.3521,
+          "long": 103.8198
+        }
+      }
+    }
+  ],
+  "finding_info": {
+    "analytic": {
+      "type": "Signature",
+      "type_id": 1,
+      "uid": "rds-sql-injection-detector"
+    },
+    "created_time": 1769465000000,
+    "created_time_dt": "2026-01-26T22:03:20.000Z",
+    "desc": "SQL injection attack detected on production MySQL database. 
Malicious queries attempting to extract data from 'users' and 'credit_cards' 
tables. Attack bypassed application layer and directly targeted database. 3 
successful data exfiltration attempts detected totaling approximately 50,000 
records.",
+    "first_seen_time": 1769464000000,
+    "first_seen_time_dt": "2026-01-26T21:46:40.000Z",
+    "last_seen_time": 1769465000000,
+    "last_seen_time_dt": "2026-01-26T22:03:20.000Z",
+    "modified_time": 1769465100000,
+    "modified_time_dt": "2026-01-26T22:05:00.000Z",
+    "product": {
+      "uid": "rds-advanced-security"
+    },
+    "title": "SQL Injection Data Exfiltration - Credit Card Data Breach",
+    "types": [
+      "TTPs",
+      "Collection",
+      "Data from Information Repositories",
+      "T1213",
+      "SQL Injection"
+    ],
+    "uid": "arn:aws:securityhub:ap-southeast-1:999888777666:finding/sqli-004",
+    "uid_alt": "sql-injection-004"
+  },
+  "is_alert": true,
+  "malware": [
+    {
+      "name": "SQLi.DataExfiltration",
+      "uid": "ATTACK-SQLI-2026-004",
+      "classification_ids": [3, 9]
+    }
+  ],
+  "message": "Critical SQL injection attack with confirmed data exfiltration 
of credit card information",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "DatabaseActivityStreams"
+      },
+      "name": "Amazon RDS",
+      "uid": "arn:aws:securityhub:ap-southeast-1::product/aws/rds",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "datetime"
+    ],
+    "uid": "finding-sqli-uuid",
+    "version": "1.6.0"
+  },
+  "remediation": {
+    "desc": "CRITICAL BREACH RESPONSE: 1) Immediately isolate the database 
from public access. 2) Block attacking IP at network level. 3) Initiate 
incident response - this is a reportable data breach. 4) Engage 
legal/compliance for PCI-DSS notification requirements. 5) Preserve all logs 
for forensics. 6) Patch application SQL injection vulnerability. 7) Rotate all 
database credentials.",
+    "references": [
+      "https://owasp.org/www-community/attacks/SQL_Injection";,
+      
"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Auditing.html";,
+      "https://www.pcisecuritystandards.org/document_library/";
+    ]
+  },
+  "resources": [
+    {
+      "cloud_partition": "aws",
+      "owner": {
+        "account": {
+          "type": "AWS Account",
+          "type_id": 10,
+          "uid": "999888777666"
+        }
+      },
+      "region": "ap-southeast-1",
+      "type": "AWS::RDS::DBInstance",
+      "uid": 
"arn:aws:rds:ap-southeast-1:999888777666:db:db-instance-prod-mysql-01"
+    },
+    {
+      "cloud_partition": "aws",
+      "region": "ap-southeast-1",
+      "type": "Database::Table",
+      "uid": "production_db.credit_cards"
+    }
+  ],
+  "risk_level": "Critical",
+  "risk_level_id": 4,
+  "risk_score": 100,
+  "severity": "Critical",
+  "severity_id": 5,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769465100000,
+  "time_dt": "2026-01-26T22:05:00.000Z",
+  "type_name": "Detection Finding: Create",
+  "type_uid": 200401
+}
diff --git a/ocsf/test/detection-finding_5.json 
b/ocsf/test/detection-finding_5.json
new file mode 100644
index 0000000..02898c9
--- /dev/null
+++ b/ocsf/test/detection-finding_5.json
@@ -0,0 +1,167 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Detection Finding",
+  "class_uid": 2004,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "444333222111"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "us-east-2"
+  },
+  "confidence": "High",
+  "confidence_id": 3,
+  "count": 1,
+  "device": {
+    "hostname": "lambda-execution-env",
+    "ip": "10.5.2.78",
+    "instance_uid": "lambda-arn-exec-12345",
+    "os": {
+      "name": "Amazon Linux",
+      "version": "2"
+    },
+    "type": "Serverless",
+    "type_id": 8
+  },
+  "evidences": [
+    {
+      "api": {
+        "operation": "GetObject",
+        "service": {
+          "name": "s3.amazonaws.com"
+        }
+      },
+      "data": {
+        "bucket": "company-sensitive-data-prod",
+        "key": "exports/customer-pii-2026.csv.gz",
+        "bytes_transferred": 2147483648,
+        "resource_role": "TARGET"
+      },
+      "dst_endpoint": {
+        "ip": "185.199.108.153",
+        "port": 443,
+        "domain": "github.com"
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "Amazon.com Inc.",
+          "number": 16509
+        },
+        "ip": "10.5.2.78",
+        "location": {
+          "city": "Columbus",
+          "country": "United States",
+          "lat": 39.9612,
+          "long": -82.9988
+        }
+      }
+    }
+  ],
+  "finding_info": {
+    "analytic": {
+      "type": "Behavioral",
+      "type_id": 4,
+      "uid": "macie-data-exfil-detector"
+    },
+    "created_time": 1769470000000,
+    "created_time_dt": "2026-01-26T23:26:40.000Z",
+    "desc": "A Lambda function 'data-export-handler' downloaded 2GB of 
sensitive PII data from S3 bucket 'company-sensitive-data-prod' and initiated 
an outbound connection to github.com. This behavior is anomalous - the function 
typically processes <10MB and has never connected to external endpoints. 
Possible supply chain attack via compromised dependency.",
+    "first_seen_time": 1769469500000,
+    "first_seen_time_dt": "2026-01-26T23:18:20.000Z",
+    "last_seen_time": 1769470000000,
+    "last_seen_time_dt": "2026-01-26T23:26:40.000Z",
+    "modified_time": 1769470100000,
+    "modified_time_dt": "2026-01-26T23:28:20.000Z",
+    "product": {
+      "uid": "amazon-macie-detector"
+    },
+    "title": "Lambda Function Data Exfiltration to External Domain",
+    "types": [
+      "TTPs",
+      "Exfiltration",
+      "Exfiltration Over Web Service",
+      "T1567",
+      "Supply Chain Compromise"
+    ],
+    "uid": "arn:aws:securityhub:us-east-2:444333222111:finding/exfil-005",
+    "uid_alt": "lambda-exfil-005"
+  },
+  "is_alert": true,
+  "malware": [
+    {
+      "name": "Trojan.SupplyChain.NPM",
+      "uid": "MALWARE-SUPPLY-2026-005",
+      "classification_ids": [2, 6, 11]
+    }
+  ],
+  "message": "Lambda function exfiltrating sensitive PII data to external 
GitHub endpoint - possible supply chain attack",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "SensitiveDataDiscovery"
+      },
+      "name": "Amazon Macie",
+      "uid": "arn:aws:securityhub:us-east-2::product/aws/macie",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "datetime"
+    ],
+    "uid": "finding-exfil-uuid",
+    "version": "1.6.0"
+  },
+  "remediation": {
+    "desc": "CRITICAL: 1) Immediately disable the Lambda function. 2) Revoke 
all IAM roles associated with the function. 3) Block outbound traffic to 
github.com at VPC level. 4) Scan all Lambda dependencies for malicious 
packages. 5) Check npm/pip package versions against known compromised versions. 
6) Audit all recent code deployments. 7) Notify affected customers per 
GDPR/CCPA requirements.",
+    "references": [
+      "https://docs.aws.amazon.com/macie/latest/user/findings-types.html";,
+      "https://attack.mitre.org/techniques/T1567/";,
+      "https://www.cisa.gov/supply-chain-compromise";
+    ]
+  },
+  "resources": [
+    {
+      "cloud_partition": "aws",
+      "owner": {
+        "account": {
+          "type": "AWS Account",
+          "type_id": 10,
+          "uid": "444333222111"
+        }
+      },
+      "region": "us-east-2",
+      "type": "AWS::Lambda::Function",
+      "uid": 
"arn:aws:lambda:us-east-2:444333222111:function:data-export-handler"
+    },
+    {
+      "cloud_partition": "aws",
+      "region": "us-east-2",
+      "type": "AWS::S3::Bucket",
+      "uid": "arn:aws:s3:::company-sensitive-data-prod"
+    }
+  ],
+  "risk_level": "Critical",
+  "risk_level_id": 4,
+  "risk_score": 98,
+  "severity": "Critical",
+  "severity_id": 5,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769470100000,
+  "time_dt": "2026-01-26T23:28:20.000Z",
+  "type_name": "Detection Finding: Create",
+  "type_uid": 200401
+}
diff --git a/ocsf/test/detection-finding_6.json 
b/ocsf/test/detection-finding_6.json
new file mode 100644
index 0000000..1330587
--- /dev/null
+++ b/ocsf/test/detection-finding_6.json
@@ -0,0 +1,161 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Detection Finding",
+  "class_uid": 2004,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "777888999000"
+    },
+    "cloud_partition": "aws",
+    "provider": "AWS",
+    "region": "eu-west-2"
+  },
+  "confidence": "Medium",
+  "confidence_id": 2,
+  "count": 15,
+  "device": {
+    "hostname": "eks-worker-node-prod-01",
+    "ip": "10.8.4.55",
+    "instance_uid": "i-0eks789worker123",
+    "os": {
+      "name": "Bottlerocket",
+      "version": "1.19.0"
+    },
+    "type": "Container Host",
+    "type_id": 9
+  },
+  "evidences": [
+    {
+      "api": {
+        "operation": "exec",
+        "service": {
+          "name": "kubernetes.io"
+        }
+      },
+      "data": {
+        "namespace": "production",
+        "pod": "webapp-frontend-7d8f9c6b5-x2k9m",
+        "container": "nginx",
+        "command": "/bin/sh -c 'curl 
http://169.254.169.254/latest/meta-data/iam/security-credentials/'",
+        "resource_role": "ACTOR"
+      },
+      "src_endpoint": {
+        "autonomous_system": {
+          "name": "Amazon.com Inc.",
+          "number": 16509
+        },
+        "ip": "10.8.4.55",
+        "location": {
+          "city": "London",
+          "country": "United Kingdom",
+          "lat": 51.5074,
+          "long": -0.1278
+        }
+      }
+    }
+  ],
+  "finding_info": {
+    "analytic": {
+      "type": "Rule",
+      "type_id": 1,
+      "uid": "eks-imds-access-detector"
+    },
+    "created_time": 1769475000000,
+    "created_time_dt": "2026-01-27T00:50:00.000Z",
+    "desc": "Multiple attempts to access EC2 Instance Metadata Service (IMDS) 
from a container in the production namespace. The container 'nginx' in pod 
'webapp-frontend' is attempting to retrieve IAM credentials via the metadata 
endpoint. This is a known SSRF attack vector used for credential theft in 
containerized environments.",
+    "first_seen_time": 1769473000000,
+    "first_seen_time_dt": "2026-01-27T00:16:40.000Z",
+    "last_seen_time": 1769475000000,
+    "last_seen_time_dt": "2026-01-27T00:50:00.000Z",
+    "modified_time": 1769475100000,
+    "modified_time_dt": "2026-01-27T00:51:40.000Z",
+    "product": {
+      "uid": "amazon-guardduty-eks"
+    },
+    "title": "Container IMDS Credential Theft Attempt via SSRF",
+    "types": [
+      "TTPs",
+      "Credential Access",
+      "Unsecured Credentials",
+      "T1552.005",
+      "Server-Side Request Forgery"
+    ],
+    "uid": 
"arn:aws:guardduty:eu-west-2:777888999000:detector/eks123/finding/imds-006",
+    "uid_alt": "container-imds-006"
+  },
+  "is_alert": true,
+  "message": "Container attempting to steal IAM credentials via IMDS endpoint 
- SSRF attack detected",
+  "metadata": {
+    "extensions": [
+      {
+        "name": "aws",
+        "uid": "998",
+        "version": "1.0.0"
+      },
+      {
+        "name": "kubernetes",
+        "uid": "1001",
+        "version": "1.0.0"
+      }
+    ],
+    "product": {
+      "feature": {
+        "name": "EKSProtection"
+      },
+      "name": "GuardDuty",
+      "uid": "arn:aws:securityhub:eu-west-2::product/aws/guardduty",
+      "vendor_name": "AWS"
+    },
+    "profiles": [
+      "cloud",
+      "container",
+      "datetime"
+    ],
+    "uid": "finding-imds-uuid",
+    "version": "1.6.0"
+  },
+  "remediation": {
+    "desc": "1) Immediately terminate the compromised pod. 2) Enable IMDSv2 
with hop limit of 1 on all EC2 instances. 3) Implement network policies to 
block IMDS access from pods. 4) Review container image for vulnerabilities. 5) 
Scan for web application SSRF vulnerabilities. 6) Enable EKS Pod Identity 
instead of node-level IAM roles.",
+    "references": [
+      
"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html";,
+      "https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html";,
+      "https://attack.mitre.org/techniques/T1552/005/";
+    ]
+  },
+  "resources": [
+    {
+      "cloud_partition": "aws",
+      "owner": {
+        "account": {
+          "type": "AWS Account",
+          "type_id": 10,
+          "uid": "777888999000"
+        }
+      },
+      "region": "eu-west-2",
+      "type": "AWS::EKS::Cluster",
+      "uid": "arn:aws:eks:eu-west-2:777888999000:cluster/prod-cluster"
+    },
+    {
+      "region": "eu-west-2",
+      "type": "Kubernetes::Pod",
+      "uid": "production/webapp-frontend-7d8f9c6b5-x2k9m"
+    }
+  ],
+  "risk_level": "High",
+  "risk_level_id": 3,
+  "risk_score": 82,
+  "severity": "High",
+  "severity_id": 4,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769475100000,
+  "time_dt": "2026-01-27T00:51:40.000Z",
+  "type_name": "Detection Finding: Create",
+  "type_uid": 200401
+}
diff --git a/ocsf/test/file-activity_1.json b/ocsf/test/file-activity_1.json
new file mode 100644
index 0000000..8ff44a1
--- /dev/null
+++ b/ocsf/test/file-activity_1.json
@@ -0,0 +1,78 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "System Activity",
+  "category_uid": 1,
+  "class_name": "File Activity",
+  "class_uid": 1001,
+  "actor": {
+    "process": {
+      "cmd_line": "powershell.exe -ExecutionPolicy Bypass -NoProfile 
-EncodedCommand JABjAGwA...",
+      "file": {
+        "name": "powershell.exe",
+        "path": 
"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
+        "type": "Regular File",
+        "type_id": 1
+      },
+      "name": "powershell.exe",
+      "pid": 4532,
+      "uid": "proc-ps-4532"
+    },
+    "user": {
+      "name": "SYSTEM",
+      "uid": "S-1-5-18",
+      "type": "System",
+      "type_id": 2
+    }
+  },
+  "device": {
+    "hostname": "DESKTOP-FINANCE01",
+    "ip": "10.2.5.101",
+    "os": {
+      "name": "Windows",
+      "version": "11 Pro 22H2"
+    },
+    "type": "Desktop",
+    "type_id": 2
+  },
+  "file": {
+    "name": "mimikatz.exe",
+    "path": "C:\\Users\\Public\\Downloads\\mimikatz.exe",
+    "size": 1247232,
+    "type": "Regular File",
+    "type_id": 1,
+    "hashes": [
+      {
+        "algorithm": "SHA-256",
+        "algorithm_id": 3,
+        "value": 
"e930b05fd7c0e1c8d6f4d6aa0c4c9a8e7b3c2d1f0e9a8b7c6d5e4f3a2b1c0d9e"
+      },
+      {
+        "algorithm": "MD5",
+        "algorithm_id": 1,
+        "value": "a1b2c3d4e5f6789012345678"
+      }
+    ],
+    "signature": {
+      "state": "Unsigned",
+      "state_id": 2
+    }
+  },
+  "message": "Known credential dumping tool (Mimikatz) dropped to disk by 
PowerShell with encoded command",
+  "metadata": {
+    "product": {
+      "name": "CrowdStrike Falcon",
+      "vendor_name": "CrowdStrike",
+      "version": "6.45"
+    },
+    "version": "1.6.0"
+  },
+  "severity": "Critical",
+  "severity_id": 5,
+  "status": "Success",
+  "status_id": 1,
+  "time": 1769515000000,
+  "time_dt": "2026-01-27T11:56:40.000Z",
+  "type_name": "File Activity: Create",
+  "type_uid": 100101
+}
diff --git a/ocsf/test/network-activity_1.json 
b/ocsf/test/network-activity_1.json
new file mode 100644
index 0000000..7c568fe
--- /dev/null
+++ b/ocsf/test/network-activity_1.json
@@ -0,0 +1,78 @@
+{
+  "activity_id": 6,
+  "activity_name": "Traffic",
+  "category_name": "Network Activity",
+  "category_uid": 4,
+  "class_name": "Network Activity",
+  "class_uid": 4001,
+  "action": "Blocked",
+  "action_id": 2,
+  "app_name": "SSH",
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "111222333444"
+    },
+    "provider": "AWS",
+    "region": "us-west-2"
+  },
+  "connection_info": {
+    "boundary": "External",
+    "boundary_id": 3,
+    "direction": "Inbound",
+    "direction_id": 1,
+    "protocol_name": "TCP",
+    "protocol_num": 6
+  },
+  "count": 2547,
+  "dst_endpoint": {
+    "hostname": "bastion-prod-01.internal",
+    "ip": "10.0.1.50",
+    "port": 22,
+    "svc_name": "SSH"
+  },
+  "duration": 3600000,
+  "end_time": 1769505000000,
+  "end_time_dt": "2026-01-27T09:10:00.000Z",
+  "message": "SSH brute force attack blocked - 2547 connection attempts from 
known malicious IP",
+  "metadata": {
+    "product": {
+      "name": "AWS Network Firewall",
+      "vendor_name": "AWS",
+      "version": "1.0"
+    },
+    "version": "1.6.0"
+  },
+  "severity": "High",
+  "severity_id": 4,
+  "src_endpoint": {
+    "ip": "45.155.205.233",
+    "port": 54321,
+    "location": {
+      "city": "Moscow",
+      "country": "Russia",
+      "lat": 55.7558,
+      "long": 37.6173
+    },
+    "isp": "SELECTEL",
+    "autonomous_system": {
+      "name": "Selectel Ltd.",
+      "number": 49505
+    }
+  },
+  "start_time": 1769501400000,
+  "start_time_dt": "2026-01-27T08:10:00.000Z",
+  "status": "Success",
+  "status_id": 1,
+  "time": 1769505000000,
+  "time_dt": "2026-01-27T09:10:00.000Z",
+  "traffic": {
+    "bytes_in": 512000,
+    "bytes_out": 0,
+    "packets_in": 2547,
+    "packets_out": 0
+  },
+  "type_name": "Network Activity: Traffic",
+  "type_uid": 400106
+}
diff --git a/ocsf/test/network-activity_2.json 
b/ocsf/test/network-activity_2.json
new file mode 100644
index 0000000..1dac015
--- /dev/null
+++ b/ocsf/test/network-activity_2.json
@@ -0,0 +1,89 @@
+{
+  "activity_id": 6,
+  "activity_name": "Traffic",
+  "category_name": "Network Activity",
+  "category_uid": 4,
+  "class_name": "Network Activity",
+  "class_uid": 4001,
+  "action": "Allowed",
+  "action_id": 1,
+  "app_name": "HTTPS",
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "999888777666"
+    },
+    "provider": "AWS",
+    "region": "ap-northeast-1"
+  },
+  "connection_info": {
+    "boundary": "External",
+    "boundary_id": 3,
+    "direction": "Outbound",
+    "direction_id": 2,
+    "protocol_name": "TCP",
+    "protocol_num": 6
+  },
+  "count": 1,
+  "dst_endpoint": {
+    "hostname": "c2-server.malicious-domain.xyz",
+    "ip": "194.26.135.89",
+    "port": 443,
+    "svc_name": "HTTPS",
+    "location": {
+      "city": "Riga",
+      "country": "Latvia",
+      "lat": 56.9496,
+      "long": 24.1052
+    },
+    "autonomous_system": {
+      "name": "ITLDC-LV",
+      "number": 52048
+    }
+  },
+  "duration": 45000,
+  "end_time": 1769510000000,
+  "end_time_dt": "2026-01-27T10:33:20.000Z",
+  "message": "Suspicious outbound connection to known C2 server - potential 
malware beaconing detected",
+  "metadata": {
+    "product": {
+      "name": "AWS VPC Flow Logs",
+      "vendor_name": "AWS",
+      "version": "1.0"
+    },
+    "version": "1.6.0"
+  },
+  "severity": "Critical",
+  "severity_id": 5,
+  "src_endpoint": {
+    "hostname": "workstation-finance-042",
+    "ip": "10.5.12.42",
+    "port": 49876,
+    "instance_uid": "i-0finance042abc123"
+  },
+  "start_time": 1769509955000,
+  "start_time_dt": "2026-01-27T10:32:35.000Z",
+  "status": "Success",
+  "status_id": 1,
+  "time": 1769510000000,
+  "time_dt": "2026-01-27T10:33:20.000Z",
+  "traffic": {
+    "bytes_in": 1024,
+    "bytes_out": 524288,
+    "packets_in": 8,
+    "packets_out": 512
+  },
+  "tls": {
+    "cipher": "TLS_AES_256_GCM_SHA384",
+    "certificate": {
+      "issuer": "CN=Let's Encrypt Authority X3",
+      "subject": "CN=c2-server.malicious-domain.xyz",
+      "created_time": 1769000000000,
+      "expiration_time": 1776862800000
+    },
+    "version": "1.3"
+  },
+  "type_name": "Network Activity: Traffic",
+  "type_uid": 400106
+}
diff --git a/ocsf/test/process-activity_1.json 
b/ocsf/test/process-activity_1.json
new file mode 100644
index 0000000..178ae19
--- /dev/null
+++ b/ocsf/test/process-activity_1.json
@@ -0,0 +1,85 @@
+{
+  "activity_id": 1,
+  "activity_name": "Launch",
+  "category_name": "System Activity",
+  "category_uid": 1,
+  "class_name": "Process Activity",
+  "class_uid": 1007,
+  "actor": {
+    "process": {
+      "cmd_line": "cmd.exe /c whoami /all && net user && net localgroup 
administrators",
+      "file": {
+        "name": "cmd.exe",
+        "path": "C:\\Windows\\System32\\cmd.exe",
+        "type": "Regular File",
+        "type_id": 1
+      },
+      "name": "cmd.exe",
+      "pid": 6789,
+      "uid": "proc-cmd-6789",
+      "parent_process": {
+        "name": "excel.exe",
+        "pid": 5432,
+        "cmd_line": "\"C:\\Program Files\\Microsoft 
Office\\root\\Office16\\EXCEL.EXE\" /e 
\"C:\\Users\\victim\\Downloads\\Invoice_2026.xlsm\""
+      }
+    },
+    "user": {
+      "name": "jsmith",
+      "uid": "S-1-5-21-123456789-987654321-111222333-1001",
+      "type": "User",
+      "type_id": 1
+    }
+  },
+  "device": {
+    "hostname": "LAPTOP-SALES042",
+    "ip": "10.3.8.42",
+    "os": {
+      "name": "Windows",
+      "version": "10 Enterprise 21H2"
+    },
+    "type": "Laptop",
+    "type_id": 3
+  },
+  "process": {
+    "cmd_line": "cmd.exe /c whoami /all && net user && net localgroup 
administrators",
+    "created_time": 1769520000000,
+    "file": {
+      "name": "cmd.exe",
+      "path": "C:\\Windows\\System32\\cmd.exe"
+    },
+    "name": "cmd.exe",
+    "pid": 6789,
+    "tid": 6790,
+    "uid": "proc-cmd-6789",
+    "user": {
+      "name": "jsmith",
+      "type": "User",
+      "type_id": 1
+    },
+    "parent_process": {
+      "name": "excel.exe",
+      "pid": 5432,
+      "file": {
+        "name": "excel.exe",
+        "path": "C:\\Program Files\\Microsoft 
Office\\root\\Office16\\EXCEL.EXE"
+      }
+    }
+  },
+  "message": "Suspicious reconnaissance commands executed by cmd.exe spawned 
from Excel macro - likely malicious document",
+  "metadata": {
+    "product": {
+      "name": "Microsoft Defender for Endpoint",
+      "vendor_name": "Microsoft",
+      "version": "10.0"
+    },
+    "version": "1.6.0"
+  },
+  "severity": "High",
+  "severity_id": 4,
+  "status": "Success",
+  "status_id": 1,
+  "time": 1769520000000,
+  "time_dt": "2026-01-27T13:20:00.000Z",
+  "type_name": "Process Activity: Launch",
+  "type_uid": 100701
+}
diff --git a/ocsf/test/vulnerability-finding_1.json 
b/ocsf/test/vulnerability-finding_1.json
new file mode 100644
index 0000000..60c13e1
--- /dev/null
+++ b/ocsf/test/vulnerability-finding_1.json
@@ -0,0 +1,105 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Vulnerability Finding",
+  "class_uid": 2002,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "123456789012"
+    },
+    "provider": "AWS",
+    "region": "us-east-1"
+  },
+  "confidence": "High",
+  "confidence_id": 3,
+  "count": 1,
+  "finding_info": {
+    "created_time": 1769490000000,
+    "created_time_dt": "2026-01-27T05:00:00.000Z",
+    "desc": "Critical remote code execution vulnerability in Apache Log4j 
library (Log4Shell). Allows unauthenticated remote code execution via specially 
crafted log messages. CVSS Score: 10.0",
+    "title": "CVE-2021-44228 - Log4Shell RCE Vulnerability",
+    "types": ["Vulnerability", "Remote Code Execution"],
+    "uid": "vuln-log4j-001"
+  },
+  "message": "Critical Log4Shell vulnerability detected in production Java 
application",
+  "metadata": {
+    "product": {
+      "name": "Amazon Inspector",
+      "vendor_name": "AWS",
+      "version": "2.0"
+    },
+    "version": "1.6.0"
+  },
+  "resource": {
+    "type": "AWS::EC2::Instance",
+    "uid": "arn:aws:ec2:us-east-1:123456789012:instance/i-0abc123def456789",
+    "name": "prod-app-server-01"
+  },
+  "resources": [
+    {
+      "type": "AWS::EC2::Instance",
+      "uid": "arn:aws:ec2:us-east-1:123456789012:instance/i-0abc123def456789",
+      "name": "prod-app-server-01",
+      "region": "us-east-1"
+    }
+  ],
+  "risk_level": "Critical",
+  "risk_level_id": 4,
+  "risk_score": 100,
+  "severity": "Critical",
+  "severity_id": 5,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769490000000,
+  "time_dt": "2026-01-27T05:00:00.000Z",
+  "type_name": "Vulnerability Finding: Create",
+  "type_uid": 200201,
+  "vulnerabilities": [
+    {
+      "cve": {
+        "uid": "CVE-2021-44228",
+        "created_time": 1639094400000,
+        "modified_time": 1639180800000
+      },
+      "cvss": [
+        {
+          "base_score": 10.0,
+          "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
+          "version": "3.1"
+        }
+      ],
+      "cwe": {
+        "uid": "CWE-502",
+        "caption": "Deserialization of Untrusted Data"
+      },
+      "desc": "Apache Log4j2 2.0-beta9 through 2.15.0 allows remote code 
execution via JNDI lookups",
+      "first_seen_time": 1769400000000,
+      "is_exploit_available": true,
+      "is_fix_available": true,
+      "kb_articles": [
+        {
+          "title": "Apache Log4j Security Advisory",
+          "src_url": "https://logging.apache.org/log4j/2.x/security.html";
+        }
+      ],
+      "packages": [
+        {
+          "name": "log4j-core",
+          "version": "2.14.1",
+          "type": "Maven"
+        }
+      ],
+      "references": [
+        "https://nvd.nist.gov/vuln/detail/CVE-2021-44228";,
+        "https://www.cisa.gov/known-exploited-vulnerabilities-catalog";
+      ],
+      "severity": "Critical",
+      "title": "Log4Shell Remote Code Execution",
+      "vendor_name": "Apache"
+    }
+  ]
+}
diff --git a/ocsf/test/vulnerability-finding_2.json 
b/ocsf/test/vulnerability-finding_2.json
new file mode 100644
index 0000000..f50d8de
--- /dev/null
+++ b/ocsf/test/vulnerability-finding_2.json
@@ -0,0 +1,104 @@
+{
+  "activity_id": 1,
+  "activity_name": "Create",
+  "category_name": "Findings",
+  "category_uid": 2,
+  "class_name": "Vulnerability Finding",
+  "class_uid": 2002,
+  "cloud": {
+    "account": {
+      "type": "AWS Account",
+      "type_id": 10,
+      "uid": "555666777888"
+    },
+    "provider": "AWS",
+    "region": "eu-central-1"
+  },
+  "confidence": "High",
+  "confidence_id": 3,
+  "count": 1,
+  "finding_info": {
+    "created_time": 1769495000000,
+    "created_time_dt": "2026-01-27T06:23:20.000Z",
+    "desc": "Container image contains OpenSSL version 3.0.2 vulnerable to 
buffer overflow. Attackers can crash the application or execute arbitrary code 
via malformed certificate.",
+    "title": "CVE-2022-3602 - OpenSSL X.509 Certificate Buffer Overflow",
+    "types": ["Vulnerability", "Buffer Overflow"],
+    "uid": "vuln-openssl-002"
+  },
+  "message": "High severity OpenSSL vulnerability in production container 
image",
+  "metadata": {
+    "product": {
+      "name": "Amazon ECR Image Scanning",
+      "vendor_name": "AWS",
+      "version": "1.0"
+    },
+    "version": "1.6.0"
+  },
+  "resource": {
+    "type": "AWS::ECR::Repository",
+    "uid": "arn:aws:ecr:eu-central-1:555666777888:repository/webapp-frontend",
+    "name": "webapp-frontend"
+  },
+  "resources": [
+    {
+      "type": "AWS::ECR::Repository",
+      "uid": 
"arn:aws:ecr:eu-central-1:555666777888:repository/webapp-frontend",
+      "name": "webapp-frontend:v2.3.1",
+      "region": "eu-central-1"
+    }
+  ],
+  "risk_level": "High",
+  "risk_level_id": 3,
+  "risk_score": 78,
+  "severity": "High",
+  "severity_id": 4,
+  "status": "New",
+  "status_id": 1,
+  "time": 1769495000000,
+  "time_dt": "2026-01-27T06:23:20.000Z",
+  "type_name": "Vulnerability Finding: Create",
+  "type_uid": 200201,
+  "vulnerabilities": [
+    {
+      "cve": {
+        "uid": "CVE-2022-3602",
+        "created_time": 1667260800000,
+        "modified_time": 1667347200000
+      },
+      "cvss": [
+        {
+          "base_score": 7.5,
+          "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+          "version": "3.1"
+        }
+      ],
+      "cwe": {
+        "uid": "CWE-120",
+        "caption": "Buffer Copy without Checking Size of Input"
+      },
+      "desc": "A buffer overrun can be triggered in X.509 certificate 
verification",
+      "first_seen_time": 1769400000000,
+      "is_exploit_available": false,
+      "is_fix_available": true,
+      "kb_articles": [
+        {
+          "title": "OpenSSL Security Advisory",
+          "src_url": "https://www.openssl.org/news/secadv/20221101.txt";
+        }
+      ],
+      "packages": [
+        {
+          "name": "openssl",
+          "version": "3.0.2",
+          "type": "Alpine"
+        }
+      ],
+      "references": [
+        "https://nvd.nist.gov/vuln/detail/CVE-2022-3602";
+      ],
+      "severity": "High",
+      "title": "OpenSSL X.509 Buffer Overflow",
+      "vendor_name": "OpenSSL"
+    }
+  ]
+}


Reply via email to