diff --git a/stix_shifter_modules/gcp_chronicle/README.md b/stix_shifter_modules/gcp_chronicle/README.md new file mode 100644 index 000000000..ac24b50d6 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/README.md @@ -0,0 +1,841 @@ +# GCP Chronicle Connector + +**Table of Contents** + +- [GCP Chronicle API Endpoints](#gcp-chronicle-api-endpoints) +- [Pattern expression with STIX attributes](#pattern-expression-with-stix-attributes) +- [Pattern expression with CUSTOM attributes](#pattern-expression-with-custom-attributes) +- [STIX Execute Query](#stix-execute-query) +- [Pattern expression with STIX attributes - Multiple Observation](#stix-multiple-observation) +- [Limitations](#limitations) +- [Observations](#observations) +- [References](#references) + +### GCP Chronicle API Endpoints + + | Connector Method | GCP Chronicle API Endpoint | Method | + |-----------------|------| ------| + | Ping Endpoint | https://< server >/v2/detect/rules | GET | + | Query Endpoint | 1) https://< server >/v2/detect/rules/
2) https://< server >/v2/detect/rules/{ruleId}:runRetrohunt | POST | + | Status Endpoint | https://< server >/v2/detect/rules/{ruleId}/retrohunts/{retrohuntId} | GET| + | Results Endpoint | https://< server >/v2/detect/rules/{ruleId}/detections | GET | + | Delete Endpoint | https://< server >/v2/detect/rules/{ruleId} | DELETE | + +### Format for calling stix-shifter from the command line +``` +$ stix-shifter translate " "" "" +``` +### Pattern expression with STIX attributes + +### Single Observation + +#### STIX Translate query to get the events associated with the process name and the file hash +```shell +translate gcp_chronicle query '{}' "[process:name = 'powershell.exe' AND file:hashes.'SHA-1' = 'ded8fd7f36417f66eb6ada10e0c0d7c0022986e9'] START t'2022-06-05T16:43:26.000Z' STOP t'2022-06-10T16:43:26.003Z'" +``` +#### STIX Translate query - output +```json +{ + "queries": [ + { + "ruleText": "rule cp4s_gcp_udi_rule_1659699862 { meta: author = \"ibm cp4s user\" description = \"Create event rule that should generate detections\" events: ($udm.src.file.sha1 = \"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\" nocase or $udm.target.file.sha1 = \"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\" nocase or $udm.src.process.file.sha1 = \"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\" nocase or $udm.target.process.file.sha1 = \"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\" nocase or $udm.principal.process.file.sha1 = \"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\" nocase or $udm.about.file.sha1 = \"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\" nocase) and ($udm.src.process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.target.process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.principal.process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.target.process.parent_process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.principal.process.parent_process.file.full_path = /(?s)powershell\\.exe/ nocase) condition: $udm}", + "startTime": "2022-06-05T16:43:26.000Z", + "endTime": "2022-06-10T16:43:26.003Z" + } + ] +} + +``` + +#### STIX Transmit query + +```shell +transmit +gcp_chronicle +"{\"host\":\"xx.xx.xx\",\"selfSignedCert\": \"-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n\"}" +"{\"auth\":{ \"client_email\": \"xyz.com\"}}" +query +" { \"ruleText\": \"rule cp4s_gcp_udi_rule_1659699862 { meta: author = \\"ibm cp4s user\\" description = \\"Create event rule that should generate detections\\" events: ($udm.src.file.sha1 = \\"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\\" nocase or $udm.target.file.sha1 = \\"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\\" nocase or $udm.src.process.file.sha1 = \\"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\\" nocase or $udm.target.process.file.sha1 = \\"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\\" nocase or $udm.principal.process.file.sha1 = \\"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\\" nocase or $udm.about.file.sha1 = \\"ded8fd7f36417f66eb6ada10e0c0d7c0022986e9\\" nocase) and ($udm.src.process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.target.process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.principal.process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.target.process.parent_process.file.full_path = /(?s)powershell\\.exe/ nocase or $udm.principal.process.parent_process.file.full_path = /(?s)powershell\\.exe/ nocase) condition: $udm}\", \"startTime\": \"2022-06-05T16:43:26.000Z\", \"endTime\": \"2022-06-10T16:43:26.003Z\" }" +``` + +#### STIX Transmit query - output +```json +{ + "success": true, + "search_id": "oh_cda1ea4f-87d8-4f21-b80c-9eb3c5e8bf6d:ru_2fec7add-f727-41e1-a839-9de344d2a98d" +} +``` +#### STIX Transmit status + +```shell +transmit +gcp_chronicle +"{\"host\":\"xx.xx.xx\",\"selfSignedCert\": \"-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n\"}" +"{\"auth\":{ \"client_email\": \"xyz.com\"}}" +status +"oh_cda1ea4f-87d8-4f21-b80c-9eb3c5e8bf6d:ru_2fec7add-f727-41e1-a839-9de344d2a98d" +``` + +#### STIX Transmit status - output +```json +{ + "success": true, + "status": "COMPLETED", + "progress": 100 +} +``` +#### STIX Transmit results + +```shell +transmit +gcp_chronicle +"{\"host\":\"xx.xx.xx\",\"selfSignedCert\": \"-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n\"}" +"{\"auth\":{ \"client_email\": \"xyz.com\"}}" +results +"oh_cda1ea4f-87d8-4f21-b80c-9eb3c5e8bf6d:ru_2fec7add-f727-41e1-a839-9de344d2a98d" 0 1 +``` + +#### STIX Transmit results - output +```json +{ + "success": true, + "data": [ + { + "event": { + "metadata": { + "productLogId": "4f22ab5dc4be96566ee3c9adb3b77280dc08bfdb", + "eventTimestamp": "2022-06-06T11:27:47.531824Z", + "eventType": "PROCESS_LAUNCH", + "vendorName": "Microsoft Defender ATP", + "productName": "AdvancedHunting-DeviceProcessEvents", + "productEventType": "ProcessCreated", + "ingestedTimestamp": "2022-06-06T14:57:54.955806Z" + }, + "principal": { + "hostname": "alert-windows", + "user": { + "userid": "defenderadm", + "windowsSid": "S-1-5-21-2421154212-3139753733-2135342675-1000" + }, + "process": { + "pid": "6772", + "file": { + "sha256": "bc866cfcdda37e24dc2634dc282c7a0e6f55209da17a8fa105b07414c0e7c527", + "md5": "911d039e71583a07320b32bde22f8e22", + "sha1": "ded8fd7f36417f66eb6ada10e0c0d7c0022986e9", + "size": "278528", + "fullPath": "c:\\windows\\system32\\cmd.exe" + }, + "commandLine": "\"cmd.exe\" ", + "parentProcess": { + "pid": "6720", + "file": { + "fullPath": "svchost.exe" + } + } + }, + "administrativeDomain": "alert-windows" + }, + "target": { + "process": { + "pid": "6856", + "file": { + "sha256": "de96a6e69944335375dc1ac238336066889d9ffc7d73628ef4fe1b1b160ab32c", + "md5": "7353f60b1739074eb17c5f4dddefe239", + "sha1": "6cbce4a295c163791b60fc23d285e6d84f28ee4c", + "size": "448000", + "fullPath": "powershell.exe" + }, + "commandLine": "powershell.exe -ExecutionPolicy Bypass -NoProfile -Command \"Add-Type 'using System; using System.Diagnostics; using System.Diagnostics.Tracing; namespace Sense { [EventData(Name = \\\"Onboarding\\\")]public struct Onboarding{public string Message { get; set; }} public class Trace {public static EventSourceOptions TelemetryCriticalOption = new EventSourceOptions(){Level = EventLevel.Informational, Keywords = (EventKeywords)0x0000200000000000, Tags = (EventTags)0x0200000}; public void WriteOnboardingMessage(string message){es.Write(\\\"OnboardingScript\\\", TelemetryCriticalOption, new Onboarding {Message = message});} private static readonly string[] telemetryTraits = { \\\"ETW_GROUP\\\", \\\"{5ECB0BAC-B930-47F5-A8A4-E8253529EDB7}\\\" }; private EventSource es = new EventSource(\\\"Microsoft.Windows.Sense.Client.Management\\\",EventSourceSettings.EtwSelfDescribingEventFormat,telemetryTraits);}}'; $logger = New-Object -TypeName Sense.Trace; $logger.WriteOnboardingMessage('Successfully onboarded machine to Microsoft Defender for Endpoint')\" " + }, + "file": { + "fullPath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + } + }, + "observer": { + "cloud": { + "project": { + "id": "b73e5ba8-34d5-495a-9901-06bdb84cf13e" + } + } + }, + "about": [ + { + "labels": [ + { + "key": "ReportId", + "value": "14720" + }, + { + "key": "InitiatingProcessTokenElevation", + "value": "TokenElevationTypeFull" + }, + { + "key": "InitiatingProcessIntegrityLevel", + "value": "High" + } + ] + } + ], + "securityResult": [{ + "category": "alert", + "summary": "ProcessCreated" + }] + }, + "detection": { + "ruleName": "cp4s_gcp_udi_rule_1659699862", + "urlBackToProduct": "https://hcllab.backstory.chronicle.security/ruleDetections?ruleId=ru_568c1be7-476b-494d-a1b3-b6d6b06e8988&selectedList=RuleDetectionsViewTimeline&selectedParentDetectionId=de_e38651d7-ea4e-7b39-c36e-0e42d590b981&selectedTimestamp=2022-06-06T11:27:47.531824Z&versionTimestamp=2022-08-05T12:53:43.162079Z", + "ruleId": "ru_568c1be7-476b-494d-a1b3-b6d6b06e8988", + "ruleVersion": "ru_568c1be7-476b-494d-a1b3-b6d6b06e8988@v_1659704023_162079000", + "alertState": "NOT_ALERTING", + "ruleType": "SINGLE_EVENT", + "ruleLabels": [ + { + "key": "author", + "value": "ibm cp4s user" + }, + { + "key": "description", + "value": "Create event rule that should generate detections" + } + ] + } + } + ], + "metadata": "1:abc" +} +``` + + +#### STIX Translate results + +```json +{ + "type": "bundle", + "id": "bundle--15d030d9-7696-476e-815a-fe381e369958", + "objects": [ + { + "type": "identity", + "id": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + "name": "gcp", + "identity_class": "events", + "created": "2022-07-22T13:22:50.336Z", + "modified": "2022-07-22T13:22:50.336Z" + }, + { + "id": "observed-data--6923575f-57fe-40cf-bb47-94b9b9baa804", + "type": "observed-data", + "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + "created": "2022-07-22T13:22:50.336Z", + "modified": "2022-07-22T13:22:50.336Z", + "objects": { + "0": { + "type": "x-oca-event", + "code": "4f22ab5dc4be96566ee3c9adb3b77280dc08bfdb", + "created": "2022-06-06T11:27:47.531824Z", + "action": "PROCESS_LAUNCH", + "provider": "Microsoft Defender ATP", + "agent": "AdvancedHunting-DeviceProcessEvents", + "outcome": "ProcessCreated", + "host_ref": "1", + "user_ref": "2", + "file_ref": "4", + "process_ref": "3", + "parent_process_ref": "6", + "cross_process_target_ref": "8" + }, + "1": { + "type": "x-oca-asset", + "hostname": "alert-windows" + }, + "2": { + "type": "user-account", + "user_id": "defenderadm", + "extensions": { + "windows-account-ext": { + "sid": "S-1-5-21-2421154212-3139753733-2135342675-1000" + } + } + }, + "3": { + "type": "process", + "creator_user_ref": "2", + "pid": 6772, + "binary_ref": "4", + "name": "cmd.exe", + "command_line": "\"cmd.exe\" ", + "parent_ref": "6" + }, + "4": { + "type": "file", + "hashes": { + "SHA-256": "bc866cfcdda37e24dc2634dc282c7a0e6f55209da17a8fa105b07414c0e7c527", + "MD5": "911d039e71583a07320b32bde22f8e22", + "SHA-1": "ded8fd7f36417f66eb6ada10e0c0d7c0022986e9" + }, + "size": 278528, + "name": "cmd.exe", + "parent_directory_ref": "5" + }, + "5": { + "type": "directory", + "path": "c:\\windows\\system32" + }, + "6": { + "type": "process", + "pid": 6720, + "name": "svchost.exe", + "binary_ref": "7" + }, + "7": { + "type": "file", + "name": "svchost.exe" + }, + "8": { + "type": "process", + "pid": 6856, + "binary_ref": "9", + "name": "powershell.exe", + "command_line": "powershell.exe -ExecutionPolicy Bypass -NoProfile -Command \"Add-Type 'using System; using System.Diagnostics; using System.Diagnostics.Tracing; namespace Sense { [EventData(Name = \\\"Onboarding\\\")]public struct Onboarding{public string Message { get; set; }} public class Trace {public static EventSourceOptions TelemetryCriticalOption = new EventSourceOptions(){Level = EventLevel.Informational, Keywords = (EventKeywords)0x0000200000000000, Tags = (EventTags)0x0200000}; public void WriteOnboardingMessage(string message){es.Write(\\\"OnboardingScript\\\", TelemetryCriticalOption, new Onboarding {Message = message});} private static readonly string[] telemetryTraits = { \\\"ETW_GROUP\\\", \\\"{5ECB0BAC-B930-47F5-A8A4-E8253529EDB7}\\\" }; private EventSource es = new EventSource(\\\"Microsoft.Windows.Sense.Client.Management\\\",EventSourceSettings.EtwSelfDescribingEventFormat,telemetryTraits);}}'; $logger = New-Object -TypeName Sense.Trace; $logger.WriteOnboardingMessage('Successfully onboarded machine to Microsoft Defender for Endpoint')\" " + }, + "9": { + "type": "file", + "hashes": { + "SHA-256": "de96a6e69944335375dc1ac238336066889d9ffc7d73628ef4fe1b1b160ab32c", + "MD5": "7353f60b1739074eb17c5f4dddefe239", + "SHA-1": "6cbce4a295c163791b60fc23d285e6d84f28ee4c" + }, + "size": 448000, + "name": "powershell.exe" + }, + "10": { + "type": "file", + "name": "powershell.exe", + "parent_directory_ref": "11" + }, + "11": { + "type": "directory", + "path": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0" + }, + "12": { + "type": "x-ibm-finding", + "name": "ProcessCreated", + "finding_type": "alert" + } + }, + "first_observed": "2022-06-06T11:27:47.531824Z", + "last_observed": "2022-06-06T11:27:47.531824Z", + "number_observed": 1 + } + ], + "spec_version": "2.0" +} +``` + + +### Pattern expression with CUSTOM attributes +### Single Observation + +#### STIX Translate query to find the threats in an email transaction +```shell +translate gcp_chronicle query '{}' "[x-ibm-finding:finding_type = 'threat' AND x-oca-event:action = 'EMAIL_TRANSACTION'] START t'2022-06-21T16:43:26.000Z' STOP t'2022-06-24T16:43:26.003Z'" +``` + +#### STIX Translate query - output + +```json +{ + "queries": [ + { + "ruleText": "rule cp4s_gcp_udi_rule_1659715234 { meta: author = \"ibm cp4s user\" description = \"Create event rule that should generate detections\" events: $udm.metadata.event_type = \"EMAIL_TRANSACTION\" and (any $udm.security_result.category = \"SOFTWARE_MALICIOUS\" or any $udm.security_result.category = \"SOFTWARE_PUA\" or any $udm.security_result.category = \"NETWORK_MALICIOUS\" or any $udm.security_result.category = \"MAIL_SPAM\" or any $udm.security_result.category = \"MAIL_PHISHING\" or any $udm.security_result.category = \"MAIL_SPOOFING\") condition: $udm}", + "startTime": "2022-06-21T16:43:26.000Z", + "endTime": "2022-06-24T16:43:26.003Z" + } + ] +} + +``` + + +#### STIX Transmit query + +```shell +transmit +gcp_chronicle +"{\"host\":\"xx.xx.xx\",\"selfSignedCert\": \"-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n\"}" +"{\"auth\":{ \"client_email\": \"xyz.com\"}}" +query +"{ \"ruleText\": \"rule cp4s_gcp_udi_rule_1659715234 { meta: author = \\"ibm cp4s user\\" description = \\"Create event rule that should generate detections\\" events: $udm.metadata.event_type = \\"EMAIL_TRANSACTION\\" and (any $udm.security_result.category = \\"SOFTWARE_MALICIOUS\\" or any $udm.security_result.category = \\"SOFTWARE_PUA\\" or any $udm.security_result.category = \\"NETWORK_MALICIOUS\\" or any $udm.security_result.category = \\"MAIL_SPAM\\" or any $udm.security_result.category = \\"MAIL_PHISHING\\" or any $udm.security_result.category = \\"MAIL_SPOOFING\\") condition: $udm}\", \"startTime\": \"2022-06-21T16:43:26.000Z\", \"endTime\": \"2022-06-24T16:43:26.003Z\" }" +``` + +#### STIX Transmit query - output + +```json +{ + "success": true, + "search_id": "oh_7111ae97-b1bc-4393-a305-ec88dd13fbb2:ru_d9341b46-4cea-4cbc-9890-5dabe1d2b62f" +} +``` +#### STIX Transmit status + +```shell +transmit +gcp_chronicle +"{\"host\":\"xx.xx.xx\",\"selfSignedCert\": \"-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n\"}" +"{\"auth\":{ \"client_email\": \"xyz.com\"}}" +status +"oh_7111ae97-b1bc-4393-a305-ec88dd13fbb2:ru_d9341b46-4cea-4cbc-9890-5dabe1d2b62f" +``` + +#### STIX Transmit status - output +```json +{ + "success": true, + "status": "COMPLETED", + "progress": 100 +} + +``` +#### STIX Transmit results + +```shell +transmit +gcp_chronicle +"{\"host\":\"xx.xx.xx\",\"selfSignedCert\": \"-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n\"}" +"{\"auth\":{ \"client_email\": \"xyz.com\"}}" +results +"oh_7111ae97-b1bc-4393-a305-ec88dd13fbb2:ru_d9341b46-4cea-4cbc-9890-5dabe1d2b62f" 0 1 +``` + +#### STIX Transmit results - output +```json +{ + "success": true, + "data": [ + { + "event": { + "metadata": { + "productLogId": "MYhRuvkA0N4NtUSOmiBIumilYcKRlBcy", + "eventTimestamp": "2022-06-22T11:19:20Z", + "eventType": "EMAIL_TRANSACTION", + "vendorName": "PROOFPOINT", + "productName": "TAP", + "productEventType": "messagesDelivered", + "ingestedTimestamp": "2022-06-22T11:43:29.036379Z" + }, + "additional": { + "phishScore": 0, + "spamScore": 0, + "headerFrom": "test user1 " + }, + "principal": { + "user": { + "emailAddresses": [ + "user1@iscgalaxy.com" + ] + }, + "ip": [ + "1.1.1.1" + ] + }, + "target": { + "user": { + "emailAddresses": [ + "user2@iscgalaxy.com" + ] + } + }, + "intermediary": [ + { + "user": { + "emailAddresses": [ + "testuser21@iscgalaxy.com", + "user2@iscgalaxy.com" + ] + } + } + ], + "about": [ + { + "file": { + "sha256": "b025e6114db79f3a891740c45ed264231ec77b07ab9e7ff9156b6d73eb35861e", + "md5": "ff357be90b834ed71da60df190124592", + "fullPath": "text.txt", + "mimeType": "text/plain" + } + } + ], + "securityResult": [ + { + "about": { + "url": "https://testurl.com" + }, + "category": "threat", + "categoryDetails": [ + "malware" + ], + "threatName": "url", + "action": [ + "ALLOW_WITH_MODIFICATION" + ], + "urlBackToProduct": "https://threatinsight.proofpoint.com/a7929edb-9295-4c75-8767-8a8ddf8a5807/threat/email/d00309e12e797021511111456056ff434c2b85c908d72b8c0dc138259182b9a0", + "threatId": "d00309e12e797021511111456056ff434c2b85c908d72b8c0dc138259182b9a0", + "threatStatus": "ACTIVE", + "detectionFields": [ + { + "key": "completelyRewritten", + "value": "True" + } + ] + } + ], + "network": { + "email": { + "from": "010001818b22f94b-c1c1b98b-16f5-40d0-a911-a7f562a5d1d1-000000@amazonses.com", + "to": [ + "user2@iscgalaxy.com" + ], + "mailId": "010001818b22f94b-c1c1b98b-16f5-40d0-a911-a7f562a5d1d1-000000@email.amazonses.com", + "subject": [ + "https://testurl.com" + ], + "isMultipart": false + } + } + }, + "detection": { + "ruleName": "cp4s_gcp_udi_rule_1659715234", + "urlBackToProduct": "https://hcllab.backstory.chronicle.security/ruleDetections?ruleId=ru_d9341b46-4cea-4cbc-9890-5dabe1d2b62f&selectedList=RuleDetectionsViewTimeline&selectedParentDetectionId=de_1022f4e7-70ea-db83-ac0f-ff550fad631d&selectedTimestamp=2022-06-22T11:19:20Z&versionTimestamp=2022-08-05T16:03:39.150767Z", + "ruleId": "ru_d9341b46-4cea-4cbc-9890-5dabe1d2b62f", + "ruleVersion": "ru_d9341b46-4cea-4cbc-9890-5dabe1d2b62f@v_1659715419_150767000", + "alertState": "NOT_ALERTING", + "ruleType": "SINGLE_EVENT", + "ruleLabels": [ + { + "key": "author", + "value": "ibm cp4s user" + }, + { + "key": "description", + "value": "Create event rule that should generate detections" + } + ] + } + } + ], + "metadata": "1:abc" +} +``` + + +#### STIX Translate results + +```json +{ + "type": "bundle", + "id": "bundle--d01a64a1-c2b1-4477-9e5f-7f47a031419e", + "objects": [ + { + "type": "identity", + "id": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + "name": "gcp", + "identity_class": "events", + "created": "2022-08-05T13:22:50.336Z", + "modified": "2022-08-05T13:22:50.336Z" + }, + { + "id": "observed-data--7425dc2d-a247-47c8-b4df-c5c508fe6ccd", + "type": "observed-data", + "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + "created": "2022-08-05T13:22:50.336Z", + "modified": "2022-08-05T13:22:50.336Z", + "objects": { + "0": { + "type": "x-oca-event", + "code": "MYhRuvkA0N4NtUSOmiBIumilYcKRlBcy", + "created": "2022-06-22T11:19:20Z", + "action": "EMAIL_TRANSACTION", + "provider": "PROOFPOINT", + "agent": "TAP", + "outcome": "messagesDelivered", + "ip_refs": [ + "2" + ], + "extensions": { + "x-gcp-chronicle-event": { + "email_message_ref": "8" + } + } + }, + "1": { + "type": "email-addr", + "value": "user1@iscgalaxy.com" + }, + "2": { + "type": "ipv4-addr", + "value": "1.1.1.1" + }, + "4": { + "type": "x-ibm-finding", + "src_ip_ref": "2", + "extensions": { + "x-gcp-chronicle-security-result": { + "url_ref": "9", + "threat_name": "url", + "actions_taken": [ + "ALLOW_WITH_MODIFICATION" + ], + "threat_id": "d00309e12e797021511111456056ff434c2b85c908d72b8c0dc138259182b9a0", + "threat_status": "ACTIVE" + } + }, + "finding_type": "threat" + }, + "6": { + "type": "email-addr", + "value": "user2@iscgalaxy.com" + }, + "7": { + "type": "file", + "hashes": { + "SHA-256": "b025e6114db79f3a891740c45ed264231ec77b07ab9e7ff9156b6d73eb35861e", + "MD5": "ff357be90b834ed71da60df190124592" + }, + "name": "text.txt", + "extensions": { + "x-gcp-chronicle-file": { + "mime_type": "text/plain" + } + } + }, + "8": { + "type": "email-message", + "extensions": { + "x-gcp-chronicle-email-message": { + "file_ref": "7" + } + }, + "from_ref": "10", + "to_refs": [ + "11" + ], + "subject": "https://testurl.com", + "is_multipart": false + }, + "9": { + "type": "url", + "value": "https://testurl.com" + }, + "10": { + "type": "email-addr", + "value": "010001818b22f94b-c1c1b98b-16f5-40d0-a911-a7f562a5d1d1-000000@amazonses.com" + }, + "11": { + "type": "email-addr", + "value": "user2@iscgalaxy.com" + } + }, + "first_observed": "2022-06-22T11:19:20Z", + "last_observed": "2022-06-22T11:19:20Z", + "number_observed": 1 + } + ], + "spec_version": "2.0" +} +``` + + +### STIX Execute query +```shell +execute +gcp_chronicle +gcp_chronicle +"{\"type\":\"identity\",\"id\":\"identity--f431f809-377b-45e0-aa1c-6a4751cae5ff\",\"name\":\"gcp_chronicle\",\"identity_class\":\"events\",\"created\":\"2022-08-05T13:22:50.336Z\",\"modified\":\"2022-08-05T13:22:50.336Z\"}" +"{\"host\":\"xx.xx.xx\",\"selfSignedCert\": \"-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n\"}" +"{\"auth\":{ \"client_email\": \"xyz.com\"}}" +"[ipv4-addr:value = '1.0.0.1' AND network-traffic:src_port = '52221'] START t'2022-06-06T00:00:00.000000Z' STOP t'2022-06-15T00:00:00.000000Z'" +``` +#### STIX Execute query - output +```json +{ + "type": "bundle", + "id": "bundle--c5711d18-3ba9-4356-ada6-45c6c6b30c9c", + "objects": [ + { + "type": "identity", + "id": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + "name": "chronicle", + "identity_class": "system", + "created": "2022-08-05T13:22:50.336Z", + "modified": "2022-08-05T13:22:50.336Z" + }, + { + "id": "observed-data--920eb15d-f959-4112-a9b6-cbf7195ad231", + "type": "observed-data", + "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", + "created": "2022-08-05T13:22:50.336Z", + "modified": "2022-08-05T13:22:50.336Z", + "objects": { + "0": { + "type": "x-oca-event", + "code": "10190", + "created": "2022-06-13T14:14:54.409074800Z", + "action": "NETWORK_CONNECTION", + "provider": "Microsoft", + "agent": "AdvancedHunting-DeviceNetworkEvents", + "outcome": "DeviceNetworkEvents", + "host_ref": "1", + "user_ref": "2", + "file_ref": "4", + "process_ref": "3", + "parent_process_ref": "6", + "ip_refs": [ + "8", + "11" + ], + "network_ref": "9", + "extensions": { + "x-gcp-chronicle-event": { + "target_hostname": "v20.events.data.microsoft.com" + } + } + }, + "1": { + "type": "x-oca-asset", + "hostname": "alert-windows", + "extensions": { + "x-gcp-chronicle-asset": { + "asset_id": "DeviceId:4f22ab5dc4be96566ee3c9adb3b77280dc08bfdb" + } + }, + "ip_refs": [ + "8" + ] + }, + "2": { + "type": "user-account", + "user_id": "system", + "extensions": { + "windows-account-ext": { + "sid": "S-1-5-18" + } + } + }, + "3": { + "type": "process", + "creator_user_ref": "2", + "pid": 2788, + "binary_ref": "4", + "name": "svchost.exe", + "command_line": "svchost.exe -k utcsvc -p", + "parent_ref": "6" + }, + "4": { + "type": "file", + "hashes": { + "SHA-256": "2b3efaca2e57e433e6950286f7a6fb46ed48411322a26d657e58f02f7d232224", + "MD5": "53a2c077e868af30525019e9d070eddd", + "SHA-1": "ed68d965d3572218fa5b17b54e7726df3b18dee3" + }, + "size": 56352, + "name": "svchost.exe", + "parent_directory_ref": "5" + }, + "5": { + "type": "directory", + "path": "c:\\windows\\system32" + }, + "6": { + "type": "process", + "pid": 756, + "name": "services.exe", + "binary_ref": "7" + }, + "7": { + "type": "file", + "name": "services.exe" + }, + "8": { + "type": "ipv4-addr", + "value": "1.0.0.1" + }, + "9": { + "type": "network-traffic", + "src_ref": "8", + "src_port": 52221, + "dst_ref": "11", + "dst_port": 443, + "protocols": [ + "tcp" + ], + "extensions": { + "x-gcp-chronicle-network": { + "direction": "OUTBOUND" + } + } + }, + "10": { + "type": "x-ibm-finding", + "src_ip_ref": "8", + "dst_ip_ref": "11", + "name": "ConnectionSuccess", + "extensions": { + "x-gcp-chronicle-security-result": { + "actions_taken": [ + "ALLOW" + ] + } + }, + "finding_type": "alert" + }, + "11": { + "type": "ipv4-addr", + "value": "1.0.0.2" + } + }, + "first_observed": "2022-06-13T14:14:54.409074800Z", + "last_observed": "2022-06-13T14:14:54.409074800Z", + "number_observed": 1 + } + ], + "spec_version": "2.0" +} + +``` + +### STIX Multiple Observation +```shell +translate gcp_chronicle query '{}' "([file:hashes.'SHA-1' = '6cbce4a295c163791b60fc23d285e6d84f28ee4c' OR file:size >10 ] OR [network-traffic:src_port IN (52221,443)] AND [process:command_line LIKE 'MsMpEng.exe']) START t'2022-06-01T00:00:00.030Z' STOP t'2022-07-05T00:00:00.030Z' OR [ipv4-addr:value != '1.1.1.2']" +``` +#### STIX Multiple observation - output +```json +{ + "queries": [ + { + "ruleText": "rule cp4s_gcp_udi_rule_1659763825 { meta: author = \"ibm cp4s user\" description = \"Create event rule that should generate detections\" events: (($udm.src.file.size > 10 or $udm.target.file.size > 10 or $udm.src.process.file.size > 10 or $udm.target.process.file.size > 10 or $udm.principal.process.file.size > 10 or $udm.about.file.size > 10) or ($udm.src.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.src.process.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.process.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.principal.process.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.about.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase)) or (($udm.src.port = 52221 or $udm.src.port = 443) or ($udm.principal.port = 52221 or $udm.principal.port = 443)) or ($udm.src.process.command_line = /(?s)MsMpEng\\.exe/ nocase or $udm.target.process.command_line = /(?s)MsMpEng\\.exe/ nocase or $udm.principal.process.command_line = /(?s)MsMpEng\\.exe/ nocase or $udm.target.process.parent_process.command_line = /(?s)MsMpEng\\.exe/ nocase or $udm.principal.process.parent_process.command_line = /(?s)MsMpEng\\.exe/ nocase) condition: $udm}", + "startTime": "2022-06-01T00:00:00.030Z", + "endTime": "2022-07-05T00:00:00.030Z" + }, + { + "ruleText": "rule cp4s_gcp_udi_rule_1659763825 { meta: author = \"ibm cp4s user\" description = \"Create event rule that should generate detections\" events: ( all $udm.src.ip != \"1.1.1.2\" nocase and all $udm.src.ip != \"\" ) or ( all $udm.target.ip != \"1.1.1.2\" nocase and all $udm.target.ip != \"\" ) or ( all $udm.principal.ip != \"1.1.1.2\" nocase and all $udm.principal.ip != \"\" ) condition: $udm}", + "startTime": "2022-08-06T05:25:25.414Z", + "endTime": "2022-08-06T05:30:25.414Z" + } + ] +} +``` +### Limitations + +- Delete method is not automatically available to all customers +- As per the chronicle documentation, after the rule has been saved, we cannot delete it from the Rules Editor or the Rules Dashboard in UI. It can be deleted only through Delete Rule API. +Reference: [Managing rules using the Rules Editor](#https://cloud.google.com/chronicle/docs/detection/manage-all-rules) + +### Observations +- The private_key value which is used for authentication should be passed in selfSignedCert field +- It is recommended to use LIKE operator for substring match and MATCHES operator for regular expression match. +- Supported values for the stix attribute x-ibm-finding:severity is 16,32,48,64,80,100. This has been mapped with chronicle severity value 'INFORMATIONAL','ERROR',LOW','MEDIUM','HIGH','CRITICAL' correspondingly. + +### References +- [Chronicle Detection Engine API](https://cloud.google.com/chronicle/docs/reference/detection-engine-api) +- [YARA-L 2.0 Language syntax](https://cloud.google.com/chronicle/docs/detection/yara-l-2-0-syntax) +- [UDM fields list](https://cloud.google.com/chronicle/docs/reference/udm-field-list) diff --git a/stix_shifter_modules/gcp_chronicle/__init__.py b/stix_shifter_modules/gcp_chronicle/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/stix_shifter_modules/gcp_chronicle/configuration/config.json b/stix_shifter_modules/gcp_chronicle/configuration/config.json new file mode 100644 index 000000000..4d7c3105b --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/configuration/config.json @@ -0,0 +1,27 @@ +{ + "connection": { + "type": { + "displayName": "Google Chronicle Security" + }, + "host": { + "type": "text", + "regex": "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$" + }, + "help": { + "type": "link", + "default": "data-sources.html" + }, + "selfSignedCert": { + "type": "password" + } + }, + "configuration": { + "auth": { + "type": "fields", + "client_email": { + "type": "password" + } + + } + } +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/configuration/lang_en.json b/stix_shifter_modules/gcp_chronicle/configuration/lang_en.json new file mode 100644 index 000000000..d5aea8872 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/configuration/lang_en.json @@ -0,0 +1,24 @@ +{ + "connection": { + "host": { + "label": "Management IP address or Hostname", + "description": "Specify the IP address or hostname of the data source so that IBM Cloud Pak for Security can communicate with it" + }, + "help": { + "label": "Need additional help?", + "description": "More details on the data source setting can be found in the specified link" + }, + "selfSignedCert": { + "label": "Private Key (Required)", + "description": "Private Key is a mandatory authentication parameter to communicate with the GCP Chronicle security datasource." + } + }, + "configuration": { + "auth": { + "client_email": { + "label": "Client Email", + "description": "Client Email used in authentication to make API calls" + } + } + } +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/entry_point.py b/stix_shifter_modules/gcp_chronicle/entry_point.py new file mode 100644 index 000000000..ab9d76202 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/entry_point.py @@ -0,0 +1,14 @@ +from stix_shifter_utils.utils.base_entry_point import BaseEntryPoint + + +class EntryPoint(BaseEntryPoint): + + def __init__(self, connection={}, configuration={}, options={}): + super().__init__(connection, configuration, options) + self.set_async(True) + if connection: + self.setup_transmission_simple(connection, configuration) + + self.setup_translation_simple(dialect_default='default') + + diff --git a/stix_shifter_modules/gcp_chronicle/requirements.txt b/stix_shifter_modules/gcp_chronicle/requirements.txt new file mode 100644 index 000000000..c82e57695 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/requirements.txt @@ -0,0 +1 @@ +google-api-python-client==2.52.0 diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/__init__.py b/stix_shifter_modules/gcp_chronicle/stix_translation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/json/config_map.json b/stix_shifter_modules/gcp_chronicle/stix_translation/json/config_map.json new file mode 100644 index 000000000..5c41566f4 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/json/config_map.json @@ -0,0 +1,266 @@ +{ + "int_supported_fields": [ + "src.port", + "principal.port", + "target.port", + "network.received_bytes", + "network.sent_bytes", + "network.dns.id", + "network.session_duration.seconds", + "network.dns.opcode", + "network.dns.response_code", + "network.dns.questions.class", + "network.dns.questions.type", + "network.dhcp.transaction_id", + "network.http.response_code", + "src.file.size", + "target.file.size", + "about.file.size", + "src.process.file.size", + "target.process.file.size", + "principal.process.file.size" + ], + "timestamp_supported_fields": [ + "src.file.last_modification_time.seconds", + "target.file.last_modification_time.seconds", + "src.process.file.last_modification_time.seconds", + "target.process.file.last_modification_time.seconds", + "principal.process.file.last_modification_time.seconds", + "about.file.last_modification_time.seconds", + "network.tls.server.certificate.not_before.seconds", + "network.tls.client.certificate.not_before.seconds", + "network.tls.server.certificate.not_after.seconds", + "network.tls.client.certificate.not_after.seconds", + "metadata.event_timestamp.seconds" + ], + "mac_supported_fields": [ + "src.mac", + "target.mac", + "principal.mac" + ], + "email_supported_fields": [ + "security_result.about.email", + "principal.user.email_addresses", + "src.user.email_addresses", + "target.user.email_addresses", + "network.email.from", + "network.email.to", + "network.email.cc", + "network.email.bcc" + ], + "enum_supported_fields": [ + "network.ip_protocol", + "network.application_protocol", + "network.dhcp.opcode", + "network.dhcp.type", + "src.user.account_type", + "target.user.account_type", + "principal.user.account_type", + "src.file.file_type", + "target.file.file_type", + "src.process.file.file_type", + "about.file.file_type", + "target.process.file.file_type", + "principal.process.file.file_type", + "metadata.event_type", + "src.resource.resource_type", + "target.resource.resource_type", + "principal.resource.resource_type", + "src.resource.attribute.cloud.environment", + "target.resource.attribute.cloud.environment", + "principal.resource.attribute.cloud.environment", + "security_result.action", + "security_result.severity", + "security_result.alert_state", + "security_result.category", + "security_result.threat_status", + "principal.asset.type", + "src.asset.platform_software.platform", + "target.asset.platform_software.platform", + "principal.asset.platform_software.platform", + "src.asset.attribute.cloud.environment", + "target.asset.attribute.cloud.environment", + "principal.asset.attribute.cloud.environment", + "network.direction" + ], + "enum_supported_values": { + "ip_protocol": [ + "UNKNOWN_IP_PROTOCOL", "EIGRP", "ESP", "ETHERIP", "GRE", "ICMP", "IGMP", + "IP6IN4", "PIM", "TCP", "UDP", "VRRP" + ], + "application_protocol": [ + "UNKNOWN_APPLICATION_PROTOCOL", "AFP", "APPC", "AMQP", "ATOM", "BEEP", "BITCOIN", + "BIT_TORRENT", "CFDP", "COAP", "DDS", "DEVICE_NET", "DHCP", "DNS", "E_DONKEY", "ENRP", + "FAST_TRACK", "FINGER", "FREENET", "FTAM", "GOPHER", "HL7", "H323", "HTTP", "HTTPS", + "IRCP", "KADEMLIA", "LDAP", "LPD", "MIME", "MODBUS", "MQTT", "NETCONF", "NFS", "NIS", + "NNTP", "NTCIP", "NTP", "OSCAR", "PNRP", "QUIC", "RDP", "RELP", "RIP", "RLOGIN", "RPC", + "RTMP", "RTP", "RTPS", "RTSP", "SAP", "SDP", "SIP", "SLP", "SMB", "SMTP", "SNTP", "SSH", + "SSMS", "STYX", "TCAP", "TDS", "TOR", "TSP", "VTP", "WHOIS", "WEB_DAV", "X400", "X500", + "XMPP" + ], + "opcode": ["UNKNOWN_OPCODE", "BOOTREQUEST", "BOOTREPLY"], + "network.dhcp.type": [ + "UNKNOWN_MESSAGE_TYPE", "DISCOVER", "OFFER", "REQUEST", "DECLINE", "ACK", "NAK", "RELEASE", + "INFORM", "WIN_DELETED", "WIN_EXPIRED" + ], + "account_type": [ + "ACCOUNT_TYPE_UNSPECIFIED", "DOMAIN_ACCOUNT_TYPE", "LOCAL_ACCOUNT_TYPE", + "CLOUD_ACCOUNT_TYPE", "SERVICE_ACCOUNT_TYPE", "DEFAULT_ACCOUNT_TYPE" + ], + "file_type": [ + "FILE_TYPE_UNSPECIFIED", "FILE_TYPE_PE_EXE", "FILE_TYPE_PE_DLL", "FILE_TYPE_MSI", + "FILE_TYPE_NE_EXE", "FILE_TYPE_NE_DLL", "FILE_TYPE_DOS_EXE", "FILE_TYPE_DOS_COM", + "FILE_TYPE_COFF", "FILE_TYPE_ELF", "FILE_TYPE_LINUX_KERNEL", "FILE_TYPE_RPM", + "FILE_TYPE_LINUX", "FILE_TYPE_MACH_O", "FILE_TYPE_JAVA_BYTECODE", "FILE_TYPE_DMG", + "FILE_TYPE_DEB", "FILE_TYPE_PKG", "FILE_TYPE_LNK", "FILE_TYPE_JPEG", "FILE_TYPE_TIFF", + "FILE_TYPE_GIF", "FILE_TYPE_PNG", "FILE_TYPE_BMP", "FILE_TYPE_GIMP", "FILE_TYPE_IN_DESIGN", + "FILE_TYPE_PSD", "FILE_TYPE_TARGA", "FILE_TYPE_XWD", "FILE_TYPE_DIB", "FILE_TYPE_JNG", + "FILE_TYPE_ICO", "FILE_TYPE_FPX", "FILE_TYPE_EPS", "FILE_TYPE_SVG", "FILE_TYPE_EMF", + "FILE_TYPE_WEBP", "FILE_TYPE_OGG", "FILE_TYPE_FLC", "FILE_TYPE_FLI", "FILE_TYPE_MP3", + "FILE_TYPE_FLAC", "FILE_TYPE_WAV", "FILE_TYPE_MIDI", "FILE_TYPE_AVI", "FILE_TYPE_MPEG", + "FILE_TYPE_QUICKTIME", "FILE_TYPE_ASF", "FILE_TYPE_DIVX", "FILE_TYPE_FLV", "FILE_TYPE_WMA", + "FILE_TYPE_WMV", "FILE_TYPE_RM", "FILE_TYPE_MOV", "FILE_TYPE_MP4", "FILE_TYPE_T3GP", + "FILE_TYPE_PDF", "FILE_TYPE_PS", "FILE_TYPE_DOC", "FILE_TYPE_DOCX", "FILE_TYPE_PPT", + "FILE_TYPE_PPTX", "FILE_TYPE_PPSX", "FILE_TYPE_XLS", "FILE_TYPE_XLSX", "FILE_TYPE_RTF", + "FILE_TYPE_ODP", "FILE_TYPE_ODS", "FILE_TYPE_ODT", "FILE_TYPE_HWP", "FILE_TYPE_GUL", + "FILE_TYPE_ODF", "FILE_TYPE_ODG", "FILE_TYPE_EBOOK", "FILE_TYPE_LATEX", "FILE_TYPE_TTF", + "FILE_TYPE_EOT", "FILE_TYPE_WOFF", "FILE_TYPE_CHM", "FILE_TYPE_ZIP", "FILE_TYPE_GZIP", + "FILE_TYPE_BZIP", "FILE_TYPE_RZIP", "FILE_TYPE_DZIP", "FILE_TYPE_SEVENZIP", "FILE_TYPE_CAB", + "FILE_TYPE_JAR", "FILE_TYPE_RAR", "FILE_TYPE_MSCOMPRESS", "FILE_TYPE_ACE", "FILE_TYPE_ARC", + "FILE_TYPE_ARJ", "FILE_TYPE_ASD", "FILE_TYPE_BLACKHOLE", "FILE_TYPE_KGB", "FILE_TYPE_ZLIB", + "FILE_TYPE_TAR", "FILE_TYPE_TEXT", "FILE_TYPE_SCRIPT", "FILE_TYPE_PHP", "FILE_TYPE_PYTHON", + "FILE_TYPE_PERL", "FILE_TYPE_RUBY", "FILE_TYPE_C", "FILE_TYPE_CPP", "FILE_TYPE_JAVA", + "FILE_TYPE_SHELLSCRIPT", "FILE_TYPE_PASCAL", "FILE_TYPE_AWK", "FILE_TYPE_DYALOG", + "FILE_TYPE_FORTRAN", "FILE_TYPE_JAVASCRIPT", "FILE_TYPE_POWERSHELL", "FILE_TYPE_VBA", + "FILE_TYPE_SYMBIAN", "FILE_TYPE_PALMOS", "FILE_TYPE_WINCE", "FILE_TYPE_ANDROID", + "FILE_TYPE_IPHONE", "FILE_TYPE_HTML", "FILE_TYPE_XML", "FILE_TYPE_SWF", "FILE_TYPE_FLA", + "FILE_TYPE_COOKIE", "FILE_TYPE_TORRENT", "FILE_TYPE_EMAIL_TYPE", "FILE_TYPE_OUTLOOK", + "FILE_TYPE_CAP", "FILE_TYPE_ISOIMAGE", "FILE_TYPE_APPLE", "FILE_TYPE_MACINTOSH", + "FILE_TYPE_APPLESINGLE", "FILE_TYPE_APPLEDOUBLE", "FILE_TYPE_MACINTOSH_HFS", + "FILE_TYPE_APPLE_PLIST", "FILE_TYPE_MACINTOSH_LIB", "FILE_TYPE_APPLESCRIPT", + "FILE_TYPE_APPLESCRIPT_COMPILED", "FILE_TYPE_CRX", "FILE_TYPE_XPI", "FILE_TYPE_ROM" + ], + "event_type": [ + "EVENTTYPE_UNSPECIFIED", "PROCESS_UNCATEGORIZED", "PROCESS_LAUNCH", "PROCESS_INJECTION", + "PROCESS_PRIVILEGE_ESCALATION", "PROCESS_TERMINATION", "PROCESS_OPEN", + "PROCESS_MODULE_LOAD", "REGISTRY_UNCATEGORIZED", "REGISTRY_CREATION", + "REGISTRY_MODIFICATION", "REGISTRY_DELETION", "SETTING_UNCATEGORIZED", "SETTING_CREATION", + "SETTING_MODIFICATION", "SETTING_DELETION", "MUTEX_UNCATEGORIZED", "MUTEX_CREATION", + "FILE_UNCATEGORIZED", "FILE_CREATION", "FILE_DELETION", "FILE_MODIFICATION", "FILE_READ", + "FILE_COPYFILE_OPEN", "FILE_MOVE", "FILE_SYNCUSER_UNCATEGORIZED", "USER_LOGIN", + "USER_LOGOUT", "USER_CREATION", "USER_CHANGE_PASSWORD", "USER_CHANGE_PERMISSIONS", + "USER_BADGE_IN", "USER_DELETION", "USER_RESOURCE_CREATION", + "USER_RESOURCE_UPDATE_CONTENT", "USER_RESOURCE_UPDATE_PERMISSIONS", "USER_COMMUNICATION", + "USER_RESOURCE_ACCESS", "USER_RESOURCE_DELETION", "GROUP_UNCATEGORIZED", "GROUP_CREATION", + "GROUP_DELETION", "GROUP_MODIFICATION", "EMAIL_UNCATEGORIZED", "EMAIL_TRANSACTION", + "NETWORK_UNCATEGORIZED", "NETWORK_FLOW", + "NETWORK_CONNECTION", "NETWORK_FTP", "NETWORK_DHCP", "NETWORK_DNS", "NETWORK_HTTP", + "NETWORK_SMTP", "STATUS_UNCATEGORIZED", "STATUS_HEARTBEAT", "STATUS_STARTUP", + "STATUS_SHUTDOWN", "STATUS_UPDATE", "SCAN_UNCATEGORIZED", "SCAN_FILE", + "SCAN_PROCESS", "SCAN_HOST", "SCAN_VULN_HOST", + "SCAN_VULN_NETWORK", "SCAN_NETWORK", "SCHEDULED_TASK_UNCATEGORIZED", + "SCHEDULED_TASK_CREATION", "SCHEDULED_TASK_DELETION", "SCHEDULED_TASK_ENABLE", + "SCHEDULED_TASK_DISABLE", "SCHEDULED_TASK_MODIFICATION", "SYSTEM_AUDIT_LOG_UNCATEGORIZED", + "SYSTEM_AUDIT_LOG_WIPE", "SERVICE_UNSPECIFIED", "SERVICE_CREATION", "SERVICE_DELETION", + "SERVICE_START", "SERVICE_STOP", "SERVICE_MODIFICATION", "GENERIC_EVENT", + "RESOURCE_CREATION", "RESOURCE_DELETION", "RESOURCE_PERMISSIONS_CHANGE", "RESOURCE_READ", + "RESOURCE_WRITTEN", "ANALYST_UPDATE_VERDICT", "ANALYST_UPDATE_REPUTATION", + "ANALYST_UPDATE_SEVERITY_SCORE", "ANALYST_UPDATE_STATUS", "ANALYST_ADD_COMMENT" + ], + "resource_type": [ + "UNSPECIFIED", "MUTEX", "TASK", "PIPE", "DEVICE", "FIREWALL_RULE", + "MAILBOX_FOLDER", "VPC_NETWORK", "VIRTUAL_MACHINE", "STORAGE_BUCKET", + "STORAGE_OBJECT", "DATABASE", "TABLE", "CLOUD_PROJECT", "CLOUD_ORGANIZATION", + "ACCESS_POLICY", "CLUSTER", "SETTING", "DATASET", "BACKEND_SERVICE" + ], + "action": ["UNKNOWN_ACTION", "ALLOW", "BLOCK", "ALLOW_WITH_MODIFICATION", "QUARANTINE", "FAIL"], + "severity": ["16","32","48","64","80","100"], + "alert_state": ["UNSPECIFIED", "NOT_ALERTING", "ALERTING"], + "category": ["THREAT", "VIOLATION", "POLICY", "ALERT"], + "threat_status": ["THREAT_STATUS_UNSPECIFIED", "ACTIVE", "CLEARED", "FALSE_POSITIVE"], + "type": [ + "ROLE_UNSPECIFIED", "WORKSTATION", "LAPTOP", "IOT", "NETWORK_ATTACHED_STORAGE", "PRINTER", "SCANNER", + "SERVER", "TAPE_LIBRARY", "MOBILE" + ], + "platform": [ + "UNKNOWN_PLATFORM", "WINDOWS", "MAC", "LINUX" + ], + "environment": [ + "UNSPECIFIED_CLOUD_ENVIRONMENT", "GOOGLE_CLOUD_PLATFORM", "AMAZON_WEB_SERVICES", "MICROSOFT_AZURE" + ], + "direction": [ + "UNKNOWN_DIRECTION", "INBOUND", "OUTBOUND", "BROADCAST" + ] + }, + "list_type_fields": [ + "src.ip", + "target.ip", + "principal.ip", + "src.mac", + "target.mac", + "principal.mac", + "network.smtp.server_response", + "security_result.about.email", + "security_result.about.url", + "principal.user.email_addresses", + "src.user.email_addresses", + "target.user.email_addresses", + "network.email.to", + "network.email.cc", + "network.email.bcc", + "security_result.threat_name", + "security_result.rule_name", + "security_result.summary", + "security_result.threat_id", + "security_result.description", + "network.dns.questions.name", + "network.email.subject", + "src.asset.software.name", + "target.asset.software.name", + "principal.asset.software.name", + "src.asset.software.version", + "target.asset.software.version", + "principal.asset.software.version", + "security_result.action", + "security_result.severity", + "security_result.alert_state", + "security_result.category", + "security_result.threat_status", + "network.dns.questions.class", + "network.dns.questions.type", + "principal.asset.hardware.cpu_platform", + "principal.asset.hardware.manufacturer", + "principal.asset.hardware.serial_number" + ], + "regex_formatting_fields": [ + "src.file.full_path", + "target.file.full_path", + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path", + "about.file.full_path" + ], + "security_result_conversion_fields": { + "security_result.severity": { + "16": "INFORMATIONAL", + "32": "ERROR", + "48": "LOW", + "64": "MEDIUM", + "80": "HIGH", + "100": "CRITICAL" + }, + "security_result.category": { + "ALERT": ["SOFTWARE_SUSPICIOUS", "NETWORK_SUSPICIOUS", "NETWORK_CATEGORIZED_CONTENT", + "NETWORK_DENIAL_OF_SERVICE", "NETWORK_RECON", "NETWORK_COMMAND_AND_CONTROL", "EXPLOIT", + "DATA_EXFILTRATION", "DATA_AT_REST", "DATA_DESTRUCTION" + ], + "POLICY": ["POLICY_VIOLATION"], + "VIOLATION": ["ACL_VIOLATION", "AUTH_VIOLATION"], + "THREAT": ["SOFTWARE_MALICIOUS", "SOFTWARE_PUA", "NETWORK_MALICIOUS", "MAIL_SPAM", "MAIL_PHISHING", + "MAIL_SPOOFING" + ] + } + } +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/json/from_stix_map.json b/stix_shifter_modules/gcp_chronicle/stix_translation/json/from_stix_map.json new file mode 100644 index 000000000..1eae62d76 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/json/from_stix_map.json @@ -0,0 +1,687 @@ +{ + "ipv4-addr": { + "fields": { + "value": [ + "src.ip", + "target.ip", + "principal.ip" + ], + "resolves_to_refs[*].value": [ + "src.mac", + "target.mac", + "principal.mac" + ] + } + }, + "ipv6-addr": { + "fields": { + "value": [ + "src.ip", + "target.ip", + "principal.ip" + ], + "resolves_to_refs[*].value": [ + "src.mac", + "target.mac", + "principal.mac" + ] + } + }, + "url": { + "fields": { + "value": [ + "src.url", + "target.url", + "principal.url", + "network.http.referral_url", + "security_result.about.url" + ] + } + }, + "network-traffic": { + "fields": { + "src_port": [ + "src.port", + "principal.port" + ], + "dst_port": [ + "target.port" + ], + "protocols[*]": [ + "network.ip_protocol", + "network.application_protocol" + ], + "src_ref.value": [ + "src.ip", + "principal.ip" + ], + "dst_ref.value": [ + "target.ip" + ], + "src_byte_count": [ + "network.sent_bytes" + ], + "dst_byte_count": [ + "network.received_bytes" + ], + "extensions.'x-gcp-chronicle-network'.session_duration": [ + "network.session_duration.seconds" + ], + "extensions.'x-gcp-chronicle-network'.session_id": [ + "network.session_id" + ], + "extensions.'x-gcp-chronicle-network'.direction": [ + "network.direction" + ], + "extensions.'ftp-ext'.command": [ + "network.ftp.command" + ], + "extensions.'dns-ext'.query_id": [ + "network.dns.id" + ], + "extensions.'dns-ext'.opcode": [ + "network.dns.opcode" + ], + "extensions.'dns-ext'.response_code": [ + "network.dns.response_code" + ], + "extensions.'dns-ext'.query_class": [ + "network.dns.questions.class" + ], + "extensions.'dns-ext'.query_type": [ + "network.dns.questions.type" + ], + "extensions.'dns-ext'.questions_domain_name": [ + "network.dns.questions.name" + ], + "extensions.'dhcp-ext'.client_hostname": [ + "network.dhcp.client_hostname" + ], + "extensions.'dhcp-ext'.opcode": [ + "network.dhcp.opcode" + ], + "extensions.'dhcp-ext'.server_name": [ + "network.dhcp.sname" + ], + "extensions.'dhcp-ext'.transaction_id": [ + "network.dhcp.transaction_id" + ], + "extensions.'dhcp-ext'.message_type": [ + "network.dhcp.type" + ], + "extensions.'http-ext'.request_method": [ + "network.http.method" + ], + "extensions.'http-ext'.response_code": [ + "network.http.response_code" + ], + "extensions.'http-ext'.user_agent": [ + "network.http.user_agent" + ], + "extensions.'smtp-ext'.server_response[*]": [ + "network.smtp.server_response" + ], + "extensions.'tls-ext'.cipher": [ + "network.tls.cipher" + ], + "extensions.'tls-ext'.version": [ + "network.tls.version" + ], + "extensions.'tls-ext'.version_protocol": [ + "network.tls.version_protocol" + ], + "extensions.'tls-ext'.elliptical_curve": [ + "network.tls.curve" + ], + "extensions.'tls-ext'.next_protocol": [ + "network.tls.next_protocol" + ], + "extensions.'tls-ext'.server_ja3_hash": [ + "network.tls.server.ja3s" + ], + "extensions.'tls-ext'.client_ja3_hash": [ + "network.tls.client.ja3" + ], + "extensions.'tls-ext'.server_host_name": [ + "network.tls.client.server_name" + ], + "extensions.'tls-ext'.server_certificate_ref.subject": [ + "network.tls.server.certificate.subject" + ], + "extensions.'tls-ext'.client_certificate_ref.subject": [ + "network.tls.client.certificate.subject" + ] + } + }, + "mac-addr": { + "fields": { + "value": [ + "src.mac", + "target.mac", + "principal.mac" + ] + } + }, + "autonomous-system": { + "fields": { + "number": [ + "network.asn" + ] + } + }, + "domain-name": { + "fields": { + "value": [ + "src.domain.name", + "target.domain.name", + "principal.domain.name", + "network.dns_domain" + ], + "extensions.'x-gcp-chronicle-domain'.status": [ + "src.domain.status", + "target.domain.status", + "principal.domain.status" + ] + } + }, + "email-addr": { + "fields": { + "value": [ + "principal.user.email_addresses", + "src.user.email_addresses", + "target.user.email_addresses", + "network.email.from", + "network.email.to", + "network.email.cc", + "network.email.bcc", + "security_result.about.email" + ] + } + }, + "email-message": { + "fields": { + "subject": [ + "network.email.subject" + ], + "to_refs[*]": [ + "network.email.to" + ], + "from_ref": [ + "network.email.from" + ], + "cc_refs[*]": [ + "network.email.cc" + ], + "bcc_refs[*]": [ + "network.email.bcc" + ], + "extensions.'x-gcp-chronicle-email-message'.file_ref.name": [ + "about.file.full_path" + ] + } + }, + "user-account": { + "fields": { + "user_id": [ + "src.user.userid", + "target.user.userid", + "principal.user.userid" + ], + "display_name": [ + "src.user.user_display_name", + "target.user.user_display_name", + "principal.user.user_display_name" + ], + "extensions.'x-gcp-chronicle-user'.type": [ + "src.user.account_type", + "target.user.account_type", + "principal.user.account_type" + ], + "extensions.'windows-account-ext'.sid": [ + "src.user.windows_sid", + "target.user.windows_sid", + "principal.user.windows_sid" + ] + } + }, + "file": { + "fields": { + "name": [ + "src.file.full_path", + "target.file.full_path", + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path", + "about.file.full_path" + ], + "size": [ + "src.file.size", + "target.file.size", + "src.process.file.size", + "target.process.file.size", + "principal.process.file.size", + "about.file.size" + ], + "hashes.MD5": [ + "src.file.md5", + "target.file.md5", + "src.process.file.md5", + "target.process.file.md5", + "principal.process.file.md5", + "about.file.md5" + ], + "hashes.'SHA-1'": [ + "src.file.sha1", + "target.file.sha1", + "src.process.file.sha1", + "target.process.file.sha1", + "principal.process.file.sha1", + "about.file.sha1" + ], + "hashes.'SHA-256'": [ + "src.file.sha256", + "target.file.sha256", + "src.process.file.sha256", + "target.process.file.sha256", + "principal.process.file.sha256", + "about.file.sha256" + ], + "modified": [ + "src.file.last_modification_time.seconds", + "target.file.last_modification_time.seconds", + "src.process.file.last_modification_time.seconds", + "target.process.file.last_modification_time.seconds", + "principal.process.file.last_modification_time.seconds", + "about.file.last_modification_time.seconds" + ], + "parent_directory_ref.path": [ + "src.file.full_path", + "target.file.full_path", + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path", + "about.file.full_path" + ], + "extensions.'x-gcp-chronicle-file'.mime_type": [ + "src.file.mime_type", + "target.file.mime_type", + "src.process.file.mime_type", + "target.process.file.mime_type", + "principal.process.file.mime_type", + "about.file.mime_type" + ], + "extensions.'x-gcp-chronicle-file'.file_type": [ + "src.file.file_type", + "target.file.file_type", + "src.process.file.file_type", + "target.process.file.file_type", + "principal.process.file.file_type", + "about.file.file_type" + ] + } + }, + "directory": { + "fields": { + "path": [ + "src.file.full_path", + "target.file.full_path", + "about.file.full_path", + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ] + } + }, + "process": { + "fields": { + "command_line": [ + "src.process.command_line", + "target.process.command_line", + "principal.process.command_line", + "target.process.parent_process.command_line", + "principal.process.parent_process.command_line" + ], + "name": [ + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ], + "pid": [ + "src.process.pid", + "target.process.pid", + "principal.process.pid", + "target.process.parent_process.pid", + "principal.process.parent_process.pid" + ], + "parent_ref.pid": [ + "target.process.parent_process.pid", + "principal.process.parent_process.pid" + ], + "creator_user_ref.user_id": [ + "src.user.userid", + "target.user.userid", + "principal.user.userid" + ], + "parent_ref.name": [ + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ], + "binary_ref.name": [ + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ], + "binary_ref.hashes.MD5": [ + "src.process.file.md5", + "target.process.file.md5", + "principal.process.file.md5" + ], + "binary_ref.hashes.'SHA-256'": [ + "src.process.file.sha256", + "target.process.file.sha256", + "principal.process.file.sha256" + ], + "binary_ref.parent_directory_ref.path": [ + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path" + ] + } + }, + "software": { + "fields": { + "name": [ + "src.asset.software.name", + "target.asset.software.name", + "principal.asset.software.name", + "principal.asset.platform_software.platform", + "target.asset.platform_software.platform", + "src.asset.platform_software.platform" + ], + "version": [ + "src.asset.software.version", + "target.asset.software.version", + "principal.asset.software.version", + "principal.asset.platform_software.platform_version", + "src.asset.platform_software.platform_version", + "target.asset.platform_software.platform_version" + ] + } + }, + "windows-registry-key": { + "fields": { + "key": [ + "src.registry.registry_key", + "target.registry.registry_key" + ], + "values[*]": [ + "src.registry.registry_value_data", + "target.registry.registry_value_data" + ] + } + }, + "x509-certificate": { + "fields": { + "version": [ + "network.tls.client.certificate.version", + "network.tls.server.certificate.version" + ], + "serial_number": [ + "network.tls.client.certificate.serial", + "network.tls.server.certificate.serial" + ], + "issuer": [ + "network.tls.client.certificate.issuer", + "network.tls.server.certificate.issuer" + ], + "validity_not_before": [ + "network.tls.server.certificate.not_before.seconds", + "network.tls.client.certificate.not_before.seconds" + ], + "validity_not_after": [ + "network.tls.server.certificate.not_after.seconds", + "network.tls.client.certificate.not_after.seconds" + ], + "subject": [ + "network.tls.client.certificate.subject", + "network.tls.server.certificate.subject" + ], + "hashes.MD5": [ + "network.tls.client.certificate.md5", + "network.tls.server.certificate.md5" + ], + "hashes.'SHA-1'": [ + "network.tls.client.certificate.sha1", + "network.tls.server.certificate.sha1" + ], + "hashes.'SHA-256'": [ + "network.tls.client.certificate.sha256", + "network.tls.server.certificate.sha256" + ] + } + }, + "x-ibm-finding": { + "fields": { + "name": [ + "security_result.summary" + ], + "finding_type": [ + "security_result.category" + ], + "rule_names[*]": [ + "security_result.rule_name" + ], + "severity": [ + "security_result.severity" + ], + "src_ip_ref.value": [ + "principal.ip" + ], + "dst_ip_ref.value": [ + "target.ip" + ], + "src_os_ref": [ + "principal.asset.platform_software.platform" + ], + "dst_os_ref": [ + "target.asset.platform_software.platform" + ], + "src_application_ref.name": [ + "principal.asset.software.name" + ], + "dst_application_ref.name": [ + "target.asset.software.name" + ], + "extensions.'x-gcp-chronicle-security-result'.url_ref.value": [ + "security_result.about.url" + ], + "extensions.'x-gcp-chronicle-security-result'.alert_state": [ + "security_result.alert_state" + ], + "extensions.'x-gcp-chronicle-security-result'.threat_id": [ + "security_result.threat_id" + ], + "extensions.'x-gcp-chronicle-security-result'.threat_status": [ + "security_result.threat_status" + ], + "extensions.'x-gcp-chronicle-security-result'.threat_name": [ + "security_result.threat_name" + ], + "extensions.'x-gcp-chronicle-security-result'.description": [ + "security_result.description" + ], + "extensions.'x-gcp-chronicle-security-result'.actions_taken[*]": [ + "security_result.action" + ] + } + }, + "x-oca-event": { + "fields": { + "action": [ + "metadata.event_type" + ], + "code": [ + "metadata.product_log_id" + ], + "created": [ + "metadata.event_timestamp.seconds" + ], + "agent": [ + "metadata.product_name" + ], + "provider": [ + "metadata.vendor_name" + ], + "outcome": [ + "metadata.product_event_type" + ], + "host_ref.hostname": [ + "principal.hostname" + ], + "url_ref.value": [ + "principal.url" + ], + "file_ref.name": [ + "principal.process.file.full_path" + ], + "process_ref.name": [ + "principal.process.file.full_path" + ], + "parent_process_ref.name": [ + "principal.process.parent_process.file.full_path" + ], + "domain_ref.value": [ + "principal.domain.name" + ], + "ip_refs[*].value": [ + "src.ip", + "principal.ip", + "target.ip" + ], + "network_ref.src_port": [ + "principal.port" + ], + "network_ref.dst_port": [ + "target.port" + ], + "user_ref.user_id": [ + "principal.user.userid" + ], + "registry_ref.key": [ + "target.registry.registry_key" + ], + "cross_process_target_ref.name": [ + "target.process.file.full_path" + ], + "extensions.'x-gcp-chronicle-event'.src_location": [ + "principal.location.name" + ], + "extensions.'x-gcp-chronicle-event'.target_location": [ + "target.location.name" + ], + "extensions.'x-gcp-chronicle-event'.target_hostname": [ + "target.hostname" + ], + "extensions.'x-gcp-chronicle-event'.email_message_ref.value": [ + "network.email.from" + ], + "extensions.'x-gcp-chronicle-event'.src_appservice": [ + "principal.application" + ], + "extensions.'x-gcp-chronicle-event'.target_appservice": [ + "target.application" + ], + "extensions.'x-gcp-chronicle-event'.src_resource_ref.name": [ + "principal.resource.name" + ], + "extensions.'x-gcp-chronicle-event'.target_resource_ref.name": [ + "target.resource.name" + ], + "extensions.'x-gcp-chronicle-event'.description": [ + "metadata.description" + ] + } + }, + "x-oca-asset": { + "fields": { + "hostname": [ + "principal.hostname" + ], + "ip_refs[*].value": [ + "principal.ip" + ], + "mac_refs[*].value": [ + "principal.mac" + ], + "extensions.'x-gcp-chronicle-asset'.cloud_environment": [ + "principal.asset.attribute.cloud.environment" + ], + "extensions.'x-gcp-chronicle-asset'.cloud_availability_zone": [ + "principal.asset.attribute.cloud.availability_zone" + ], + "extensions.'x-gcp-chronicle-asset'.city": [ + "principal.location.city" + ], + "extensions.'x-gcp-chronicle-asset'.country_or_region": [ + "principal.location.country_or_region" + ], + "extensions.'x-gcp-chronicle-asset'.asset_id": [ + "principal.asset_id" + ], + "extensions.'x-gcp-chronicle-asset'.category": [ + "principal.asset.category" + ], + "extensions.'x-gcp-chronicle-asset'.type": [ + "principal.asset.type" + ], + "extensions.'x-gcp-chronicle-asset'.hw_cpu_platform": [ + "principal.asset.hardware.cpu_platform" + ], + "extensions.'x-gcp-chronicle-asset'.hw_manufacturer": [ + "principal.asset.hardware.manufacturer" + ], + "extensions.'x-gcp-chronicle-asset'.hw_serial_number": [ + "principal.asset.hardware.serial_number" + ] + } + }, + "x-gcp-chronicle-resource": { + "fields": { + "name": [ + "src.resource.name", + "target.resource.name", + "principal.resource.name" + ], + "resource_type": [ + "src.resource.resource_type", + "target.resource.resource_type", + "principal.resource.resource_type" + ], + "resource_subtype": [ + "src.resource.resource_subtype", + "target.resource.resource_subtype", + "principal.resource.resource_subtype" + ], + "availability_zone": [ + "src.resource.attribute.cloud.availability_zone", + "target.resource.attribute.cloud.availability_zone", + "principal.resource.attribute.cloud.availability_zone" + ], + "environment": [ + "src.resource.attribute.cloud.environment", + "target.resource.attribute.cloud.environment", + "principal.resource.attribute.cloud.environment" + ] + } + } +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/json/operators.json b/stix_shifter_modules/gcp_chronicle/stix_translation/json/operators.json new file mode 100644 index 000000000..eb8712b26 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/json/operators.json @@ -0,0 +1,15 @@ +{ + "ComparisonExpressionOperators.And": "and", + "ComparisonExpressionOperators.Or": "or", + "ComparisonComparators.Equal": "=", + "ComparisonComparators.NotEqual": "!=", + "ComparisonComparators.Like": "=", + "ComparisonComparators.Matches": "=", + "ComparisonComparators.GreaterThan": ">", + "ComparisonComparators.GreaterThanOrEqual": ">=", + "ComparisonComparators.LessThan": "<", + "ComparisonComparators.LessThanOrEqual": "<=", + "ComparisonComparators.In": "=", + "ObservationOperators.Or": "or", + "ObservationOperators.And": "or" +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/json/stix_2_1/from_stix_map.json b/stix_shifter_modules/gcp_chronicle/stix_translation/json/stix_2_1/from_stix_map.json new file mode 100644 index 000000000..f66491b93 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/json/stix_2_1/from_stix_map.json @@ -0,0 +1,676 @@ +{ + "ipv4-addr": { + "fields": { + "value": [ + "src.ip", + "target.ip", + "principal.ip" + ], + "resolves_to_refs[*].value": [ + "src.mac", + "target.mac", + "principal.mac" + ] + } + }, + "ipv6-addr": { + "fields": { + "value": [ + "src.ip", + "target.ip", + "principal.ip" + ], + "resolves_to_refs[*].value": [ + "src.mac", + "target.mac", + "principal.mac" + ] + } + }, + "url": { + "fields": { + "value": [ + "src.url", + "target.url", + "principal.url", + "network.http.referral_url", + "security_result.about.url" + ] + } + }, + "network-traffic": { + "fields": { + "src_port": [ + "src.port", + "principal.port" + ], + "dst_port": [ + "target.port" + ], + "protocols[*]": [ + "network.ip_protocol", + "network.application_protocol" + ], + "src_ref.value": [ + "src.ip", + "principal.ip" + ], + "dst_ref.value": [ + "target.ip" + ], + "src_byte_count": [ + "network.sent_bytes" + ], + "dst_byte_count": [ + "network.received_bytes" + ], + "extensions.'x-gcp-chronicle-network-ext'.session_duration": [ + "network.session_duration.seconds" + ], + "extensions.'x-gcp-chronicle-network-ext'.session_id": [ + "network.session_id" + ], + "extensions.'x-gcp-chronicle-network-ext'.direction": [ + "network.direction" + ], + "extensions.'ftp-ext'.command": [ + "network.ftp.command" + ], + "extensions.'dns-ext'.query_id": [ + "network.dns.id" + ], + "extensions.'dns-ext'.opcode": [ + "network.dns.opcode" + ], + "extensions.'dns-ext'.response_code": [ + "network.dns.response_code" + ], + "extensions.'dns-ext'.query_class": [ + "network.dns.questions.class" + ], + "extensions.'dns-ext'.query_type": [ + "network.dns.questions.type" + ], + "extensions.'dns-ext'.questions_domain_name": [ + "network.dns.questions.name" + ], + "extensions.'dhcp-ext'.client_hostname": [ + "network.dhcp.client_hostname" + ], + "extensions.'dhcp-ext'.opcode": [ + "network.dhcp.opcode" + ], + "extensions.'dhcp-ext'.server_name": [ + "network.dhcp.sname" + ], + "extensions.'dhcp-ext'.transaction_id": [ + "network.dhcp.transaction_id" + ], + "extensions.'dhcp-ext'.message_type": [ + "network.dhcp.type" + ], + "extensions.'http-ext'.request_method": [ + "network.http.method" + ], + "extensions.'http-ext'.response_code": [ + "network.http.response_code" + ], + "extensions.'http-ext'.user_agent": [ + "network.http.user_agent" + ], + "extensions.'smtp-ext'.server_response[*]": [ + "network.smtp.server_response" + ], + "extensions.'tls-ext'.cipher": [ + "network.tls.cipher" + ], + "extensions.'tls-ext'.version": [ + "network.tls.version" + ], + "extensions.'tls-ext'.version_protocol": [ + "network.tls.version_protocol" + ], + "extensions.'tls-ext'.elliptical_curve": [ + "network.tls.curve" + ], + "extensions.'tls-ext'.next_protocol": [ + "network.tls.next_protocol" + ], + "extensions.'tls-ext'.server_ja3_hash": [ + "network.tls.server.ja3s" + ], + "extensions.'tls-ext'.client_ja3_hash": [ + "network.tls.client.ja3" + ], + "extensions.'tls-ext'.server_host_name": [ + "network.tls.client.server_name" + ], + "extensions.'tls-ext'.server_certificate_ref.subject": [ + "network.tls.server.certificate.subject" + ], + "extensions.'tls-ext'.client_certificate_ref.subject": [ + "network.tls.client.certificate.subject" + ] + } + }, + "mac-addr": { + "fields": { + "value": [ + "src.mac", + "target.mac", + "principal.mac" + ] + } + }, + "autonomous-system": { + "fields": { + "number": [ + "network.asn" + ] + } + }, + "domain-name": { + "fields": { + "value": [ + "src.domain.name", + "target.domain.name", + "principal.domain.name", + "network.dns_domain" + ], + "extensions.'x-gcp-chronicle-domain-ext'.status": [ + "src.domain.status", + "target.domain.status", + "principal.domain.status" + ] + } + }, + "email-addr": { + "fields": { + "value": [ + "principal.user.email_addresses", + "src.user.email_addresses", + "target.user.email_addresses", + "network.email.from", + "network.email.to", + "network.email.cc", + "network.email.bcc", + "security_result.about.email" + ] + } + }, + "email-message": { + "fields": { + "subject": [ + "network.email.subject" + ], + "to_refs[*]": [ + "network.email.to" + ], + "from_ref": [ + "network.email.from" + ], + "cc_refs[*]": [ + "network.email.cc" + ], + "bcc_refs[*]": [ + "network.email.bcc" + ], + "extensions.'x-gcp-chronicle-email-message'.file_ref.name": [ + "about.file.full_path" + ] + } + }, + "user-account": { + "fields": { + "user_id": [ + "src.user.userid", + "target.user.userid", + "principal.user.userid" + ], + "display_name": [ + "src.user.user_display_name", + "target.user.user_display_name", + "principal.user.user_display_name" + ], + "extensions.'x-gcp-chronicle-user-ext'.type": [ + "src.user.account_type", + "target.user.account_type", + "principal.user.account_type" + ], + "extensions.'windows-account-ext'.sid": [ + "src.user.windows_sid", + "target.user.windows_sid", + "principal.user.windows_sid" + ] + } + }, + "file": { + "fields": { + "name": [ + "src.file.full_path", + "target.file.full_path", + "about.file.full_path", + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ], + "size": [ + "src.file.size", + "target.file.size", + "about.file.size", + "src.process.file.size", + "target.process.file.size", + "principal.process.file.size" + ], + "hashes.MD5": [ + "src.file.md5", + "target.file.md5", + "src.process.file.md5", + "target.process.file.md5", + "principal.process.file.md5", + "about.file.md5" + ], + "hashes.'SHA-1'": [ + "src.file.sha1", + "target.file.sha1", + "src.process.file.sha1", + "target.process.file.sha1", + "principal.process.file.sha1", + "about.file.sha1" + ], + "hashes.'SHA-256'": [ + "src.file.sha256", + "target.file.sha256", + "src.process.file.sha256", + "target.process.file.sha256", + "principal.process.file.sha256", + "about.file.sha256" + ], + "mtime": [ + "src.file.last_modification_time.seconds", + "target.file.last_modification_time.seconds", + "src.process.file.last_modification_time.seconds", + "target.process.file.last_modification_time.seconds", + "principal.process.file.last_modification_time.seconds", + "about.file.last_modification_time.seconds" + ], + "parent_directory_ref.path": [ + "src.file.full_path", + "target.file.full_path", + "about.file.full_path", + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ], + "extensions.'x-gcp-chronicle-file-ext'.mime_type": [ + "src.file.mime_type", + "target.file.mime_type", + "src.process.file.mime_type", + "target.process.file.mime_type", + "principal.process.file.mime_type", + "about.file.mime_type" + ], + "extensions.'x-gcp-chronicle-file-ext'.file_type": [ + "src.file.file_type", + "target.file.file_type", + "about.file.file_type", + "src.process.file.file_type", + "target.process.file.file_type", + "principal.process.file.file_type" + ] + } + }, + "directory": { + "fields": { + "path": [ + "src.file.full_path", + "target.file.full_path", + "about.file.full_path", + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ] + } + }, + "process": { + "fields": { + "command_line": [ + "src.process.command_line", + "target.process.command_line", + "principal.process.command_line", + "target.process.parent_process.command_line", + "principal.process.parent_process.command_line" + ], + "pid": [ + "src.process.pid", + "target.process.pid", + "principal.process.pid", + "target.process.parent_process.pid", + "principal.process.parent_process.pid" + ], + "parent_ref.pid": [ + "target.process.parent_process.pid", + "principal.process.parent_process.pid" + ], + "creator_user_ref.user_id": [ + "src.user.userid", + "target.user.userid", + "principal.user.userid" + ], + "image_ref.name": [ + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path", + "target.process.parent_process.file.full_path", + "principal.process.parent_process.file.full_path" + ], + "image_ref.hashes.MD5": [ + "src.process.file.md5", + "target.process.file.md5", + "principal.process.file.md5" + ], + "image_ref.hashes.'SHA-256'": [ + "src.process.file.sha256", + "target.process.file.sha256", + "principal.process.file.sha256" + ], + "image_ref.parent_directory_ref.path": [ + "src.process.file.full_path", + "target.process.file.full_path", + "principal.process.file.full_path" + ] + } + }, + "software": { + "fields": { + "name": [ + "src.asset.software.name", + "target.asset.software.name", + "principal.asset.software.name", + "principal.asset.platform_software.platform", + "target.asset.platform_software.platform", + "src.asset.platform_software.platform" + ], + "version": [ + "src.asset.software.version", + "target.asset.software.version", + "principal.asset.software.version", + "principal.asset.platform_software.platform_version", + "src.asset.platform_software.platform_version", + "target.asset.platform_software.platform_version" + ] + } + }, + "windows-registry-key": { + "fields": { + "key": [ + "src.registry.registry_key", + "target.registry.registry_key" + ], + "values[*]": [ + "src.registry.registry_value_data", + "target.registry.registry_value_data" + ] + } + }, + "x509-certificate": { + "fields": { + "version": [ + "network.tls.client.certificate.version", + "network.tls.server.certificate.version" + ], + "serial_number": [ + "network.tls.client.certificate.serial", + "network.tls.server.certificate.serial" + ], + "issuer": [ + "network.tls.client.certificate.issuer", + "network.tls.server.certificate.issuer" + ], + "validity_not_before": [ + "network.tls.server.certificate.not_before.seconds", + "network.tls.client.certificate.not_before.seconds" + ], + "validity_not_after": [ + "network.tls.server.certificate.not_after.seconds", + "network.tls.client.certificate.not_after.seconds" + ], + "subject": [ + "network.tls.client.certificate.subject", + "network.tls.server.certificate.subject" + ], + "hashes.MD5": [ + "network.tls.client.certificate.md5", + "network.tls.server.certificate.md5" + ], + "hashes.'SHA-1'": [ + "network.tls.client.certificate.sha1", + "network.tls.server.certificate.sha1" + ], + "hashes.'SHA-256'": [ + "network.tls.client.certificate.sha256", + "network.tls.server.certificate.sha256" + ] + } + }, + "x-ibm-finding": { + "fields": { + "name": [ + "security_result.summary" + ], + "finding_type": [ + "security_result.category" + ], + "rule_names[*]": [ + "security_result.rule_name" + ], + "severity": [ + "security_result.severity" + ], + "src_ip_ref.value": [ + "principal.ip" + ], + "dst_ip_ref.value": [ + "target.ip" + ], + "src_os_ref": [ + "principal.asset.platform_software.platform" + ], + "dst_os_ref": [ + "target.asset.platform_software.platform" + ], + "src_application_ref.name": [ + "principal.asset.software.name" + ], + "dst_application_ref.name": [ + "target.asset.software.name" + ], + "extensions.'x-gcp-chronicle-security-result'.url_ref.value": [ + "security_result.about.url" + ], + "extensions.'x-gcp-chronicle-security-result'.alert_state": [ + "security_result.alert_state" + ], + "extensions.'x-gcp-chronicle-security-result'.threat_id": [ + "security_result.threat_id" + ], + "extensions.'x-gcp-chronicle-security-result'.threat_status": [ + "security_result.threat_status" + ], + "extensions.'x-gcp-chronicle-security-result'.threat_name": [ + "security_result.threat_name" + ], + "extensions.'x-gcp-chronicle-security-result'.description": [ + "security_result.description" + ], + "extensions.'x-gcp-chronicle-security-result'.actions_taken[*]": [ + "security_result.action" + ] + } + }, + "x-oca-event": { + "fields": { + "action": [ + "metadata.event_type" + ], + "code": [ + "metadata.product_log_id" + ], + "created": [ + "metadata.event_timestamp.seconds" + ], + "agent": [ + "metadata.product_name" + ], + "provider": [ + "metadata.vendor_name" + ], + "outcome": [ + "metadata.product_event_type" + ], + "host_ref.hostname": [ + "principal.hostname" + ], + "url_ref.value": [ + "principal.url" + ], + "file_ref.name": [ + "principal.process.file.full_path" + ], + "process_ref.name": [ + "principal.process.pid" + ], + "parent_process_ref.name": [ + "principal.process.parent_process.pid" + ], + "domain_ref.value": [ + "principal.domain.name" + ], + "ip_refs[*].value": [ + "src.ip", + "principal.ip", + "target.ip" + ], + "network_ref.src_port": [ + "principal.port" + ], + "network_ref.dst_port": [ + "target.port" + ], + "user_ref.user_id": [ + "principal.user.userid" + ], + "registry_ref.key": [ + "target.registry.registry_key" + ], + "cross_process_target_ref.name": [ + "target.process.pid" + ], + "extensions.'x-gcp-chronicle-event-ext'.src_location": [ + "principal.location.name" + ], + "extensions.'x-gcp-chronicle-event-ext'.target_location": [ + "target.location.name" + ], + "extensions.'x-gcp-chronicle-event-ext'.target_hostname": [ + "target.hostname" + ], + "extensions.'x-gcp-chronicle-event-ext'.email_message_ref.value": [ + "network.email.from" + ], + "extensions.'x-gcp-chronicle-event-ext'.src_appservice": [ + "principal.application" + ], + "extensions.'x-gcp-chronicle-event-ext'.target_appservice": [ + "target.application" + ], + "extensions.'x-gcp-chronicle-event-ext'.src_resource_ref.name": [ + "principal.resource.name" + ], + "extensions.'x-gcp-chronicle-event-ext'.target_resource_ref.name": [ + "target.resource.name" + ], + "extensions.'x-gcp-chronicle-event-ext'.description": [ + "metadata.description" + ] + } + }, + "x-oca-asset": { + "fields": { + "hostname": [ + "principal.hostname" + ], + "ip_refs[*].value": [ + "principal.ip" + ], + "mac_refs[*].value": [ + "principal.mac" + ], + "extensions.'x-gcp-chronicle-asset-ext'.cloud_environment": [ + "principal.asset.attribute.cloud.environment" + ], + "extensions.'x-gcp-chronicle-asset-ext'.cloud_availability_zone": [ + "principal.asset.attribute.cloud.availability_zone" + ], + "extensions.'x-gcp-chronicle-asset-ext'.city": [ + "principal.location.city" + ], + "extensions.'x-gcp-chronicle-asset-ext'.country_or_region": [ + "principal.location.country_or_region" + ], + "extensions.'x-gcp-chronicle-asset-ext'.asset_id": [ + "principal.asset_id" + ], + "extensions.'x-gcp-chronicle-asset-ext'.category": [ + "principal.asset.category" + ], + "extensions.'x-gcp-chronicle-asset-ext'.type": [ + "principal.asset.type" + ], + "extensions.'x-gcp-chronicle-asset-ext'.hw_cpu_platform": [ + "principal.asset.hardware.cpu_platform" + ], + "extensions.'x-gcp-chronicle-asset-ext'.hw_manufacturer": [ + "principal.asset.hardware.manufacturer" + ], + "extensions.'x-gcp-chronicle-asset-ext'.hw_serial_number": [ + "principal.asset.hardware.serial_number" + ] + } + }, + "x-gcp-chronicle-resource": { + "fields": { + "name": [ + "src.resource.name", + "target.resource.name", + "principal.resource.name" + ], + "resource_type": [ + "src.resource.resource_type", + "target.resource.resource_type", + "principal.resource.resource_type" + ], + "resource_subtype": [ + "src.resource.resource_subtype", + "target.resource.resource_subtype", + "principal.resource.resource_subtype" + ], + "availability_zone": [ + "src.resource.attribute.cloud.availability_zone", + "target.resource.attribute.cloud.availability_zone", + "principal.resource.attribute.cloud.availability_zone" + ], + "environment": [ + "src.resource.attribute.cloud.environment", + "target.resource.attribute.cloud.environment", + "principal.resource.attribute.cloud.environment" + ] + } + } +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/json/stix_2_1/to_stix_map.json b/stix_shifter_modules/gcp_chronicle/stix_translation/json/stix_2_1/to_stix_map.json new file mode 100644 index 000000000..71561a416 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/json/stix_2_1/to_stix_map.json @@ -0,0 +1,1660 @@ +{ + "event": { + "principal": { + "ip": [ + { + "key": "ipv4-addr.value", + "object": "principal_ip", + "unwrap": true, + "transformer": "FilterIPv4List" + }, + { + "key": "ipv6-addr.value", + "object": "principal_ip", + "unwrap": true, + "transformer": "FilterIPv6List" + }, + { + "key": "network-traffic.src_ref", + "object": "nt", + "references": "principal_ip" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + }, + { + "key": "x-ibm-finding.src_ip_ref", + "object": "security_result", + "references": "principal_ip" + }, + { + "key": "x-oca-event.ip_refs", + "object": "event", + "references": [ + "principal_ip" + ], + "group": true + }, + { + "key": "x-oca-asset.ip_refs", + "object": "principal_asset", + "references": [ + "principal_ip" + ] + } + ], + "port": [ + { + "key": "network-traffic.src_port", + "object": "nt", + "transformer": "ToInteger" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + } + ], + "mac": [ + { + "key": "mac-addr.value", + "object": "principal_mac", + "unwrap": true + }, + { + "key": "x-oca-asset.mac_refs", + "object": "principal_asset", + "references": [ + "principal_mac" + ] + }, + { + "key": "ipv4-addr.resolves_to_refs", + "object": "principal_ip", + "references": [ + "principal_mac" + ] + }, + { + "key": "ipv6-addr.resolves_to_refs", + "object": "principal_ip", + "references": [ + "principal_mac" + ] + } + ], + "url": [ + { + "key": "url.value", + "object": "principal_url", + "transformer": "ValidateUrl" + }, + { + "key": "x-oca-event.url_ref", + "object": "event", + "references": "principal_url" + } + ], + "domain": { + "name": [ + { + "key": "domain-name.value", + "object": "principal_domain" + }, + { + "key": "x-oca-event.domain_ref", + "object": "event", + "references": "principal_domain" + } + ], + "status": { + "key": "domain-name.extensions.x-gcp-chronicle-domain-ext.status", + "object": "principal_domain" + } + }, + "application": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.src_appservice", + "object": "event" + }, + "hostname": [ + { + "key": "x-oca-asset.hostname", + "object": "principal_asset" + }, + { + "key": "x-oca-event.host_ref", + "object": "event", + "references": "principal_asset" + } + ], + "assetId": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.asset_id", + "object": "principal_asset" + }, + "location": { + "name": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.src_location", + "object": "event" + }, + "city": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.city", + "object": "principal_asset" + }, + "countryOrRegion": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.country_or_region", + "object": "principal_asset" + } + }, + "asset": { + "category": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.category", + "object": "principal_asset" + }, + "type": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.type", + "object": "principal_asset" + }, + "platformSoftware": { + "platform": [ + { + "key": "software.name", + "object": "principal_os_software" + }, + { + "key": "x-ibm-finding.src_os_ref", + "object": "security_result", + "references": "principal_os_software" + } + ], + "platformVersion": { + "key": "software.version", + "object": "principal_os_software" + } + }, + "hardware": { + "cpuPlatform": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.hw_cpu_platform", + "object": "principal_asset" + }, + "manufacturer": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.hw_manufacturer", + "object": "principal_asset" + }, + "serialNumber": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.hw_serial_number", + "object": "principal_asset" + } + }, + "attribute": { + "cloud": { + "environment": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.cloud_environment", + "object": "principal_asset" + }, + "availabilityZone": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset-ext.cloud_availability_zone", + "object": "principal_asset" + } + } + }, + "software": { + "name": [ + { + "key": "software.name", + "object": "principal_software" + }, + { + "key": "x-ibm-finding.src_application_ref", + "object": "security_result", + "references": "principal_software" + } + ], + "version": { + "key": "software.version", + "object": "principal_software" + } + } + }, + "user": { + "userid": [ + { + "key": "user-account.user_id", + "object": "principal_user" + }, + { + "key": "process.creator_user_ref", + "object": "principal_process", + "references": "principal_user" + }, + { + "key": "x-oca-event.user_ref", + "object": "event", + "references": "principal_user" + } + ], + "userDisplayName": { + "key": "user-account.display_name", + "object": "principal_user" + }, + "accountType": { + "key": "user-account.extensions.x-gcp-chronicle-user-ext.type", + "object": "principal_user" + }, + "windowsSid": { + "key": "user-account.extensions.windows-account-ext.sid", + "object": "principal_user" + }, + "emailAddresses": [ + { + "key": "email-addr.value", + "object": "principal_email", + "unwrap": true + } + ] + }, + "process": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "principal_process_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "principal_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "principal_process_file", + "references": "principal_process_directory" + }, + { + "key": "process.image_ref", + "object": "principal_process", + "references": "principal_process_file" + }, + { + "key": "x-oca-event.file_ref", + "object": "event", + "references": "principal_process_file" + } + ], + "size": { + "key": "file.size", + "object": "principal_process_file", + "transformer": "ToInteger" + }, + "md5": [ + { + "key": "file.hashes.MD5", + "object": "principal_process_file" + }, + { + "key": "process.image_ref", + "object": "principal_process", + "references": "principal_process_file" + } + ], + "sha1": { + "key": "file.hashes.SHA-1", + "object": "principal_process_file" + }, + "sha256": [ + { + "key": "file.hashes.SHA-256", + "object": "principal_process_file" + }, + { + "key": "process.image_ref", + "object": "principal_process", + "references": "principal_process_file" + } + ], + "lastModificationTime": { + "key": "file.mtime", + "object": "principal_process_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.mime_type", + "object": "principal_process_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.file_type", + "object": "principal_process_file" + } + }, + "commandLine": { + "key": "process.command_line", + "object": "principal_process" + }, + "parentProcess": { + "commandLine": { + "key": "process.command_line", + "object": "principal_parent_process" + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "principal_parent_process_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "principal_parent_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "principal_parent_process_file", + "references": "principal_parent_process_directory" + }, + { + "key": "process.image_ref", + "object": "principal_parent_process", + "references": "principal_parent_process_file" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "principal_parent_process", + "transformer": "ToInteger" + }, + { + "key": "process.parent_ref", + "object": "principal_process", + "references": "principal_parent_process" + }, + { + "key": "x-oca-event.parent_process_ref", + "object": "event", + "references": "principal_parent_process" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "principal_process", + "transformer": "ToInteger" + }, + { + "key": "x-oca-event.process_ref", + "object": "event", + "references": "principal_process" + } + ] + }, + "resource": { + "resourceType": { + "key": "x-gcp-chronicle-resource.resource_type", + "object": "principal_resource" + }, + "resourceSubtype": { + "key": "x-gcp-chronicle-resource.resource_subtype", + "object": "principal_resource" + }, + "name": [ + { + "key": "x-gcp-chronicle-resource.name", + "object": "principal_resource" + }, + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.src_resource_ref", + "object": "event", + "references": "principal_resource" + } + ], + "attribute": { + "cloud": { + "availabilityZone": { + "key": "x-gcp-chronicle-resource.availability_zone", + "object": "principal_resource" + }, + "environment": { + "key": "x-gcp-chronicle-resource.environment", + "object": "principal_resource" + } + } + } + } + }, + "src": { + "ip": [ + { + "key": "ipv4-addr.value", + "object": "src_ip", + "unwrap": true, + "transformer": "FilterIPv4List" + }, + { + "key": "ipv6-addr.value", + "object": "src_ip", + "unwrap": true, + "transformer": "FilterIPv6List" + }, + { + "key": "network-traffic.src_ref", + "object": "nt", + "references": "src_ip" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + }, + { + "key": "x-oca-event.ip_refs", + "object": "event", + "references": [ + "src_ip" + ], + "group": true + } + ], + "port": { + "key": "network-traffic.src_port", + "object": "nt", + "transformer": "ToInteger" + }, + "mac": [ + { + "key": "mac-addr.value", + "object": "src_mac", + "unwrap": true + }, + { + "key": "ipv4-addr.resolves_to_refs", + "object": "src_ip", + "references": [ + "src_mac" + ] + }, + { + "key": "ipv6-addr.resolves_to_refs", + "object": "src_ip", + "references": [ + "src_mac" + ] + } + ], + "url": { + "key": "url.value", + "object": "src_url", + "transformer": "ValidateUrl" + }, + "domain": { + "name": { + "key": "domain-name.value", + "object": "src_domain" + }, + "status": { + "key": "domain-name.extensions.x-gcp-chronicle-domain-ext.status", + "object": "src_domain" + } + }, + "registry": { + "registryKey": { + "key": "windows-registry-key.key", + "object": "src_registry" + }, + "registryValues": { + "key": "windows-registry-key.values", + "object": "src_registry", + "transformer": "FormatToStixRegistryValue" + } + }, + "asset": { + "platformSoftware": { + "platform": { + "key": "software.name", + "object": "src_os_software" + }, + "platformVersion": { + "key": "software.version", + "object": "src_os_software" + } + }, + "software": { + "name": { + "key": "software.name", + "object": "src_software" + }, + "version": { + "key": "software.version", + "object": "src_software" + } + } + }, + "user": { + "userid": [ + { + "key": "user-account.user_id", + "object": "src_user" + }, + { + "key": "process.creator_user_ref", + "object": "src_process", + "references": "src_user" + } + ], + "userDisplayName": { + "key": "user-account.display_name", + "object": "src_user" + }, + "accountType": { + "key": "user-account.extensions.x-gcp-chronicle-user-ext.type", + "object": "src_user" + }, + "windowsSid": { + "key": "user-account.extensions.windows-account-ext.sid", + "object": "src_user" + }, + "emailAddresses": { + "key": "email-addr.value", + "object": "src_email", + "unwrap": true + } + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "src_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "src_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "src_file", + "references": "src_directory" + } + ], + "size": { + "key": "file.size", + "object": "src_file", + "transformer": "ToInteger" + }, + "md5": { + "key": "file.hashes.MD5", + "object": "src_file" + }, + "sha1": { + "key": "file.hashes.SHA-1", + "object": "src_file" + }, + "sha256": { + "key": "file.hashes.SHA-256", + "object": "src_file" + }, + "lastModificationTime": { + "key": "file.mtime", + "object": "src_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.mime_type", + "object": "src_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.file_type", + "object": "src_file" + } + }, + "process": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "src_process_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "src_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "src_process_file", + "references": "src_process_directory" + }, + { + "key": "process.image_ref", + "object": "src_process", + "references": "src_process_file" + } + ], + "size": { + "key": "file.size", + "object": "src_process_file", + "transformer": "ToInteger" + }, + "md5": [ + { + "key": "file.hashes.MD5", + "object": "src_process_file" + }, + { + "key": "process.image_ref", + "object": "src_process", + "references": "src_process_file" + } + ], + "sha1": { + "key": "file.hashes.SHA-1", + "object": "src_process_file" + }, + "sha256": [ + { + "key": "file.hashes.SHA-256", + "object": "src_process_file" + }, + { + "key": "process.image_ref", + "object": "src_process", + "references": "src_process_file" + } + ], + "lastModificationTime": { + "key": "file.mtime", + "object": "src_process_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.mime_type", + "object": "src_process_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.file_type", + "object": "src_process_file" + } + }, + "commandLine": { + "key": "process.command_line", + "object": "src_process" + }, + "pid": { + "key": "process.pid", + "object": "src_process", + "transformer": "ToInteger" + } + }, + "resource": { + "resourceType": { + "key": "x-gcp-chronicle-resource.resource_type", + "object": "src_resource" + }, + "resourceSubtype": { + "key": "x-gcp-chronicle-resource.resource_subtype", + "object": "src_resource" + }, + "name": { + "key": "x-gcp-chronicle-resource.name", + "object": "src_resource" + }, + "attribute": { + "cloud": { + "availabilityZone": { + "key": "x-gcp-chronicle-resource.availability_zone", + "object": "src_resource" + }, + "environment": { + "key": "x-gcp-chronicle-resource.environment", + "object": "src_resource" + } + } + } + } + }, + "target": { + "ip": [ + { + "key": "ipv4-addr.value", + "object": "dst_ip", + "unwrap": true, + "transformer": "FilterIPv4List" + }, + { + "key": "ipv6-addr.value", + "object": "dst_ip", + "unwrap": true, + "transformer": "FilterIPv6List" + }, + { + "key": "network-traffic.dst_ref", + "object": "nt", + "references": "dst_ip" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + }, + { + "key": "x-ibm-finding.dst_ip_ref", + "object": "security_result", + "references": "dst_ip" + }, + { + "key": "x-oca-event.ip_refs", + "object": "event", + "references": [ + "dst_ip" + ], + "group": true + } + ], + "port": [ + { + "key": "network-traffic.dst_port", + "object": "nt", + "transformer": "ToInteger" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + } + ], + "mac": [ + { + "key": "mac-addr.value", + "object": "dst_mac", + "unwrap": true + }, + { + "key": "ipv4-addr.resolves_to_refs", + "object": "dst_ip", + "references": [ + "dst_mac" + ] + }, + { + "key": "ipv6-addr.resolves_to_refs", + "object": "dst_ip", + "references": [ + "dst_mac" + ] + } + ], + "url": { + "key": "url.value", + "object": "dst_url", + "transformer": "ValidateUrl" + }, + "domain": { + "name": { + "key": "domain-name.value", + "object": "dst_domain" + }, + "status": { + "key": "domain-name.extensions.x-gcp-chronicle-domain-ext.status", + "object": "dst_domain" + } + }, + "registry": { + "registryKey": [ + { + "key": "windows-registry-key.key", + "object": "target_registry" + }, + { + "key": "x-oca-event.registry_ref", + "object": "event", + "references": "target_registry" + } + ], + "registryValues": { + "key": "windows-registry-key.values", + "object": "target_registry", + "transformer": "FormatToStixRegistryValue" + } + }, + "application": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.target_appservice", + "object": "event" + }, + "hostname": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.target_hostname", + "object": "event" + }, + "location": { + "name": [ + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.target_location", + "object": "event" + } + ] + }, + "asset": { + "platformSoftware": { + "platform": [ + { + "key": "software.name", + "object": "target_os_software" + }, + { + "key": "x-ibm-finding.dst_os_ref", + "object": "security_result", + "references": "target_os_software" + } + ], + "platformVersion": { + "key": "software.version", + "object": "target_os_software" + } + }, + "software": { + "name": [ + { + "key": "software.name", + "object": "target_software" + }, + { + "key": "x-ibm-finding.dst_application_ref", + "object": "security_result", + "references": "target_software" + } + ], + "version": { + "key": "software.version", + "object": "target_software" + } + } + }, + "user": { + "userid": [ + { + "key": "user-account.user_id", + "object": "dst_user" + }, + { + "key": "process.creator_user_ref", + "object": "target_process", + "references": "dst_user" + } + ], + "userDisplayName": { + "key": "user-account.display_name", + "object": "dst_user" + }, + "accountType": { + "key": "user-account.extensions.x-gcp-chronicle-user-ext.type", + "object": "dst_user" + }, + "windowsSid": { + "key": "user-account.extensions.windows-account-ext.sid", + "object": "dst_user" + }, + "emailAddresses": [ + { + "key": "email-addr.value", + "object": "target_email", + "unwrap": true + } + ] + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "target_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "target_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "target_file", + "references": "target_directory" + } + ], + "size": { + "key": "file.size", + "object": "target_file", + "transformer": "ToInteger" + }, + "md5": { + "key": "file.hashes.MD5", + "object": "target_file" + }, + "sha1": { + "key": "file.hashes.SHA-1", + "object": "target_file" + }, + "sha256": { + "key": "file.hashes.SHA-256", + "object": "target_file" + }, + "lastModificationTime": { + "key": "file.mtime", + "object": "target_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.mime_type", + "object": "target_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.file_type", + "object": "target_file" + } + }, + "process": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "target_process_file", + "transformer": "ToFileName" + }, + { + "key": "process.image_ref", + "object": "target_process", + "references": "target_process_file" + }, + { + "key": "directory.path", + "object": "target_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "target_process_file", + "references": "target_process_directory" + } + ], + "size": { + "key": "file.size", + "object": "target_process_file", + "transformer": "ToInteger" + }, + "md5": [ + { + "key": "file.hashes.MD5", + "object": "target_process_file" + }, + { + "key": "process.image_ref", + "object": "target_process", + "references": "target_process_file" + } + ], + "sha1": { + "key": "file.hashes.SHA-1", + "object": "target_process_file" + }, + "sha256": [ + { + "key": "file.hashes.SHA-256", + "object": "target_process_file" + }, + { + "key": "process.image_ref", + "object": "target_process", + "references": "target_process_file" + } + ], + "lastModificationTime": { + "key": "file.mtime", + "object": "target_process_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.mime_type", + "object": "target_process_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.file_type", + "object": "target_process_file" + } + }, + "commandLine": { + "key": "process.command_line", + "object": "target_process" + }, + "parentProcess": { + "commandLine": { + "key": "process.command_line", + "object": "target_parent_process" + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "target_parent_process_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "target_parent_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "target_parent_process_file", + "references": "target_parent_process_directory" + }, + { + "key": "process.image_ref", + "object": "target_parent_process", + "references": "target_parent_process_file" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "target_parent_process", + "transformer": "ToInteger" + }, + { + "key": "process.parent_ref", + "object": "target_process", + "references": "target_parent_process" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "target_process", + "transformer": "ToInteger" + }, + { + "key": "x-oca-event.cross_process_target_ref", + "object": "event", + "references": "target_process" + } + ] + }, + "resource": { + "resourceType": { + "key": "x-gcp-chronicle-resource.resource_type", + "object": "target_resource" + }, + "resourceSubtype": { + "key": "x-gcp-chronicle-resource.resource_subtype", + "object": "target_resource" + }, + "name": [ + { + "key": "x-gcp-chronicle-resource.name", + "object": "target_resource" + }, + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.target_resource_ref", + "object": "event", + "references": "target_resource" + } + ], + "attribute": { + "cloud": { + "availabilityZone": { + "key": "x-gcp-chronicle-resource.availability_zone", + "object": "target_resource" + }, + "environment": { + "key": "x-gcp-chronicle-resource.environment", + "object": "target_resource" + } + } + } + } + }, + "network": { + "ipProtocol": { + "key": "network-traffic.protocols", + "object": "nt", + "group": true, + "transformer": "ToLowercaseArray" + }, + "applicationProtocol": { + "key": "network-traffic.protocols", + "object": "nt", + "group": true, + "transformer": "ToLowercaseArray" + }, + "sentBytes": { + "key": "network-traffic.src_byte_count", + "object": "nt", + "transformer": "ToInteger" + }, + "receivedBytes": { + "key": "network-traffic.dst_byte_count", + "object": "nt", + "transformer": "ToInteger" + }, + "dnsDomain": { + "key": "domain-name.value", + "object": "domain" + }, + "sessionDuration": { + "key": "network-traffic.extensions.x-gcp-chronicle-network-ext.session_duration", + "object": "nt" + }, + "sessionId": { + "key": "network-traffic.extensions.x-gcp-chronicle-network-ext.session_id", + "object": "nt" + }, + "direction": { + "key": "network-traffic.extensions.x-gcp-chronicle-network-ext.direction", + "object": "nt" + }, + "ftp": { + "command": { + "key": "network-traffic.extensions.ftp-ext.command", + "object": "nt" + } + }, + "dns": { + "id": { + "key": "network-traffic.extensions.dns-ext.query_id", + "object": "nt" + }, + "opcode": { + "key": "network-traffic.extensions.dns-ext.opcode", + "object": "nt" + }, + "responseCode": { + "key": "network-traffic.extensions.dns-ext.response_code", + "object": "nt" + }, + "questions": { + "key": "network-traffic.extensions.dns-ext.questions", + "object": "nt", + "group": "true" + } + }, + "dhcp": { + "clientHostname": { + "key": "network-traffic.extensions.dhcp-ext.client_hostname", + "object": "nt" + }, + "opcode": { + "key": "network-traffic.extensions.dhcp-ext.opcode", + "object": "nt" + }, + "sname": { + "key": "network-traffic.extensions.dhcp-ext.server_name", + "object": "nt" + }, + "transactionId": { + "key": "network-traffic.extensions.dhcp-ext.transaction_id", + "object": "nt" + }, + "type": { + "key": "network-traffic.extensions.dhcp-ext.message_type", + "object": "nt" + } + }, + "http": { + "method": { + "key": "network-traffic.extensions.http-ext.request_method", + "object": "nt" + }, + "responseCode": { + "key": "network-traffic.extensions.http-ext.response_code", + "object": "nt" + }, + "userAgent": { + "key": "network-traffic.extensions.http-ext.user_agent", + "object": "nt" + }, + "referralUrl": { + "key": "url.value", + "object": "url", + "transformer": "ValidateUrl" + } + }, + "tls": { + "cipher": { + "key": "network-traffic.extensions.tls-ext.cipher", + "object": "nt" + }, + "version": { + "key": "network-traffic.extensions.tls-ext.version", + "object": "nt" + }, + "versionProtocol": { + "key": "network-traffic.extensions.tls-ext.version_protocol", + "object": "nt" + }, + "curve": { + "key": "network-traffic.extensions.tls-ext.elliptical_curve", + "object": "nt" + }, + "nextProtocol": { + "key": "network-traffic.extensions.tls-ext.next_protocol", + "object": "nt" + }, + "server": { + "ja3s": { + "key": "network-traffic.extensions.tls-ext.server_ja3_hash", + "object": "nt" + }, + "certificate": { + "version": [ + { + "key": "x509-certificate.version", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + } + ], + "serial": [ + { + "key": "x509-certificate.serial_number", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + } + ], + "issuer": [ + { + "key": "x509-certificate.issuer", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + } + ], + "notBefore": { + "key": "x509-certificate.validity_not_before", + "object": "server_cert" + }, + "notAfter": { + "key": "x509-certificate.validity_not_after", + "object": "server_cert" + }, + "subject": [ + { + "key": "x509-certificate.subject", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + }], + "md5": { + "key": "x509-certificate.hashes.MD5", + "object": "server_cert" + }, + "sha1": { + "key": "x509-certificate.hashes.SHA-1", + "object": "server_cert" + }, + "sha256": { + "key": "x509-certificate.hashes.SHA-256", + "object": "server_cert" + } + } + }, + "client": { + "ja3": { + "key": "network-traffic.extensions.tls-ext.client_ja3_hash", + "object": "nt" + }, + "serverName": { + "key": "network-traffic.extensions.tls-ext.server_host_name", + "object": "nt" + }, + "certificate": { + "version": [ + { + "key": "x509-certificate.version", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + + } + ], + "serial": [ + { + "key": "x509-certificate.serial_number", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + + }], + "issuer": [ + { + "key": "x509-certificate.issuer", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + + }], + "notBefore": { + "key": "x509-certificate.validity_not_before", + "object": "client_cert" + }, + "notAfter": { + "key": "x509-certificate.validity_not_after", + "object": "client_cert" + }, + "subject": [ + { + "key": "x509-certificate.subject", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + } + ], + "md5": { + "key": "x509-certificate.hashes.MD5", + "object": "client_cert" + }, + "sha1": { + "key": "x509-certificate.hashes.SHA-1", + "object": "client_cert" + }, + "sha256": { + "key": "x509-certificate.hashes.SHA-256", + "object": "client_cert" + } + } + } + }, + "smtp": { + "serverResponse": { + "key": "network-traffic.extensions.smtp-ext.server_response", + "object": "nt" + } + }, + "asn": { + "key": "autonomous-system.number", + "object": "asn", + "transformer": "ToInteger" + }, + "email": { + "subject": { + "key": "email-message.subject", + "object": "em", + "unwrap": true + }, + "isMultipart": { + "key": "email-message.is_multipart", + "object": "em" + }, + "from": [ + { + "key": "email-addr.value", + "object": "email_from", + "transformer" : "FilterValidEmail" + }, + { + "key": "email-message.from_ref", + "object": "em", + "references": "email_from" + }, + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.email_message_ref", + "object": "event", + "references": "em" + } + ], + "to": [ + { + "key": "email-addr.value", + "object": "email_to", + "unwrap": true + }, + { + "key": "email-message.to_refs", + "object": "em", + "references": [ + "email_to" + ] + } + ], + "cc": [ + { + "key": "email-addr.value", + "object": "email_cc", + "unwrap": true + }, + { + "key": "email-message.cc_refs", + "object": "em", + "references": [ + "email_cc" + ] + } + ], + "bcc": [ + { + "key": "email-addr.value", + "object": "email_bcc", + "unwrap": true + }, + { + "key": "email-message.bcc_refs", + "object": "em", + "references": [ + "email_bcc" + ] + } + ] + } + }, + "securityResult": { + "about": { + "email": { + "key": "email-addr.value", + "object": "security_email", + "transformer" : "FilterValidEmail" + }, + "url": [ + { + "key": "url.value", + "object": "security_url", + "transformer": "ValidateUrl" + }, + { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.url_ref", + "object": "security_result", + "references": "security_url" + } + ] + }, + "findingType": { + "key": "x-ibm-finding.finding_type", + "object": "security_result" + }, + "threatName": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.threat_name", + "object": "security_result" + }, + "ruleName": { + "key": "x-ibm-finding.rule_names", + "object": "security_result", + "transformer": "ValueToList" + }, + "alertState": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.alert_state", + "object": "security_result" + }, + "severity": { + "key": "x-ibm-finding.severity", + "object": "security_result" + }, + "category": { + "key": "x-ibm-finding.finding_type", + "object": "security_result" + }, + "summary": { + "key": "x-ibm-finding.name", + "object": "security_result" + }, + "threatId": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.threat_id", + "object": "security_result" + }, + "threatStatus": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.threat_status", + "object": "security_result" + }, + "description": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.description", + "object": "security_result" + }, + "action": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.actions_taken", + "object": "security_result" + } + }, + "metadata": { + "eventType": { + "key": "x-oca-event.action", + "object": "event" + }, + "productLogId": { + "key": "x-oca-event.code", + "object": "event" + }, + "eventTimestamp": [ + { + "key": "x-oca-event.created", + "object": "event" + }, + { + "key": "first_observed", + "cybox": false + }, + { + "key": "last_observed", + "cybox": false + } + ], + "description": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.description", + "object": "event" + }, + "productName": { + "key": "x-oca-event.agent", + "object": "event" + }, + "vendorName": { + "key": "x-oca-event.provider", + "object": "event" + }, + "productEventType": { + "key": "x-oca-event.outcome", + "object": "event" + } + }, + "about": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "about_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "about_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "about_file", + "references": "about_directory" + }, + { + "key": "email-message.extensions.x-gcp-chronicle-email-message-ext.file_ref", + "object": "em", + "references": "about_file" + } + ], + "size": { + "key": "file.size", + "object": "about_file", + "transformer": "ToInteger" + }, + "md5": { + "key": "file.hashes.MD5", + "object": "about_file" + }, + "sha1": { + "key": "file.hashes.SHA-1", + "object": "about_file" + }, + "sha256": { + "key": "file.hashes.SHA-256", + "object": "about_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.mime_type", + "object": "about_file" + }, + "lastModificationTime": { + "key": "file.modified", + "object": "about_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file-ext.file_type", + "object": "about_file" + } + } + }, + "additional": { + "detectorId": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.aws_detector_id", + "object": "security_result" + }, + "archived": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.aws_archived", + "object": "security_result" + }, + "count": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.aws_count", + "object": "security_result" + }, + "imageId": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.aws_image_id", + "object": "security_result" + }, + "resourceRole": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result-ext.aws_resource_role", + "object": "security_result" + } + } + } +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/json/to_stix_map.json b/stix_shifter_modules/gcp_chronicle/stix_translation/json/to_stix_map.json new file mode 100644 index 000000000..526dd7de7 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/json/to_stix_map.json @@ -0,0 +1,1702 @@ +{ + "event": { + "principal": { + "ip": [ + { + "key": "ipv4-addr.value", + "object": "principal_ip", + "unwrap": true, + "transformer": "FilterIPv4List" + }, + { + "key": "ipv6-addr.value", + "object": "principal_ip", + "unwrap": true, + "transformer": "FilterIPv6List" + }, + { + "key": "network-traffic.src_ref", + "object": "nt", + "references": "principal_ip" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + }, + { + "key": "x-ibm-finding.src_ip_ref", + "object": "security_result", + "references": "principal_ip" + }, + { + "key": "x-oca-event.ip_refs", + "object": "event", + "references": [ + "principal_ip" + ], + "group": true + }, + { + "key": "x-oca-asset.ip_refs", + "object": "principal_asset", + "references": [ + "principal_ip" + ] + } + ], + "port": [ + { + "key": "network-traffic.src_port", + "object": "nt", + "transformer": "ToInteger" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + } + ], + "mac": [ + { + "key": "mac-addr.value", + "object": "principal_mac", + "unwrap": true + }, + { + "key": "x-oca-asset.mac_refs", + "object": "principal_asset", + "references": [ + "principal_mac" + ] + }, + { + "key": "ipv4-addr.resolves_to_refs", + "object": "principal_ip", + "references": [ + "principal_mac" + ] + }, + { + "key": "ipv6-addr.resolves_to_refs", + "object": "principal_ip", + "references": [ + "principal_mac" + ] + } + ], + "url": [ + { + "key": "url.value", + "object": "principal_url", + "transformer": "ValidateUrl" + }, + { + "key": "x-oca-event.url_ref", + "object": "event", + "references": "principal_url" + } + ], + "domain": { + "name": [ + { + "key": "domain-name.value", + "object": "principal_domain" + }, + { + "key": "x-oca-event.domain_ref", + "object": "event", + "references": "principal_domain" + } + ], + "status": { + "key": "domain-name.extensions.x-gcp-chronicle-domain.status", + "object": "principal_domain" + } + }, + "application": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.src_appservice", + "object": "event" + }, + "hostname": [ + { + "key": "x-oca-asset.hostname", + "object": "principal_asset" + }, + { + "key": "x-oca-event.host_ref", + "object": "event", + "references": "principal_asset" + } + ], + "assetId": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.asset_id", + "object": "principal_asset" + }, + "location": { + "name": [ + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.src_location", + "object": "event" + } + ], + "city": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.city", + "object": "principal_asset" + }, + "countryOrRegion": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.country_or_region", + "object": "principal_asset" + } + }, + "asset": { + "category": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.category", + "object": "principal_asset" + }, + "type": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.type", + "object": "principal_asset" + }, + "platformSoftware": { + "platform": [ + { + "key": "software.name", + "object": "principal_os_software" + }, + { + "key": "x-ibm-finding.src_os_ref", + "object": "security_result", + "references": "principal_os_software" + } + ], + "platformVersion": { + "key": "software.version", + "object": "principal_os_software" + } + }, + "hardware": { + "cpuPlatform": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.hw_cpu_platform", + "object": "principal_asset" + }, + "manufacturer": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.hw_manufacturer", + "object": "principal_asset" + }, + "serialNumber": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.hw_serial_number", + "object": "principal_asset" + } + }, + "attribute": { + "cloud": { + "environment": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.cloud_environment", + "object": "principal_asset" + }, + "availabilityZone": { + "key": "x-oca-asset.extensions.x-gcp-chronicle-asset.cloud_availability_zone", + "object": "principal_asset" + } + } + }, + "software": { + "name": [ + { + "key": "software.name", + "object": "principal_software" + }, + { + "key": "x-ibm-finding.src_application_ref", + "object": "security_result", + "references": "principal_software" + } + ], + "version": { + "key": "software.version", + "object": "principal_software" + } + } + }, + "user": { + "userid": [ + { + "key": "user-account.user_id", + "object": "principal_user" + }, + { + "key": "process.creator_user_ref", + "object": "principal_process", + "references": "principal_user" + }, + { + "key": "x-oca-event.user_ref", + "object": "event", + "references": "principal_user" + } + ], + "userDisplayName": { + "key": "user-account.display_name", + "object": "principal_user" + }, + "accountType": { + "key": "user-account.extensions.x-gcp-chronicle-user.type", + "object": "principal_user" + }, + "windowsSid": { + "key": "user-account.extensions.windows-account-ext.sid", + "object": "principal_user" + }, + "emailAddresses": [ + { + "key": "email-addr.value", + "object": "principal_email", + "unwrap": true + } + ] + }, + "process": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "principal_process_file", + "transformer": "ToFileName" + }, + { + "key": "process.name", + "object": "principal_process", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "principal_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "principal_process_file", + "references": "principal_process_directory" + }, + { + "key": "process.binary_ref", + "object": "principal_process", + "references": "principal_process_file" + }, + { + "key": "x-oca-event.file_ref", + "object": "event", + "references": "principal_process_file" + }, + { + "key": "x-oca-event.process_ref", + "object": "event", + "references": "principal_process" + } + ], + "size": { + "key": "file.size", + "object": "principal_process_file", + "transformer": "ToInteger" + }, + "md5": [ + { + "key": "file.hashes.MD5", + "object": "principal_process_file" + }, + { + "key": "process.binary_ref", + "object": "principal_process", + "references": "principal_process_file" + } + ], + "sha1": { + "key": "file.hashes.SHA-1", + "object": "principal_process_file" + }, + "sha256": [ + { + "key": "file.hashes.SHA-256", + "object": "principal_process_file" + }, + { + "key": "process.binary_ref", + "object": "principal_process", + "references": "principal_process_file" + } + ], + "lastModificationTime": { + "key": "file.modified", + "object": "principal_process_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file.mime_type", + "object": "principal_process_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file.file_type", + "object": "principal_process_file" + } + }, + "commandLine": { + "key": "process.command_line", + "object": "principal_process" + }, + "parentProcess": { + "commandLine": { + "key": "process.command_line", + "object": "principal_parent_process" + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "principal_parent_process_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "principal_parent_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "principal_parent_process_file", + "references": "principal_parent_process_directory" + }, + { + "key": "process.name", + "object": "principal_parent_process", + "transformer": "ToFileName" + }, + { + "key": "process.binary_ref", + "object": "principal_parent_process", + "references": "principal_parent_process_file" + }, + { + "key": "process.parent_ref", + "object": "principal_process", + "references": "principal_parent_process" + }, + { + "key": "x-oca-event.parent_process_ref", + "object": "event", + "references": "principal_parent_process" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "principal_parent_process", + "transformer": "ToInteger" + }, + { + "key": "process.parent_ref", + "object": "principal_process", + "references": "principal_parent_process" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "principal_process", + "transformer": "ToInteger" + } + ] + }, + "resource": { + "resourceType": { + "key": "x-gcp-chronicle-resource.resource_type", + "object": "principal_resource" + }, + "resourceSubtype": { + "key": "x-gcp-chronicle-resource.resource_subtype", + "object": "principal_resource" + }, + "name": [ + { + "key": "x-gcp-chronicle-resource.name", + "object": "principal_resource" + }, + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event-ext.src_resource_ref", + "object": "event", + "references": "principal_resource" + } + ], + "attribute": { + "cloud": { + "availabilityZone": { + "key": "x-gcp-chronicle-resource.availability_zone", + "object": "principal_resource" + }, + "environment": { + "key": "x-gcp-chronicle-resource.environment", + "object": "principal_resource" + } + } + } + } + }, + "src": { + "ip": [ + { + "key": "ipv4-addr.value", + "object": "src_ip", + "unwrap": true, + "transformer": "FilterIPv4List" + }, + { + "key": "ipv6-addr.value", + "object": "src_ip", + "unwrap": true, + "transformer": "FilterIPv6List" + }, + { + "key": "network-traffic.src_ref", + "object": "nt", + "references": "src_ip" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + }, + { + "key": "x-oca-event.ip_refs", + "object": "event", + "references": [ + "src_ip" + ], + "group": true + } + ], + "port": { + "key": "network-traffic.src_port", + "object": "nt", + "transformer": "ToInteger" + }, + "mac": [ + { + "key": "mac-addr.value", + "object": "src_mac", + "unwrap": true + }, + { + "key": "ipv4-addr.resolves_to_refs", + "object": "src_ip", + "references": [ + "src_mac" + ] + }, + { + "key": "ipv6-addr.resolves_to_refs", + "object": "src_ip", + "references": [ + "src_mac" + ] + } + ], + "url": { + "key": "url.value", + "object": "src_url", + "transformer": "ValidateUrl" + }, + "domain": { + "name": { + "key": "domain-name.value", + "object": "src_domain" + }, + "status": { + "key": "domain-name.extensions.x-gcp-chronicle-domain.status", + "object": "src_domain" + } + }, + "registry": { + "registryKey": [ + { + "key": "windows-registry-key.key", + "object": "src_registry" + } + ], + "registryValues": { + "key": "windows-registry-key.values", + "object": "src_registry", + "transformer": "FormatToStixRegistryValue" + } + }, + "asset": { + "platformSoftware": { + "platform": { + "key": "software.name", + "object": "src_os_software" + }, + "platformVersion": { + "key": "software.version", + "object": "src_os_software" + } + }, + "software": { + "name": { + "key": "software.name", + "object": "src_software" + }, + "version": { + "key": "software.version", + "object": "src_software" + } + } + }, + "user": { + "userid": [ + { + "key": "user-account.user_id", + "object": "src_user" + }, + { + "key": "process.creator_user_ref", + "object": "src_process", + "references": "src_user" + } + ], + "userDisplayName": { + "key": "user-account.display_name", + "object": "src_user" + }, + "accountType": { + "key": "user-account.extensions.x-gcp-chronicle-user.type", + "object": "src_user" + }, + "windowsSid": { + "key": "user-account.extensions.windows-account-ext.sid", + "object": "src_user" + }, + "emailAddresses": { + "key": "email-addr.value", + "object": "src_email", + "unwrap": true + } + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "src_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "src_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "src_file", + "references": "src_directory" + } + ], + "size": { + "key": "file.size", + "object": "src_file", + "transformer": "ToInteger" + }, + "md5": { + "key": "file.hashes.MD5", + "object": "src_file" + }, + "sha1": { + "key": "file.hashes.SHA-1", + "object": "src_file" + }, + "sha256": { + "key": "file.hashes.SHA-256", + "object": "src_file" + }, + "lastModificationTime": { + "key": "file.modified", + "object": "src_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file.mime_type", + "object": "src_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file.file_type", + "object": "src_file" + } + }, + "process": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "src_process_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "src_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "src_process_file", + "references": "src_process_directory" + }, + { + "key": "process.name", + "object": "src_process", + "transformer": "ToFileName" + }, + { + "key": "process.binary_ref", + "object": "src_process", + "references": "src_process_file" + } + ], + "size": { + "key": "file.size", + "object": "src_process_file", + "transformer": "ToInteger" + }, + "md5": [ + { + "key": "file.hashes.MD5", + "object": "src_process_file" + }, + { + "key": "process.binary_ref", + "object": "src_process", + "references": "src_process_file" + } + ], + "sha1": { + "key": "file.hashes.SHA-1", + "object": "src_process_file" + }, + "sha256": [ + { + "key": "file.hashes.SHA-256", + "object": "src_process_file" + }, + { + "key": "process.binary_ref", + "object": "src_process", + "references": "src_process_file" + } + ], + "lastModificationTime": { + "key": "file.modified", + "object": "src_process_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file.mime_type", + "object": "src_process_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file.file_type", + "object": "src_process_file" + } + }, + "commandLine": { + "key": "process.command_line", + "object": "src_process" + }, + "pid": { + "key": "process.pid", + "object": "src_process", + "transformer": "ToInteger" + } + }, + "resource": { + "resourceType": { + "key": "x-gcp-chronicle-resource.resource_type", + "object": "src_resource" + }, + "resourceSubtype": { + "key": "x-gcp-chronicle-resource.resource_subtype", + "object": "src_resource" + }, + "name": { + "key": "x-gcp-chronicle-resource.name", + "object": "src_resource" + }, + "attribute": { + "cloud": { + "availabilityZone": { + "key": "x-gcp-chronicle-resource.availability_zone", + "object": "src_resource" + }, + "environment": { + "key": "x-gcp-chronicle-resource.environment", + "object": "src_resource" + } + } + } + } + }, + "target": { + "ip": [ + { + "key": "ipv4-addr.value", + "object": "dst_ip", + "unwrap": true, + "transformer": "FilterIPv4List" + }, + { + "key": "ipv6-addr.value", + "object": "dst_ip", + "unwrap": true, + "transformer": "FilterIPv6List" + }, + { + "key": "network-traffic.dst_ref", + "object": "nt", + "references": "dst_ip" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + }, + { + "key": "x-ibm-finding.dst_ip_ref", + "object": "security_result", + "references": "dst_ip" + }, + { + "key": "x-oca-event.ip_refs", + "object": "event", + "references": [ + "dst_ip" + ], + "group": true + } + ], + "port": [ + { + "key": "network-traffic.dst_port", + "object": "nt", + "transformer": "ToInteger" + }, + { + "key": "x-oca-event.network_ref", + "object": "event", + "references": "nt" + } + ], + "mac": [ + { + "key": "mac-addr.value", + "object": "dst_mac", + "unwrap": true + }, + { + "key": "ipv4-addr.resolves_to_refs", + "object": "dst_ip", + "references": [ + "dst_mac" + ] + }, + { + "key": "ipv6-addr.resolves_to_refs", + "object": "dst_ip", + "references": [ + "dst_mac" + ] + } + ], + "url": { + "key": "url.value", + "object": "dst_url", + "transformer": "ValidateUrl" + }, + "domain": { + "name": { + "key": "domain-name.value", + "object": "dst_domain" + }, + "status": { + "key": "domain-name.extensions.x-gcp-chronicle-domain.status", + "object": "dst_domain" + } + }, + "registry": { + "registryKey": [ + { + "key": "windows-registry-key.key", + "object": "target_registry" + }, + { + "key": "x-oca-event.registry_ref", + "object": "event", + "references": "target_registry" + } + ], + "registryValues": { + "key": "windows-registry-key.values", + "object": "target_registry", + "transformer": "FormatToStixRegistryValue" + } + }, + "application": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.target_appservice", + "object": "event" + }, + "hostname": [ + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.target_hostname", + "object": "event" + } + ], + "location": { + "name": [ + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.target_location", + "object": "event" + } + ] + }, + "asset": { + "platformSoftware": { + "platform": [ + { + "key": "software.name", + "object": "target_os_software" + }, + { + "key": "x-ibm-finding.dst_os_ref", + "object": "security_result", + "references": "target_os_software" + } + ], + "platformVersion": { + "key": "software.version", + "object": "target_os_software" + } + }, + "software": { + "name": [ + { + "key": "software.name", + "object": "target_software" + }, + { + "key": "x-ibm-finding.dst_application_ref", + "object": "security_result", + "references": "target_software" + } + ], + "version": { + "key": "software.version", + "object": "target_software" + } + } + }, + "user": { + "userid": [ + { + "key": "user-account.user_id", + "object": "dst_user" + }, + { + "key": "process.creator_user_ref", + "object": "target_process", + "references": "dst_user" + } + ], + "userDisplayName": { + "key": "user-account.display_name", + "object": "dst_user" + }, + "accountType": { + "key": "user-account.extensions.x-gcp-chronicle-user.type", + "object": "dst_user" + }, + "windowsSid": { + "key": "user-account.extensions.windows-account-ext.sid", + "object": "dst_user" + }, + "emailAddresses": [ + { + "key": "email-addr.value", + "object": "target_email", + "unwrap": true + } + ] + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "target_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "target_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "target_file", + "references": "target_directory" + } + ], + "size": { + "key": "file.size", + "object": "target_file", + "transformer": "ToInteger" + }, + "md5": { + "key": "file.hashes.MD5", + "object": "target_file" + }, + "sha1": { + "key": "file.hashes.SHA-1", + "object": "target_file" + }, + "sha256": { + "key": "file.hashes.SHA-256", + "object": "target_file" + }, + "lastModificationTime": { + "key": "file.modified", + "object": "target_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file.mime_type", + "object": "target_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file.file_type", + "object": "target_file" + } + }, + "process": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "target_process_file", + "transformer": "ToFileName" + }, + { + "key": "process.name", + "object": "target_process", + "transformer": "ToFileName" + }, + { + "key": "process.binary_ref", + "object": "target_process", + "references": "target_process_file" + }, + { + "key": "directory.path", + "object": "target_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "target_process_file", + "references": "target_process_directory" + }, + { + "key": "x-oca-event.cross_process_target_ref", + "object": "event", + "references": "target_process" + } + ], + "size": { + "key": "file.size", + "object": "target_process_file", + "transformer": "ToInteger" + }, + "md5": [ + { + "key": "file.hashes.MD5", + "object": "target_process_file" + }, + { + "key": "process.binary_ref", + "object": "target_process", + "references": "target_process_file" + } + ], + "sha1": { + "key": "file.hashes.SHA-1", + "object": "target_process_file" + }, + "sha256": [ + { + "key": "file.hashes.SHA-256", + "object": "target_process_file" + }, + { + "key": "process.binary_ref", + "object": "target_process", + "references": "target_process_file" + } + ], + "lastModificationTime": { + "key": "file.modified", + "object": "target_process_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file.mime_type", + "object": "target_process_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file.file_type", + "object": "target_process_file" + } + }, + "commandLine": { + "key": "process.command_line", + "object": "target_process" + }, + "parentProcess": { + "commandLine": { + "key": "process.command_line", + "object": "target_parent_process" + }, + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "target_parent_process_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "target_parent_process_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "target_parent_process_file", + "references": "target_parent_process_directory" + }, + { + "key": "process.name", + "object": "target_parent_process", + "transformer": "ToFileName" + }, + { + "key": "process.binary_ref", + "object": "target_parent_process", + "references": "target_parent_process_file" + }, + { + "key": "process.parent_ref", + "object": "target_process", + "references": "target_parent_process" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "target_parent_process", + "transformer": "ToInteger" + }, + { + "key": "process.parent_ref", + "object": "target_process", + "references": "target_parent_process" + } + ] + }, + "pid": [ + { + "key": "process.pid", + "object": "target_process", + "transformer": "ToInteger" + } + ] + }, + "resource": { + "resourceType": { + "key": "x-gcp-chronicle-resource.resource_type", + "object": "target_resource" + }, + "resourceSubtype": { + "key": "x-gcp-chronicle-resource.resource_subtype", + "object": "target_resource" + }, + "name": [ + { + "key": "x-gcp-chronicle-resource.name", + "object": "target_resource" + }, + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.target_resource_ref", + "object": "event", + "references": "target_resource" + } + ], + "attribute": { + "cloud": { + "availabilityZone": { + "key": "x-gcp-chronicle-resource.availability_zone", + "object": "target_resource" + }, + "environment": { + "key": "x-gcp-chronicle-resource.environment", + "object": "target_resource" + } + } + } + } + }, + "network": { + "ipProtocol": { + "key": "network-traffic.protocols", + "object": "nt", + "group": true, + "transformer": "ToLowercaseArray" + }, + "applicationProtocol": { + "key": "network-traffic.protocols", + "object": "nt", + "group": true, + "transformer": "ToLowercaseArray" + }, + "sentBytes": { + "key": "network-traffic.src_byte_count", + "object": "nt", + "transformer": "ToInteger" + }, + "receivedBytes": { + "key": "network-traffic.dst_byte_count", + "object": "nt", + "transformer": "ToInteger" + }, + "dnsDomain": { + "key": "domain-name.value", + "object": "domain" + }, + "sessionDuration": { + "key": "network-traffic.extensions.x-gcp-chronicle-network.session_duration", + "object": "nt" + }, + "sessionId": { + "key": "network-traffic.extensions.x-gcp-chronicle-network.session_id", + "object": "nt" + }, + "direction": { + "key": "network-traffic.extensions.x-gcp-chronicle-network.direction", + "object": "nt" + }, + "ftp": { + "command": { + "key": "network-traffic.extensions.ftp-ext.command", + "object": "nt" + } + }, + "dns": { + "id": { + "key": "network-traffic.extensions.dns-ext.query_id", + "object": "nt" + }, + "opcode": { + "key": "network-traffic.extensions.dns-ext.opcode", + "object": "nt" + }, + "responseCode": { + "key": "network-traffic.extensions.dns-ext.response_code", + "object": "nt" + }, + "questions": { + "key": "network-traffic.extensions.dns-ext.questions", + "object": "nt", + "group": "true" + } + }, + "dhcp": { + "clientHostname": { + "key": "network-traffic.extensions.dhcp-ext.client_hostname", + "object": "nt" + }, + "opcode": { + "key": "network-traffic.extensions.dhcp-ext.opcode", + "object": "nt" + }, + "sname": { + "key": "network-traffic.extensions.dhcp-ext.server_name", + "object": "nt" + }, + "transactionId": { + "key": "network-traffic.extensions.dhcp-ext.transaction_id", + "object": "nt" + }, + "type": { + "key": "network-traffic.extensions.dhcp-ext.message_type", + "object": "nt" + } + }, + "http": { + "method": { + "key": "network-traffic.extensions.http-ext.request_method", + "object": "nt" + }, + "responseCode": { + "key": "network-traffic.extensions.http-ext.response_code", + "object": "nt" + }, + "userAgent": { + "key": "network-traffic.extensions.http-ext.user_agent", + "object": "nt" + }, + "referralUrl": { + "key": "url.value", + "object": "url", + "transformer": "ValidateUrl" + } + }, + "tls": { + "cipher": { + "key": "network-traffic.extensions.tls-ext.cipher", + "object": "nt" + }, + "version": { + "key": "network-traffic.extensions.tls-ext.version", + "object": "nt" + }, + "versionProtocol": { + "key": "network-traffic.extensions.tls-ext.version_protocol", + "object": "nt" + }, + "curve": { + "key": "network-traffic.extensions.tls-ext.elliptical_curve", + "object": "nt" + }, + "nextProtocol": { + "key": "network-traffic.extensions.tls-ext.next_protocol", + "object": "nt" + }, + "server": { + "ja3s": { + "key": "network-traffic.extensions.tls-ext.server_ja3_hash", + "object": "nt" + }, + "certificate": { + "version": [ + { + "key": "x509-certificate.version", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + } + ], + "serial": [ + { + "key": "x509-certificate.serial_number", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + } + ], + "issuer": [ + { + "key": "x509-certificate.issuer", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + } + ], + "notBefore": { + "key": "x509-certificate.validity_not_before", + "object": "server_cert" + }, + "notAfter": { + "key": "x509-certificate.validity_not_after", + "object": "server_cert" + }, + "subject": [ + { + "key": "x509-certificate.subject", + "object": "server_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.server_certificate_ref", + "object": "nt", + "references": "server_cert" + + }], + "md5": { + "key": "x509-certificate.hashes.MD5", + "object": "server_cert" + }, + "sha1": { + "key": "x509-certificate.hashes.SHA-1", + "object": "server_cert" + }, + "sha256": { + "key": "x509-certificate.hashes.SHA-256", + "object": "server_cert" + } + } + }, + "client": { + "ja3": { + "key": "network-traffic.extensions.tls-ext.client_ja3_hash", + "object": "nt" + }, + "serverName": { + "key": "network-traffic.extensions.tls-ext.server_host_name", + "object": "nt" + }, + "certificate": { + "version": [ + { + "key": "x509-certificate.version", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + + } + ], + "serial": [ + { + "key": "x509-certificate.serial_number", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + + }], + "issuer": [ + { + "key": "x509-certificate.issuer", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + + }], + "notBefore": { + "key": "x509-certificate.validity_not_before", + "object": "client_cert" + }, + "notAfter": { + "key": "x509-certificate.validity_not_after", + "object": "client_cert" + }, + "subject": [ + { + "key": "x509-certificate.subject", + "object": "client_cert" + }, + { + "key": "network-traffic.extensions.tls-ext.client_certificate_ref", + "object": "nt", + "references": "client_cert" + + } + ], + "md5": { + "key": "x509-certificate.hashes.MD5", + "object": "client_cert" + }, + "sha1": { + "key": "x509-certificate.hashes.SHA-1", + "object": "client_cert" + }, + "sha256": { + "key": "x509-certificate.hashes.SHA-256", + "object": "client_cert" + } + } + } + }, + "smtp": { + "serverResponse": { + "key": "network-traffic.extensions.smtp-ext.server_response", + "object": "nt" + } + }, + "asn": { + "key": "autonomous-system.number", + "object": "asn", + "transformer": "ToInteger" + }, + "email": { + "subject": { + "key": "email-message.subject", + "object": "em", + "unwrap": true + }, + "isMultipart": { + "key": "email-message.is_multipart", + "object": "em" + }, + "from": [ + { + "key": "email-addr.value", + "object": "email_from", + "transformer" : "FilterValidEmail" + }, + { + "key": "email-message.from_ref", + "object": "em", + "references": "email_from" + }, + { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.email_message_ref", + "object": "event", + "references": "em" + } + ], + "to": [ + { + "key": "email-addr.value", + "object": "email_to", + "unwrap": true + }, + { + "key": "email-message.to_refs", + "object": "em", + "references": [ + "email_to" + ] + } + ], + "cc": [ + { + "key": "email-addr.value", + "object": "email_cc", + "unwrap": true + }, + { + "key": "email-message.cc_refs", + "object": "em", + "references": [ + "email_cc" + ] + } + ], + "bcc": [ + { + "key": "email-addr.value", + "object": "email_bcc", + "unwrap": true + }, + { + "key": "email-message.bcc_refs", + "object": "em", + "references": [ + "email_bcc" + ] + } + ] + } + }, + "securityResult": { + "about": { + "email": { + "key": "email-addr.value", + "object": "security_email", + "transformer" : "FilterValidEmail" + }, + "url": [ + { + "key": "url.value", + "object": "security_url", + "transformer": "ValidateUrl" + }, + { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.url_ref", + "object": "security_result", + "references": "security_url" + } + ] + }, + "findingType": { + "key": "x-ibm-finding.finding_type", + "object": "security_result" + }, + "threatName": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.threat_name", + "object": "security_result" + }, + "ruleName": { + "key": "x-ibm-finding.rule_names", + "object": "security_result", + "transformer": "ValueToList" + }, + "alertState": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.alert_state", + "object": "security_result" + }, + "severity": { + "key": "x-ibm-finding.severity", + "object": "security_result" + }, + "category": { + "key": "x-ibm-finding.finding_type", + "object": "security_result" + }, + "summary": { + "key": "x-ibm-finding.name", + "object": "security_result" + }, + "threatId": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.threat_id", + "object": "security_result" + }, + "threatStatus": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.threat_status", + "object": "security_result" + }, + "description": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.description", + "object": "security_result" + }, + "action": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.actions_taken", + "object": "security_result" + } + }, + "metadata": { + "eventType": { + "key": "x-oca-event.action", + "object": "event" + }, + "productLogId": { + "key": "x-oca-event.code", + "object": "event" + }, + "eventTimestamp": [ + { + "key": "x-oca-event.created", + "object": "event" + }, + { + "key": "first_observed", + "cybox": false + }, + { + "key": "last_observed", + "cybox": false + } + ], + "description": { + "key": "x-oca-event.extensions.x-gcp-chronicle-event.description", + "object": "event" + }, + "productName": { + "key": "x-oca-event.agent", + "object": "event" + }, + "vendorName": { + "key": "x-oca-event.provider", + "object": "event" + }, + "productEventType": { + "key": "x-oca-event.outcome", + "object": "event" + } + }, + "about": { + "file": { + "fullPath": [ + { + "key": "file.name", + "object": "about_file", + "transformer": "ToFileName" + }, + { + "key": "directory.path", + "object": "about_directory", + "transformer": "ToDirectoryPath" + }, + { + "key": "file.parent_directory_ref", + "object": "about_file", + "references": "about_directory" + }, + { + "key": "email-message.extensions.x-gcp-chronicle-email-message.file_ref", + "object": "em", + "references": "about_file" + } + ], + "size": { + "key": "file.size", + "object": "about_file", + "transformer": "ToInteger" + }, + "md5": { + "key": "file.hashes.MD5", + "object": "about_file" + }, + "sha1": { + "key": "file.hashes.SHA-1", + "object": "about_file" + }, + "sha256": { + "key": "file.hashes.SHA-256", + "object": "about_file" + }, + "mimeType": { + "key": "file.extensions.x-gcp-chronicle-file.mime_type", + "object": "about_file" + }, + "lastModificationTime": { + "key": "file.modified", + "object": "about_file" + }, + "fileType": { + "key": "file.extensions.x-gcp-chronicle-file.file_type", + "object": "about_file" + } + } + }, + "additional": { + "detectorId": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.aws_detector_id", + "object": "security_result" + }, + "archived": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.aws_archived", + "object": "security_result" + }, + "count": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.aws_count", + "object": "security_result" + }, + "imageId": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.aws_image_id", + "object": "security_result" + }, + "resourceRole": { + "key": "x-ibm-finding.extensions.x-gcp-chronicle-security-result.aws_resource_role", + "object": "security_result" + } + } + } +} \ No newline at end of file diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/query_constructor.py b/stix_shifter_modules/gcp_chronicle/stix_translation/query_constructor.py new file mode 100644 index 000000000..833135999 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/query_constructor.py @@ -0,0 +1,581 @@ +import copy +from stix_shifter_utils.stix_translation.src.patterns.pattern_objects import ObservationExpression, \ + ComparisonExpression, ComparisonComparators, Pattern, CombinedComparisonExpression, CombinedObservationExpression +import logging +import re +from datetime import datetime, timedelta +from os import path +import json + +logger = logging.getLogger(__name__) + +START_STOP_PATTERN = r"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z)" +MAC = '^(([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))$' +EMAIL = r'^\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b$' +CONFIG_MAP_PATH = "json/config_map.json" + +STOP_TIME = datetime.utcnow() + + +class StartStopQualifierValueException(Exception): + pass + + +class FileNotFoundException(Exception): + pass + + +class QueryStringPatternTranslator: + """ + translate stix pattern to native data source query language + """ + + def __init__(self, pattern: Pattern, data_model_mapper, options): + logger.info("GCP Chronicle Connector") + self.dmm = data_model_mapper + self.options = options + self.all_queries = [] + self.comparator_lookup = self.dmm.map_comparator() + self.config_map = self.load_json(CONFIG_MAP_PATH) + self.parse_expression(pattern) + self.qualified_query = self._create_formatted_query() + + @staticmethod + def load_json(rel_path_of_file): + """ + Consumes a json file and returns a dictionary + :param rel_path_of_file: str + :return: dict + """ + + _json_path = path.dirname(path.abspath(__file__)) + "/" + rel_path_of_file + try: + if path.exists(_json_path): + with open(_json_path, encoding='utf-8') as f_obj: + return json.load(f_obj) + raise FileNotFoundException + except FileNotFoundException as e: + raise FileNotFoundError(f'{rel_path_of_file} not found') from e + + def _create_formatted_query(self): + """ + Creates formatted native data source query + Combines similar timestamp observations + Creates new query for different timestamp observations + :return: formatted query:list + """ + final_query = [] + utc_timestamp = STOP_TIME.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' + converted_utc_timestamp = QueryStringPatternTranslator._format_datetime(utc_timestamp) + rule_name = "cp4s_gcp_udi_rule_" + str(converted_utc_timestamp) + merged_queries = [] + for queries in self.all_queries: + if not merged_queries: + merged_queries.append(queries) + else: + matched = False + for query in merged_queries: + if queries["timestamp"] == query["timestamp"]: + if not query["is_ts_matched"]: + query["query"] = f'({query["query"]})' + query["is_ts_matched"] = True + query["query"] += f' {queries["operator"]} ({queries["query"]})' + matched = True + if not matched: + merged_queries.append(queries) + for queries in merged_queries: + formatted_query = { + "ruleText": "rule " + rule_name + " { " + "meta: author = \"ibm cp4s user\" description = \"Create " + "event rule that should generate detections\" " + "events: " + queries['query'] + " condition: $udm}", + "startTime": queries["timestamp"][0], + "endTime": queries["timestamp"][1] + } + final_query.append(formatted_query) + return final_query + + @staticmethod + def _format_set(values, mapped_field_type, is_regex_field) -> list: + """ + Formats value in the event of set operation + :param values + :param mapped_field_type: str + :param is_regex_field: boolean + :return formatted value + """ + gen = values.element_iterator() + formatted_values = [] + for value in gen: + formatted_value = QueryStringPatternTranslator._escape_value( + QueryStringPatternTranslator._format_value_type(value, mapped_field_type), is_regex_field) + formatted_values.append(formatted_value) + return formatted_values + + @staticmethod + def _format_match(value, mapped_field_type) -> str: + """ + Formats value in the event of Matches operation for regex pattern + supports only regular expression pattern matching + :param value + :param mapped_field_type:str + """ + if mapped_field_type not in ("string", "email", "mac"): + raise NotImplementedError(f'MATCHES operator is not supported for {mapped_field_type} type input') + if not re.search(r'[()}\]\[{*^$+?|]', value): # replace \\ if regex meta characters are not there + value = value.replace('\\', '\\\\') + value = value.replace('/', '\\/') + return f'/(?s){value}/' # (?s) to enforce full UDM field matching + + @staticmethod + def _format_like(value, mapped_field_type) -> str: + """ + Formats value in the event of like operation for substring matching + :param value + :param mapped_field_type:str + :return formatted string type value + """ + if mapped_field_type not in ("string", "email", "mac"): + raise NotImplementedError(f'LIKE operator is not supported for {mapped_field_type} type input') + value = QueryStringPatternTranslator._escape_value(value, is_regex=True) + value = value.replace('%', '.*').replace('_', '.') # replace SQL LIKE characters + return value + + @staticmethod + def _format_equality(value, is_regex_field) -> str: + """ + Formats value in the event of equality operation + :param value + :param is_regex_field: boolean + :return formatted value + """ + return QueryStringPatternTranslator._escape_value(value, is_regex_field) + + @staticmethod + def _escape_value(value, is_regex) -> str: + """ + adds escape characters to string and regex value + :param value + :param is_regex: boolean + :return formatted value + """ + if is_regex: + value = re.escape(value).replace('/', "\\/").replace(':', "\\:") + # (?s) to enforce full UDM field matching + value = f'/(?s){value}/' + elif isinstance(value, str): + value = value.replace('\\', '\\\\').replace('\"', '\\"') + value = f'\"{value}\"' + return str(value) + + def _check_enum_supported_values(self, value, mapped_fields_array): + """ + checks for enum supported values + :param mapped_fields_array: list + :param value:str + :return None + """ + all_enum_values = [] + for field_name in mapped_fields_array: + if field_name != "network.dhcp.type": + field_name = field_name.split('.')[-1] + if field_name not in self.config_map["enum_supported_values"].keys(): + raise NotImplementedError(f'Enum field - {field_name} is not found in enum_supported_values') + all_enum_values += list(self.config_map["enum_supported_values"][field_name]) + is_value_present = False + if not isinstance(value, list): + value = [value] + for values in value: + if values[1:-1] in all_enum_values: + is_value_present = True + break # any one matched input is sufficient to create comparison string + if not is_value_present: + raise NotImplementedError(f'Unsupported ENUM values provided. Possible supported' + f' enum values are {all_enum_values}') + + @staticmethod + def _format_value_type(value, mapped_field_type): + """ + Converts input value that matches with the mapped field value type + :param value + :param mapped_field_type: str + :return formatted value + """ + converted_value = str(value) + if mapped_field_type == "enum": + converted_value = converted_value.upper() + elif mapped_field_type == "timestamp": + converted_value = QueryStringPatternTranslator._format_datetime(converted_value) + elif mapped_field_type == "mac": + compile_mac_regex = re.compile(MAC) + if not compile_mac_regex.search(converted_value): + raise NotImplementedError(f'Invalid mac address - {converted_value} provided') + elif mapped_field_type == "int": + if not converted_value.isdigit(): + raise NotImplementedError(f'string type input - {converted_value} is not supported for ' + f'integer type fields') + converted_value = int(value) + elif mapped_field_type == "email": + compile_email_regex = re.compile(EMAIL) + if not compile_email_regex.search(converted_value): + raise NotImplementedError(f'Invalid email address - {converted_value} provided') + return converted_value + + def _check_value_comparator_support(self, value, comparator, mapped_field_type): + """ + checks the comparator and value support + :param value + :param comparator + :param mapped_field_type: str + """ + operator = self.comparator_lookup[str(comparator)] + if mapped_field_type == "enum" and (comparator not in [ComparisonComparators.Equal, + ComparisonComparators.NotEqual]): + raise NotImplementedError(f'{operator} operator is not supported for Enum type input. Possible supported ' + f'operator are [ =, !=, IN, NOT IN ]') + if isinstance(value, str) and comparator not in [ComparisonComparators.Equal, ComparisonComparators.NotEqual, + ComparisonComparators.Like, ComparisonComparators.Matches]: + raise NotImplementedError(f'{operator} operator is not supported for string type input') + + @staticmethod + def _format_datetime(value): + """ + Converts timestamp to seconds + :param value + :return: int, converted epoch value + """ + try: + time_pattern = '%Y-%m-%dT%H:%M:%S.%fZ' + if re.search(r"\d{4}(-\d{2}){2}T\d{2}(:\d{2}){2}Z", str(value)): + time_pattern = '%Y-%m-%dT%H:%M:%SZ' + epoch = datetime(1970, 1, 1) + converted_time = int(((datetime.strptime(value, + time_pattern) - epoch).total_seconds())) + return converted_time + except ValueError: + logger.error("Cannot convert the timestamp %s to seconds", value) + raise NotImplementedError(f'cannot convert the timestamp {value} to seconds') + + @staticmethod + def _parse_time_range(qualifier, time_range): + """ + Converts qualifier to timestamp format + :param qualifier: str + :param time_range: int + return: list of formatted timestamps + """ + try: + compile_timestamp_regex = re.compile(START_STOP_PATTERN) + if qualifier and compile_timestamp_regex.search(qualifier): + time_range_iterator = compile_timestamp_regex.finditer(qualifier) + time_range_list = [each.group() for each in time_range_iterator] + else: + start_time = STOP_TIME - timedelta(minutes=time_range) + converted_start_time = start_time.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' + # limit 3 digit value for millisecond + converted_stop_time = STOP_TIME.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' + time_range_list = [converted_start_time, converted_stop_time] + return time_range_list + except (KeyError, IndexError, TypeError) as e: + raise e + + @staticmethod + def _check_time_range_values(time_range_list): + """ + checks for valid start and stop time + :param time_range_list: list + """ + utc_timestamp = STOP_TIME.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' + converted_utc_timestamp = QueryStringPatternTranslator._format_datetime(utc_timestamp) + converted_timestamp = [] + for timestamp in time_range_list: + converted_time = QueryStringPatternTranslator._format_datetime(timestamp) + if converted_time > converted_utc_timestamp: + raise StartStopQualifierValueException('Start/Stop time should not be in the future UTC timestamp') + converted_timestamp.append(converted_time) + if converted_timestamp[0] >= converted_timestamp[1]: + raise StartStopQualifierValueException('Start time should be lesser than Stop time') + + def _get_mapped_field_type(self, mapped_field_array): + """ + Returns the type of mapped field array + :param mapped_field_array: list + :return: str + """ + mapped_field = mapped_field_array[0] + mapped_field_type = "string" + for key, value in self.config_map.items(): + if mapped_field in value and key in ["int_supported_fields", "email_supported_fields", + "timestamp_supported_fields", "mac_supported_fields", + "enum_supported_fields"]: + mapped_field_type = key.split('_')[0] + break + return mapped_field_type + + @staticmethod + def _security_result_conversion(security_result_conversion_fields, original_field_name, value): + """ + Conversion of security result field input values + :param security_result_conversion_fields: dict + :param original_field_name: str + :param value: list + :return: list + """ + if original_field_name in security_result_conversion_fields: + values = copy.deepcopy(value) + value = [] + for val in values: + formatted_value = val[1:-1] + if formatted_value in security_result_conversion_fields[original_field_name].keys(): + category_value = security_result_conversion_fields[original_field_name][formatted_value] + if not isinstance(category_value, list): + category_value = [category_value] + value += ["\"" + i + "\"" for i in category_value] + return value + + @staticmethod + def _format_negate(comparator): + """ + returns negation of input operator + :param comparator:str + :return str + """ + negate_comparator = { + ">": "<=", + ">=": "<", + "<": ">=", + "<=": ">", + "=": "!=", + "!=": "=" + } + return negate_comparator[comparator] + + def _add_field_name_condition(self, mapped_field_type, original_field_name, field_name, field_mappings): + """ + Adds default condition to each field_name + :param mapped_field_type: str + :param original_field_name: str + :param field_name: str + :param field_mappings: str + :return str + """ + split_field_name = original_field_name + if original_field_name != "network.dhcp.type": + split_field_name = split_field_name.split('.')[-1] + unspecified_value = '\"\"' + if mapped_field_type == 'int': + unspecified_value = str(0) + elif mapped_field_type == 'enum': + if original_field_name == "security_result.category": + unspecified_value = "UNKNOWN_CATEGORY" + elif original_field_name == "security_result.severity": + unspecified_value = "UNKNOWN_SEVERITY" + else: + unspecified_value = self.config_map["enum_supported_values"][split_field_name][0] + unspecified_value = f'\"{unspecified_value}\"' + add_unspecified_field = f'{field_name} != {unspecified_value}' + if field_mappings != add_unspecified_field: + field_mappings = f'( {field_mappings} and {add_unspecified_field} )' + return field_mappings + + def _create_parsed_query(self, field_name, value, mapped_field_type, comparator, is_negated): + """ + Creates comparison string for each field_name in mapped_field_array + :param field_name: str + :param value + :param mapped_field_type: str + :param comparator: str + :param is_negated: boolean + :return: str + """ + original_field_name = field_name + field_name = "$udm." + original_field_name + if original_field_name in self.config_map["list_type_fields"]: + add_keyword = "any " if comparator == "=" else "all " + field_name = add_keyword + field_name + comparison_string = "" + if not isinstance(value, list): + value = [value] + security_result_conversion_fields = self.config_map["security_result_conversion_fields"] + value = self._security_result_conversion(security_result_conversion_fields, original_field_name, value) + all_mappings = [] + for values in value: + split_field_name = original_field_name + if original_field_name in self.config_map["enum_supported_fields"]: + if not re.match(r'^/(?s).*/$', values): + values = values.upper() + mapped_field_type = "enum" + if mapped_field_type == "enum": + if split_field_name != "network.dhcp.type": + split_field_name = split_field_name.split('.')[-1] + # value[1:-1] -> removing unwanted double quotes + if original_field_name not in security_result_conversion_fields and \ + values[1:-1] not in self.config_map["enum_supported_values"][split_field_name]: + continue + if mapped_field_type not in ("enum", "int", "timestamp"): + values += " nocase" + field_mappings = f'{field_name} {comparator} {values}' + if comparator in ["!=", "<", "<="]: + field_mappings = self._add_field_name_condition(mapped_field_type, original_field_name, field_name, + field_mappings) + all_mappings.append(field_mappings) + if all_mappings: + if len(all_mappings) == 1: + comparison_string += all_mappings[0] + else: + condition_string = " and " if is_negated else " or " + in_comparison_string = f'{condition_string}'.join(all_mappings) + comparison_string += in_comparison_string + if len(all_mappings) > 1: + comparison_string = "(" + comparison_string + ")" + return comparison_string + + def _parse_mapped_fields(self, formatted_value, mapped_fields_array, mapped_field_type, expression): + """ + parse mapped fields into boolean expression + :param formatted_value: str + :param mapped_fields_array: list + :param mapped_field_type:str + :param expression: expression object + :return: str + """ + is_negated = expression.negated + comparator = self._lookup_comparison_operator(expression.comparator) + if mapped_field_type == "enum": + self._check_enum_supported_values(formatted_value, mapped_fields_array) + comparison_string = "" + comparison_string_new_count = 0 + if is_negated: + comparator = QueryStringPatternTranslator._format_negate(comparator) + for field_name in mapped_fields_array: + comparison_string_new = self._create_parsed_query(field_name, formatted_value, mapped_field_type, + comparator, is_negated) + if comparison_string_new: + comparison_string_new_count += 1 + if comparison_string: + comparison_string += " or " + comparison_string += comparison_string_new + + if len(mapped_fields_array) > 1 and comparison_string and comparison_string_new_count > 1: + # More than one data source field maps to the STIX attribute, so group comparisons together. + grouped_comparison_string = "(" + comparison_string + ")" + comparison_string = grouped_comparison_string + return comparison_string + + def _lookup_comparison_operator(self, expression_operator): + """ + lookup operators support in gcp chronicle + :param expression_operator:enum object + :return str + """ + if str(expression_operator) not in self.comparator_lookup: + raise NotImplementedError( + f'Comparison operator {expression_operator.name} unsupported for gcp chronicle connector') + + return self.comparator_lookup[str(expression_operator)] + + def _eval_comparison_value(self, expression, mapped_field_type, mapped_fields_array): + """ + Function for parsing comparison expression value + :param expression: expression object + :param mapped_field_type:str + :return: formatted expression value + """ + is_regex_field = mapped_fields_array[0] in self.config_map["regex_formatting_fields"] + if expression.comparator == ComparisonComparators.Like: + value = self._format_like(expression.value, mapped_field_type) + elif expression.comparator == ComparisonComparators.Matches: + value = self._format_match(expression.value, mapped_field_type) + elif expression.comparator == ComparisonComparators.In: + value = QueryStringPatternTranslator._format_set(expression.value, mapped_field_type, is_regex_field) + elif expression.comparator in [ComparisonComparators.GreaterThan, ComparisonComparators.GreaterThanOrEqual, + ComparisonComparators.LessThan, ComparisonComparators.LessThanOrEqual, + ComparisonComparators.Equal, ComparisonComparators.NotEqual]: + value = QueryStringPatternTranslator._format_value_type(expression.value, mapped_field_type) + self._check_value_comparator_support(value, expression.comparator, mapped_field_type) + value = self._format_equality(value, is_regex_field) + else: + raise NotImplementedError('Unknown comparator expression operator') + return value + + def _eval_combined_comparison_exp(self, expression): + """ + Function for parsing combined comparison expression + :param expression: expression object + """ + operator = self._lookup_comparison_operator(expression.operator) + expression_01 = self._parse_expression(expression.expr1) + expression_02 = self._parse_expression(expression.expr2) + if not expression_01 or not expression_02: + return '' + if isinstance(expression.expr1, CombinedComparisonExpression): + expression_01 = f'({expression_01})' + if isinstance(expression.expr2, CombinedComparisonExpression): + expression_02 = f'({expression_02})' + query_string = f'{expression_01} {operator} {expression_02}' + return f'{query_string}' + + def _eval_combined_observation_exp(self, expression, qualifier=None): + """ + Function for parsing combined observation expression + :param expression: expression object + :param qualifier: str, default in None + """ + combined_obs_operator = self._lookup_comparison_operator(expression.operator) + self._parse_expression(expression.expr1, qualifier) + self.comparator_lookup["current_observation_operator"] = combined_obs_operator + self._parse_expression(expression.expr2, qualifier) + + def _parse_expression(self, expression, qualifier=None): + """ + parse ANTLR pattern to gcp chronicle yara-l query + :param expression: expression object, ANTLR parsed expression object + :param qualifier: str, default in None + """ + if isinstance(expression, ComparisonExpression): # Base Case + stix_objects = expression.object_path.split(':') + mapped_fields_array = self.dmm.map_field(stix_objects[0], stix_objects[1]) + mapped_field_type = self._get_mapped_field_type(mapped_fields_array) + value = self._eval_comparison_value(expression, mapped_field_type, mapped_fields_array) + comparison_string = self._parse_mapped_fields(value, mapped_fields_array, mapped_field_type, expression) + return comparison_string + elif isinstance(expression, CombinedComparisonExpression): + return self._eval_combined_comparison_exp(expression) + elif isinstance(expression, ObservationExpression): + query_data = {} + query_string = f'{self._parse_expression(expression.comparison_expression)}' + if isinstance(expression.comparison_expression, ComparisonExpression) and query_string and \ + query_string[0] == "(" and query_string[-1] == ")": + query_string = query_string[1:-1] # removes extra brackets () + time_range_list = QueryStringPatternTranslator._parse_time_range(qualifier, self.options["time_range"]) + QueryStringPatternTranslator._check_time_range_values(time_range_list) # check for valid timestamp values + obs_operator = self.comparator_lookup["current_observation_operator"] if "current_observation_operator" \ + in self.comparator_lookup else "or" + query_data.update({"query": query_string, "timestamp": time_range_list, "operator": obs_operator, + "is_ts_matched": False}) + self.all_queries.append(query_data) + elif hasattr(expression, 'qualifier') and hasattr(expression, 'observation_expression'): + self._parse_expression(expression.observation_expression, expression.qualifier) + elif isinstance(expression, CombinedObservationExpression): + self._eval_combined_observation_exp(expression, qualifier) + elif isinstance(expression, Pattern): + self._parse_expression(expression.expression) + else: + raise RuntimeError(f'Unknown Recursion Case for expression={expression},' + f' type(expression)={type(expression)}') + return None + + def parse_expression(self, pattern: Pattern): + self._parse_expression(pattern) + + +def translate_pattern(pattern: Pattern, data_model_mapping, options): + """ + Conversion of ANTLR pattern to gcp chronicle query + :param pattern: expression object, ANTLR parsed expression object + :param data_model_mapping: DataMapper object, mapping object obtained by parsing json + :param options: dict, time_range defaults to 5 + :return: list + """ + query = QueryStringPatternTranslator(pattern, data_model_mapping, options).qualified_query + return query diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/query_translator.py b/stix_shifter_modules/gcp_chronicle/stix_translation/query_translator.py new file mode 100644 index 000000000..2bab64a8b --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/query_translator.py @@ -0,0 +1,24 @@ +import logging + +from stix_shifter_utils.modules.base.stix_translation.base_query_translator import BaseQueryTranslator +from . import query_constructor + +logger = logging.getLogger(__name__) + + +class QueryTranslator(BaseQueryTranslator): + + def transform_antlr(self, data, antlr_parsing_object): + """ + Transforms STIX pattern into a different query format. Based on a mapping file + :param antlr_parsing_object: Antlr parsing objects for the STIX pattern + :type antlr_parsing_object: object + :return: transformed query string + :rtype: str + """ + + logger.info("Converting STIX2 Pattern to data source query") + + query_string = query_constructor.translate_pattern( + antlr_parsing_object, self, self.options) + return query_string diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/results_translator.py b/stix_shifter_modules/gcp_chronicle/stix_translation/results_translator.py new file mode 100644 index 000000000..746b8300c --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/results_translator.py @@ -0,0 +1,5 @@ +from stix_shifter_utils.stix_translation.src.json_to_stix.json_to_stix import JSONToStix + + +class ResultsTranslator(JSONToStix): + pass diff --git a/stix_shifter_modules/gcp_chronicle/stix_translation/transformers.py b/stix_shifter_modules/gcp_chronicle/stix_translation/transformers.py new file mode 100644 index 000000000..99be3da34 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_translation/transformers.py @@ -0,0 +1,58 @@ +from stix_shifter_utils.stix_translation.src.utils.transformers import ValueTransformer +from stix_shifter_utils.utils import logger +import re + +LOGGER = logger.set_logger(__name__) + + +class FormatToStixRegistryValue(ValueTransformer): + """A value transformer to convert gcp chronicle Registry value to windows-registry-key.value STIX""" + + @staticmethod + def transform(obj): + + try: + stix_mapping = {"registryValueName": "name", "registryValueData": "data"} + converted_value = [] + for each_value in obj: + registryvalue_dict = {} + for key, value in each_value.items(): + registryvalue_dict.update({stix_mapping[key]: value}) + converted_value.append(registryvalue_dict) + return converted_value + + except ValueError: + LOGGER.error("Cannot convert root value to Stix formatted windows registry value") + raise + + +class ValidateUrl(ValueTransformer): + """ Validate the URL """ + + @staticmethod + def transform(obj): + pattern = re.compile( + r"^([a-zA-Z][a-zA-Z0-9+.-]*):(?:\/\/((?:(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:]|%[0-9a-fA-F]{2})*))(\3)@)?(?=(" + r"(?:\[?(?:::[a-fA-F0-9]+(?::[a-fA-F0-9]+)?|(?:[a-fA-F0-9]+:)+(?::[a-fA-F0-9]+)+|(?:[a-fA-F0-9]+:)+(?::|(" + r"?:[a-fA-F0-9]+:?)*))\]?)|(?:[a-zA-Z0-9-._~!$&'()*+,;=]|%[0-9a-fA-F]{2})*))\5(?::(?=(\d*))\6)?)(\/(?=((" + r"?:[a-zA-Z0-9-._~!$&'()*+,;=:@\/]|%[0-9a-fA-F]{2})*))\8)?|(\/?(?!\/)(?=((?:[a-zA-Z0-9-._~!$&'()*+," + r";=:@\/]|%[0-9a-fA-F]{2})*))\10)?)(?:\?(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:@\/?]|%[0-9a-fA-F]{2})*))\11)?(" + r"?:#(?=((?:[a-zA-Z0-9-._~!$&'()*+,;=:@\/?]|%[0-9a-fA-F]{2})*))\12)?$") + if pattern.match(str(obj)): + return obj + return None + + +class FilterValidEmail(ValueTransformer): + """ Validate email address format """ + + @staticmethod + def transform(obj): + if isinstance(obj, str): + email = obj.split(":") + pattern = re.compile(r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)') + for em in email: + if pattern.match(str(em)): + return em + return None + return obj diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/__init__.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/api_client.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/api_client.py new file mode 100644 index 000000000..424cded6d --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_transmission/api_client.py @@ -0,0 +1,173 @@ +from google.oauth2 import service_account +from googleapiclient import _auth +from google.auth.exceptions import RefreshError +from httplib2 import ServerNotFoundError +import json +from stix_shifter_utils.utils import logger +from stix_shifter_utils.utils.error_response import ErrorResponder +import time + +class InvalidResponseException(Exception): + pass + + +class APIClient: + QUERY_ENDPOINT = 'v2/detect/rules' + QUERY_SCOPES = ['https://www.googleapis.com/auth/chronicle-backstory'] + URI = "https://oauth2.googleapis.com/token" + + def __init__(self, connection, configuration): + self.auth = configuration.get('auth') + self.auth['private_key'] = connection.get('selfSignedCert').replace('\\n', '\n') + self.auth['token_uri'] = self.URI + self.host = "https://" + connection.get('host') + self.result_limit = connection['options'].get('result_limit') + self.logger = logger.set_logger(__name__) + self.connector = __name__.split('.')[1] + self.http_client = None + + def ping_box(self): + """ + Ping the Data Source + :return: Response object + """ + ping_endpoint = self.host + "/" + self.QUERY_ENDPOINT + if not self.http_client: + self.create_http_client() + return self.http_client.request(ping_endpoint, 'GET') + + def create_search(self, query): + """ + Create the rule and run the retrohunt for the rule + :param query: string/dict + :return: Response object + """ + response_dict = {} + return_obj = {} + try: + if isinstance(query, str): + query = json.loads(query) + + rule_text = {"ruleText": query["ruleText"]} + rule_response = self.create_rule(rule_text) # calls the api to create the rule + parsed = json.loads(rule_response[1]) + if rule_response[0].status == 200: + if 'ruleId' in parsed.keys(): + + run_retrohunt_endpoint = self.host + "/" + self.QUERY_ENDPOINT + "/" + parsed['ruleId'] + \ + ':runRetrohunt' + + date = {"startTime": query["startTime"], "endTime": query["endTime"]} + # calls the api to run the retrohunt. + return self.http_client.request(run_retrohunt_endpoint, 'POST', body=json.dumps(date)) + + raise InvalidResponseException + + response_dict['code'] = rule_response[0].status + response_dict['message'] = parsed['error'].get('message') + ErrorResponder.fill_error(return_obj, response_dict, ['message'], + connector=self.connector) + + except ServerNotFoundError: + response_dict['code'] = 1010 + response_dict['message'] = "Invalid Host" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except RefreshError: + response_dict['code'] = 1015 + response_dict['message'] = "Invalid Client Email" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ValueError as val_ex: + if 'Could not deserialize key data' in str(val_ex): + response_dict['message'] = val_ex + response_dict['code'] = 1015 + else: + response_dict['message'] = f'cannot parse {val_ex}' + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except InvalidResponseException: + response_dict['code'] = 100 + response_dict['message'] = "InvalidResponse" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except Exception as ex: + if "timed out" in str(ex): + response_dict['code'] = 120 + response_dict['message'] = str(ex) + else: + response_dict['message'] = ex + self.logger.error('error when getting search results: %s', ex) + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + return return_obj + + def create_rule(self, query): + + """ + Create the rule for the input query + :param query: dict + :return: rule id + """ + + create_rule_endpoint = self.host + "/" + self.QUERY_ENDPOINT + if not self.http_client: + self.create_http_client() + return self.http_client.request(create_rule_endpoint, 'POST', body=json.dumps(query)) + + def get_search_status(self, search_id): + """ + Queries the datasource to fetch the retorohunt status + :param search_id: str + :return: response object + """ + status_id = search_id.split(":") + if not self.http_client: + self.create_http_client() + get_retrohunt_status = self.QUERY_ENDPOINT + "/" + status_id[1] + '/retrohunts/' + status_id[0] + status_endpoint = self.host + "/" + get_retrohunt_status + + return self.http_client.request(status_endpoint, 'GET') + + def get_search_results(self, search_id, next_page_token, page_size): + """ + Return the search results + :param search_id:str + :param next_page_token: str + :param page_size: int + :return: Response object + """ + if not self.http_client: + self.create_http_client() + search_result = search_id.split(":") + if next_page_token != '0': + + list_detection = f"{self.QUERY_ENDPOINT}/{search_result[1]}/detections?" \ + f"page_size={page_size}&page_token={next_page_token}" + else: + list_detection = f"{self.QUERY_ENDPOINT}/{search_result[1]}/detections?page_size={page_size}" + + list_detection_endpoint = self.host + "/" + list_detection + return self.http_client.request(list_detection_endpoint, 'GET') + + def delete_search(self, search_id): + """ + Delete the search id. + :param search_id:str + :return: Response object + """ + if not self.http_client: + self.create_http_client() + delete_id = search_id.split(":") + + delete = self.QUERY_ENDPOINT + "/" + delete_id[1] + delete_endpoint = self.host + "/" + delete + return self.http_client.request(delete_endpoint, 'DELETE') + + def create_http_client(self): + """ + Initialize the http client object using the credentials + :return: None + """ + credentials = service_account.Credentials.from_service_account_info(self.auth, scopes=self.QUERY_SCOPES) + self.http_client = _auth.authorized_http(credentials) diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/delete_connector.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/delete_connector.py new file mode 100644 index 000000000..fd51174c4 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_transmission/delete_connector.py @@ -0,0 +1,64 @@ +from stix_shifter_utils.modules.base.stix_transmission.base_delete_connector import BaseDeleteConnector +from stix_shifter_utils.utils.error_response import ErrorResponder +from stix_shifter_utils.utils import logger +import json +from httplib2 import ServerNotFoundError +from google.auth.exceptions import RefreshError + + +class DeleteConnector(BaseDeleteConnector): + def __init__(self, api_client): + self.api_client = api_client + self.logger = logger.set_logger(__name__) + self.connector = __name__.split('.')[1] + + def delete_query_connection(self, search_id): + + """ + Deletes the search id. + :param search_id:str + :return dict + """ + return_obj = {} + response_dict = {} + try: + + response = self.api_client.delete_search(search_id) + response_code = response[0].status + response_text = json.loads(response[1]) + + if response_code == 200: + return_obj['success'] = True + else: + response_dict['code'] = response_code + response_dict['message'] = response_text['error']['message'] + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ServerNotFoundError: + response_dict['code'] = 1010 + response_dict['message'] = "Invalid Host" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except RefreshError: + response_dict['code'] = 1015 + response_dict['message'] = "Invalid Client Email" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ValueError as d_ex: + if 'Could not deserialize key data' in str(d_ex): + response_dict['message'] = d_ex + response_dict['code'] = 1015 + else: + response_dict['message'] = f'cannot parse {d_ex}' + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except Exception as d_err: + if "timed out" in str(d_err): + response_dict['code'] = 120 + response_dict['message'] = str(d_err) + else: + response_dict['message'] = d_err + self.logger.error('error when getting search results: %s', d_err) + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + return return_obj diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/error_mapper.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/error_mapper.py new file mode 100644 index 000000000..a261c6e15 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_transmission/error_mapper.py @@ -0,0 +1,43 @@ +from stix_shifter_utils.utils.error_mapper_base import ErrorMapperBase +from stix_shifter_utils.utils.error_response import ErrorCode +from stix_shifter_utils.utils import logger + +error_mapping = { + 100: ErrorCode.TRANSMISSION_INVALID_PARAMETER, + 120: ErrorCode.TRANSMISSION_CONNECT, + 400: ErrorCode.TRANSMISSION_INVALID_PARAMETER, + 401: ErrorCode.TRANSMISSION_AUTH_CREDENTIALS, + 403: ErrorCode.TRANSMISSION_FORBIDDEN, + 404: ErrorCode.TRANSMISSION_INVALID_PARAMETER, + 429: ErrorCode.TRANSMISSION_CONNECT, + 500: ErrorCode.TRANSMISSION_CONNECT, + 1002: ErrorCode.TRANSMISSION_SEARCH_DOES_NOT_EXISTS, + 1010: ErrorCode.TRANSMISSION_REMOTE_SYSTEM_IS_UNAVAILABLE, + 1015: ErrorCode.TRANSMISSION_AUTH_CREDENTIALS +} + + +class ErrorMapper: + """ + Set Error Code + """ + DEFAULT_ERROR = ErrorCode.TRANSMISSION_MODULE_DEFAULT_ERROR + logger = logger.set_logger(__name__) + + @staticmethod + def set_error_code(json_data, return_obj): + code = None + try: + code = int(json_data['code']) + except Exception: + pass + + error_code = ErrorMapper.DEFAULT_ERROR + + if code in error_mapping: + error_code = error_mapping.get(code) + + if error_code == ErrorMapper.DEFAULT_ERROR: + ErrorMapper.logger.error("failed to map: %s", str(json_data)) + + ErrorMapperBase.set_error_code(return_obj, error_code) diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/ping_connector.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/ping_connector.py new file mode 100644 index 000000000..2212276dd --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_transmission/ping_connector.py @@ -0,0 +1,63 @@ +from stix_shifter_utils.modules.base.stix_transmission.base_ping_connector import BasePingConnector +from stix_shifter_utils.utils.error_response import ErrorResponder +from stix_shifter_utils.utils import logger +import json +from httplib2 import ServerNotFoundError +from google.auth.exceptions import RefreshError + + +class PingConnector(BasePingConnector): + def __init__(self, api_client): + self.api_client = api_client + self.logger = logger.set_logger(__name__) + self.connector = __name__.split('.')[1] + + def ping_connection(self): + + """ + Ping the endpoint + :return: dict + """ + return_obj = {} + response_dict = {} + try: + + response = self.api_client.ping_box() + response_code = response[0].status + response_text = json.loads(response[1]) + if response_code == 200: + return_obj['success'] = True + else: + response_dict['code'] = response_code + response_dict['message'] = response_text + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ServerNotFoundError: + response_dict['code'] = 1010 + response_dict['message'] = "Invalid Host" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except RefreshError: + + response_dict['code'] = 1015 + response_dict['message'] = "Invalid Client Email" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ValueError as v_ex: + if 'Could not deserialize key data' in str(v_ex): + response_dict['message'] = v_ex + response_dict['code'] = 1015 + else: + response_dict['message'] = f'cannot parse {v_ex}' + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except Exception as ex: + if "timed out" in str(ex): + response_dict['code'] = 120 + response_dict['message'] = str(ex) + else: + response_dict['message'] = ex + self.logger.error('error when getting search results: %s', ex) + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + return return_obj diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/query_connector.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/query_connector.py new file mode 100644 index 000000000..7a1ac4c10 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_transmission/query_connector.py @@ -0,0 +1,65 @@ +from stix_shifter_utils.modules.base.stix_transmission.base_query_connector import BaseQueryConnector +from stix_shifter_utils.utils.error_response import ErrorResponder +from stix_shifter_utils.utils import logger +import json + + +class InvalidResponseException(Exception): + pass + + +class QueryConnector(BaseQueryConnector): + def __init__(self, api_client): + self.api_client = api_client + self.logger = logger.set_logger(__name__) + self.connector = __name__.split('.')[1] + + def create_query_connection(self, query): + + """ + Function to create query connection + :param query: dict, str + :return: dict + """ + response_dict = {} + return_obj = {} + try: + response = self.api_client.create_search(query) + if isinstance(response, dict): # return the object, if response code is not 200 in create_rule + return response # in api_client + response_code = response[0].status + response_text = json.loads(response[1]) + + if response_code == 200: + if 'retrohuntId' in response_text.keys() and 'ruleId' in response_text.keys(): + return_obj['success'] = True + return_obj['search_id'] = f"{response_text['retrohuntId']}:{response_text['ruleId']}" + + else: + raise InvalidResponseException + + else: + response_dict['code'] = response_code + response_dict['message'] = response_text['error'].get('message') + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except InvalidResponseException: + response_dict['code'] = 100 + response_dict['message'] = "InvalidResponse" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ValueError as q_ex: + + response_dict['message'] = f'cannot parse {q_ex}' + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except Exception as q_ex: + if "timed out" in str(q_ex): + response_dict['code'] = 120 + response_dict['message'] = str(q_ex) + else: + response_dict['message'] = q_ex + self.logger.error('error when getting search results: %s', q_ex) + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + return return_obj diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/results_connector.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/results_connector.py new file mode 100644 index 000000000..c1ab1d4e8 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_transmission/results_connector.py @@ -0,0 +1,399 @@ +from stix_shifter_utils.modules.base.stix_transmission.base_results_connector import BaseResultsConnector +from stix_shifter_utils.utils.error_response import ErrorResponder +from stix_shifter_utils.utils import logger +import json +from httplib2 import ServerNotFoundError +from google.auth.exceptions import RefreshError +import copy +import re + + +class ResultsConnector(BaseResultsConnector): + EMAIL_PATTERN = re.compile(r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)') + GCP_MAX_PAGE_SIZE = 1000 + + def __init__(self, api_client): + self.api_client = api_client + self.logger = logger.set_logger(__name__) + self.connector = __name__.split('.')[1] + + def create_results_connection(self, search_id, offset, length, metadata=None): + """ + Fetching the results using search id, offset and length + :param search_id: str, search id generated in transmit query + :param offset: str, offset value + :param length: str, length value + :param metadata: str + :return: dict + """ + return_obj = {} + response_dict = {} + response_text = {} + result = [] + result_count = 0 + local_result_count = 0 + + try: + if metadata: + result_count, next_page_token = metadata.split(':') + result_count = int(result_count) + total_records = int(length) + if abs(self.api_client.result_limit - result_count) < total_records: + total_records = abs(self.api_client.result_limit - result_count) + else: + result_count, next_page_token = 0, '0' + total_records = int(offset) + int(length) + if self.api_client.result_limit < total_records: + total_records = self.api_client.result_limit + + if total_records <= ResultsConnector.GCP_MAX_PAGE_SIZE: + page_size = total_records + else: + page_size = ResultsConnector.GCP_MAX_PAGE_SIZE + + if (result_count == 0 and next_page_token == '0') or ( + next_page_token != '0' and result_count < self.api_client.result_limit): + response_wrapper = self.api_client.get_search_results(search_id, next_page_token, page_size) + response_text = json.loads(response_wrapper[1]) + if response_wrapper[0].status == 200: + if 'detections' in response_text.keys(): + result_count += len(response_text['detections']) + local_result_count += len(response_text['detections']) + result.append(response_text['detections']) + while 'nextPageToken' in response_text.keys(): + if not metadata and result_count < total_records: + remaining_records = total_records - result_count + + elif metadata and local_result_count < total_records: + remaining_records = total_records - local_result_count + + else: + break + + if remaining_records > ResultsConnector.GCP_MAX_PAGE_SIZE: + page_size = ResultsConnector.GCP_MAX_PAGE_SIZE + else: + page_size = remaining_records + + next_page_token = response_text['nextPageToken'] + next_response = self.api_client.get_search_results(search_id, + next_page_token, + page_size) + response_text = json.loads(next_response[1]) + if next_response[0].status == 200: + if 'detections' in response_text.keys(): + result_count += len(response_text['detections']) + local_result_count += len(response_text['detections']) + result.append(response_text['detections']) + else: + return_obj = self.invalid_response(return_obj, response_dict, + next_response[0].status, response_text) + result = [] + break + + if result: + return_obj['success'] = True + final_result = ResultsConnector.get_results_data(result) + if metadata: + return_obj['data'] = final_result if final_result else [] + else: + return_obj['data'] = final_result[int(offset): total_records] if final_result else [] + return_obj['metadata'] = str(result_count) + ':' + response_text.get('nextPageToken', '0') + else: + if not return_obj.get('error') and return_obj.get('success') is not False: + return_obj['success'] = True + return_obj['data'] = [] + + else: + return_obj = self.invalid_response(return_obj, response_dict, response_wrapper[0].status, + response_text) + else: + return_obj['success'] = True + return_obj['data'] = [] + + except ServerNotFoundError: + response_dict['code'] = 1010 + response_dict['message'] = "Invalid Host" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except RefreshError: + response_dict['code'] = 1015 + response_dict['message'] = "Invalid Client Email" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ValueError as r_ex: + if 'Could not deserialize key data' in str(r_ex): + response_dict['message'] = r_ex + response_dict['code'] = 1015 + else: + response_dict['message'] = f'cannot parse {r_ex}' + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except Exception as exp: + if "timed out" in str(exp): + response_dict['code'] = 120 + response_dict['message'] = str(exp) + else: + response_dict['message'] = exp + self.logger.error('error when getting search results: %s', exp) + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + finally: + try: + if not return_obj.get('data') or result_count >= self.api_client.result_limit or \ + response_dict.get('message') or not response_text.get('nextPageToken'): + if 'code' in response_dict: + if response_dict['code'] not in (1010, 1015): + self.logger.debug("Deleting the search id in results_connector") + self.api_client.delete_search(search_id) + else: + self.logger.debug("Deleting the search id in results_connector") + self.api_client.delete_search(search_id) + + except Exception: + self.logger.info("User doesn't have permission to delete the search id") + return return_obj + + def invalid_response(self, return_obj, response_dict, status, response_text): + """ + handle error response + :return dict + """ + + response_dict['code'] = status + response_dict['message'] = response_text['error'].get('message') + ErrorResponder.fill_error(return_obj, response_dict, ['message'], + connector=self.connector) + return return_obj + + @staticmethod + def get_results_data(response_text): + + """ + Format the input results + :return list + """ + final_result = [] + + for log in response_text: + if isinstance(log, list): + for element in log: + result_dict = ResultsConnector.get_logs(element) + final_result.append(result_dict) + else: + result_dict = ResultsConnector.get_logs(log) + final_result.append(result_dict) + return final_result + + @staticmethod + def get_logs(element): + """ + Formats the input with detection and collectionElements + :return dict + """ + result_dict = {} + event = element['collectionElements'][0]['references'][0] + formatted_event = ResultsConnector.format_event_data(event) + detection = {"detection": element['detection'][0]} + result_dict.update(formatted_event) + result_dict.update(detection) + return result_dict + + @staticmethod + def format_event_data(events): + """ + formats the results data + :return dict + """ + for noun in ('principal', 'src', 'target'): + if noun in events['event'].keys(): + events = ResultsConnector.format_registry_result(events, noun) + events = ResultsConnector.format_software_result(events, noun) + events = ResultsConnector.format_user_account(events, noun) + if 'network' in events['event'].keys(): + events = ResultsConnector.format_network_event(events) + if 'securityResult' in events['event'].keys(): + events = ResultsConnector.format_security_result(events) + + return events + + @staticmethod + def format_user_account(events, noun): + """ + formats user account object + :param events: dict + :param noun: str + :return: dict + """ + if 'user' in events['event'][noun].keys(): + if 'userid' not in events['event'][noun]['user'].keys(): + if 'userDisplayName' in events['event'][noun]['user'] or \ + 'accountType' in events['event'][noun]['user'] or \ + 'windowsSid' in events['event'][noun]['user']: + events['event'][noun]['user'].update({'userid': 'UNAVAILABLE'}) + if 'emailAddresses' in events['event'][noun]['user'].keys(): + new_obj_list = [] + for email_list in events['event'][noun]['user']['emailAddresses']: + email = email_list.split(":") + for em in email: + if ResultsConnector.EMAIL_PATTERN.match(str(em)): + new_obj_list.append(em) + if new_obj_list: + events['event'][noun]['user']['emailAddresses'] = new_obj_list + else: + del events['event'][noun]['user']['emailAddresses'] + return events + + @staticmethod + def format_security_result(events): + """ + format the security result data + :param events: dict + :return: dict + """ + for result in range(len(events['event']['securityResult'])): + + if 'severity' in events['event']['securityResult'][result].keys(): + events['event']['securityResult'][result]['severity'] = \ + ResultsConnector.format_severity_result( + events['event']['securityResult'][result]['severity']) + + if 'category' in events['event']['securityResult'][result].keys(): + events['event']['securityResult'][result]['category'] = \ + ResultsConnector.format_category_result( + events['event']['securityResult'][result]['category'][0]) + else: + events['event']['securityResult'][result]['category'] = 'alert' + + return events + + @staticmethod + def format_severity_result(severity): + """ + convert the gcp chronicle severity value to stix value + :param severity: str + :return: int + """ + if severity == "INFORMATIONAL": + severity = 16 + if severity == "ERROR": + severity = 32 + elif severity == "LOW": + severity = 48 + elif severity == "MEDIUM": + severity = 64 + elif severity == "HIGH": + severity = 80 + elif severity == "CRITICAL": + severity = 100 + else: + severity = 16 + + return severity + + @staticmethod + def format_category_result(category): + """ + convert gcp chronicle category value to stix value + :param category: str + :return: str + """ + if category in ["SOFTWARE_SUSPICIOUS", "NETWORK_SUSPICIOUS", "NETWORK_CATEGORIZED_CONTENT", + "NETWORK_DENIAL_OF_SERVICE", "NETWORK_RECON", "NETWORK_COMMAND_AND_CONTROL", "EXPLOIT", + "DATA_EXFILTRATION", "DATA_AT_REST", "DATA_DESTRUCTION"]: + category = 'alert' + elif category == "POLICY_VIOLATION": + category = 'policy' + elif category in ["ACL_VIOLATION", "AUTH_VIOLATION"]: + category = 'violation' + elif category in ["SOFTWARE_MALICIOUS", "SOFTWARE_PUA", "NETWORK_MALICIOUS", "MAIL_SPAM", "MAIL_PHISHING", + "MAIL_SPOOFING"]: + category = 'threat' + return category + + @staticmethod + def format_registry_result(events, noun): + """ + format the registry event + :param events: dict + :param noun: string + :return: dict + """ + # remove registry object if registry key is not present + if 'registry' in events['event'][noun].keys(): + if 'registryKey' not in events['event'][noun]['registry'].keys() and \ + 'registryValueData' in events['event'][noun]['registry'].keys(): + del events['event'][noun]['registry'] + else: + # format registry value data + registry_value_dict = {} + registry_build_data = copy.deepcopy(events['event'][noun]['registry']) + events['event'][noun]['registry']['registryValues'] = [] + for key, value in registry_build_data.items(): + if key in ('registryValueName', 'registryValueData'): + registry_value_dict.update({key: value}) + events['event'][noun]['registry'].pop(key) + events['event'][noun]['registry']['registryValues'].append(registry_value_dict) + return events + + @staticmethod + def format_software_result(events, noun): + """ + format software data + :param events: dict + :param noun: string + :return: dict + """ + if 'asset' in events['event'][noun].keys(): + if 'platformSoftware' in events['event'][noun]['asset'].keys(): + if "platform" not in events['event'][noun]['asset']['platformSoftware'].keys(): + events['event'][noun]['asset']['platformSoftware']['platform'] = 'UNKNOWN_PLATFORM' + if 'software' in events['event'][noun]['asset'].keys(): + if "name" not in events['event'][noun]['asset']['software'].keys(): + events['event'][noun]['asset']['software']['name'] = 'unknown' + return events + + @staticmethod + def format_network_event(events): + """ + format network data + :param events: dict + :return: dict + """ + network_keys = events['event']['network'].keys() + # set is_multipart with false as default if there is email message property + if 'email' in network_keys: + if 'subject' in events['event']['network']['email'].keys(): + events['event']['network']['email']['isMultipart'] = False + # validate the email + net_email_copy = copy.deepcopy(events['event']['network']['email']) + for mail_id in ['to', 'cc', 'bcc']: + new_obj_list = [] + if mail_id in net_email_copy: + for email_list in net_email_copy[mail_id]: + email = email_list.split(":") + for em in email: + if ResultsConnector.EMAIL_PATTERN.match(str(em)): + new_obj_list.append(em) + if new_obj_list: + events['event']['network']['email'][mail_id] = new_obj_list + else: + del events['event']['network']['email'][mail_id] + + # donot create network traffic object if there is no ip + if not (events['event'].get('principal', {}).get('ip') or events['event'].get('src', {}).get('ip') or + events['event'].get('target', {}).get('ip')): + network_copy = copy.deepcopy(events['event']['network']) + for proto in network_copy: + if proto not in ('email', 'asn', 'dns_domain'): + del events['event']['network'][proto] + else: + # set the values for protocol if it is not present for a network event + if 'applicationProtocol' not in network_keys and 'ipProtocol' not in network_keys: + for udm_protocol in ['dns', 'dhcp', 'http', 'smtp', 'ftp', 'tls']: + if udm_protocol in network_keys: + events['event']['network']['applicationProtocol'] = udm_protocol + if 'applicationProtocol' not in events['event']['network'].keys() \ + and (set(events['event']['network'].keys()) - set(['email', 'asn', 'dns_domain'])): + events['event']['network']['ipProtocol'] = 'tcp' + return events diff --git a/stix_shifter_modules/gcp_chronicle/stix_transmission/status_connector.py b/stix_shifter_modules/gcp_chronicle/stix_transmission/status_connector.py new file mode 100644 index 000000000..390d6ecdd --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/stix_transmission/status_connector.py @@ -0,0 +1,124 @@ +from stix_shifter_utils.modules.base.stix_transmission.base_status_connector import BaseStatusConnector +from stix_shifter_utils.modules.base.stix_transmission.base_status_connector import Status +from enum import Enum +from stix_shifter_utils.utils.error_response import ErrorResponder +from stix_shifter_utils.utils import logger +import json +from httplib2 import ServerNotFoundError +from google.auth.exceptions import RefreshError +import time + + +class InvalidResponseException(Exception): + pass + + +class GCPChronicleStatus(Enum): + RUNNING = 'RUNNING' + DONE = 'DONE' + CANCELLED = 'CANCELLED' + STATE_UNSPECIFIED = 'STATE_UNSPECIFIED' + + +class StatusConnector(BaseStatusConnector): + PROGRESS_PERCENTAGE = 0 + + def __init__(self, api_client): + self.api_client = api_client + self.logger = logger.set_logger(__name__) + self.connector = __name__.split('.')[1] + + @staticmethod + def get_status(status): + """ + Return the status of the search id + :param status: str, + :return: str + """ + switcher = { + GCPChronicleStatus.RUNNING.value: Status.RUNNING, + GCPChronicleStatus.DONE.value: Status.COMPLETED, + GCPChronicleStatus.CANCELLED.value: Status.CANCELED, + GCPChronicleStatus.STATE_UNSPECIFIED.value: Status.ERROR + } + return switcher.get(status).value + + def create_status_connection(self, search_id): + """ + Fetching the progress and the status of the search id + :param search_id: str, search id + :return: dict + """ + + return_obj = {} + response_dict = {} + try: + response_wrapper = self.api_client.get_search_status(search_id) + response_text = json.loads(response_wrapper[1]) + + if response_wrapper[0].status == 200: + if 'state' in response_text.keys(): + if response_text['state'] in ("RUNNING", "CANCELLED"): + return_obj['success'] = True + return_obj['progress'] = response_text['progressPercentage'] \ + if 'progressPercentage' in response_text.keys() else 0 + StatusConnector.PROGRESS_PERCENTAGE = return_obj['progress'] + return_obj['status'] = StatusConnector.get_status(response_text['state']) + + elif response_text['state'] == "DONE": + return_obj['success'] = True + return_obj['progress'] = 100 + return_obj['status'] = StatusConnector.get_status(response_text['state']) + + elif response_text['state'] == "STATE_UNSPECIFIED": + return_obj['success'] = False + return_obj['status'] = StatusConnector.get_status(response_text['state']) + return_obj['progress'] = 0 + else: + raise InvalidResponseException + + else: + # sleep of 1 sec has been added to reduce the resource exhaustion in data source + if response_wrapper[0].status == 429: + return_obj['success'] = True + return_obj['status'] = "RUNNING" + return_obj['progress'] = StatusConnector.PROGRESS_PERCENTAGE + time.sleep(1) + return return_obj + response_dict['code'] = response_wrapper[0].status + response_dict['message'] = response_text['error'].get('message') + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ServerNotFoundError: + response_dict['code'] = 1010 + response_dict['message'] = "Invalid Host" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except InvalidResponseException: + response_dict['code'] = 100 + response_dict['message'] = "Invalid Response" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except RefreshError: + response_dict['code'] = 1015 + response_dict['message'] = "Invalid Client Email" + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except ValueError as v_err: + if 'Could not deserialize key data' in str(v_err): + response_dict['message'] = v_err + response_dict['code'] = 1015 + else: + response_dict['message'] = f'cannot parse {v_err}' + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + except Exception as err: + if "timed out" in str(err): + response_dict['code'] = 120 + response_dict['message'] = str(err) + else: + response_dict['message'] = err + self.logger.error('error when getting search results: %s', err) + ErrorResponder.fill_error(return_obj, response_dict, ['message'], connector=self.connector) + + return return_obj diff --git a/stix_shifter_modules/gcp_chronicle/tests/stix_translation/test_gcp_chronicle_json_to_stix.py b/stix_shifter_modules/gcp_chronicle/tests/stix_translation/test_gcp_chronicle_json_to_stix.py new file mode 100644 index 000000000..676ee88d5 --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/tests/stix_translation/test_gcp_chronicle_json_to_stix.py @@ -0,0 +1,791 @@ +import unittest +from stix_shifter_modules.gcp_chronicle.entry_point import EntryPoint +from stix_shifter_utils.stix_translation.src.json_to_stix import json_to_stix_translator +from stix_shifter_utils.stix_translation.src.utils.transformer_utils import get_module_transformers + +ENTRY_POINT = EntryPoint() + +MODULE = "gcp_chronicle" +options = {} +map_data = ENTRY_POINT.get_results_translator().map_data + +COMMON_DATA = { + "event": { + "metadata": { + "productLogId": "10190", + "eventTimestamp": "2022-06-13T14:14:54.409074800Z", + "eventType": "NETWORK_CONNECTION", + "vendorName": "Microsoft", + "productName": "AdvancedHunting-DeviceNetworkEvents", + "productEventType": "DeviceNetworkEvents", + "ingestedTimestamp": "2022-06-14T05:10:46.984602Z" + }, + "principal": { + "hostname": "alert-windows", + "assetId": "DeviceId:4f22ab5dc4be96566ee3c9adb3b77280dc08bfdb", + "asset": { + "attribute": { + "cloud": { + "environment": "GOOGLE_CLOUD_PLATFORM" + } + } + }, + "location": { + "countryOrRegion": "United States" + }, + "user": { + "userid": "system", + "windowsSid": "S-1-5-18", + "emailAddresses": ["test@user.com"] + }, + "process": { + "pid": "2788", + "file": { + "sha256": "2b3efaca2e57e433e6950286f7a6fb46ed48411322a26d657e58f02f7d232224", + "md5": "53a2c077e868af30525019e9d070eddd", + "sha1": "ed68d965d3572218fa5b17b54e7726df3b18dee3", + "size": "56352", + "fullPath": "c:\\windows\\system32\\svchost.exe" + }, + "commandLine": "svchost.exe -k utcsvc -p", + "parentProcess": { + "pid": "756", + "file": { + "fullPath": "services.exe" + } + } + }, + "ip": [ + "1.0.1.1" + ], + + "port": 52221, + "administrativeDomain": "nt authority" + }, + "target": { + "url": "v20.events.data.microsoft.com", + "ip": [ + "1.0.1.2" + ], + "port": 443 + }, + "securityResult": [ + { + "category": "alert", + "summary": "ConnectionSuccess", + "action": [ + "ALLOW" + ]}], + "network": { + "ipProtocol": "TCP", + "direction": "OUTBOUND" + } + }} + +DATA_SOURCE = { + "type": "identity", + "id": "identity--3532c56d-ea72-48be-a2ad-1a53f4c9c6d3", + "name": "gcp_chronicle", + "identity_class": "events" +} + + +class TestGcpChronicleResultsToStix(unittest.TestCase): + """ + class to perform unit test case for google chronicle security translate results + """ + + @staticmethod + def get_first(itr, constraint): + return next( + (obj for obj in itr if constraint(obj)), + None + ) + + @staticmethod + def get_first_of_type(itr, typ): + return TestGcpChronicleResultsToStix.get_first(itr, lambda o: type(o) == dict and o.get('type') == typ) + + @staticmethod + def get_observed_data_objects(data): + result_bundle = json_to_stix_translator.convert_to_stix( + DATA_SOURCE, map_data, [data], get_module_transformers(MODULE), options) + result_bundle_objects = result_bundle['objects'] + result_bundle_identity = result_bundle_objects[0] + assert result_bundle_identity['type'] == DATA_SOURCE['type'] + observed_data = result_bundle_objects[1] + assert 'objects' in observed_data + return observed_data['objects'] + + def test_network_traffic_object(self): + data = { + 'event': { + 'principal': { + 'ip': ['1.0.0.3'], + 'port': 53, + 'mac': ['6e:b7:31:d5:33:6c'], + }, + 'target': { + 'ip': ['1.0.0.4'], + 'port': 1172 + }, + 'network': { + 'sentBytes': '326', + 'ipProtocol': 'UDP', + 'applicationProtocol': 'DNS', + 'direction': 'OUTBOUND', + 'sessionDuration': '7s', + 'sessionId': '22588147' + } + }} + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + network_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'network-traffic') + assert (network_obj is not None), 'network-traffic object type not found' + assert (network_obj.keys() == {'type', 'src_ref', 'src_port', 'dst_ref', 'dst_port', 'src_byte_count', + 'protocols', 'extensions'}) + assert (network_obj['type'] == 'network-traffic') + assert (network_obj['src_port'] == 53) + assert (network_obj['dst_port'] == 1172) + assert (network_obj['protocols'] == ['udp', 'dns']) + assert (network_obj['extensions']['x-gcp-chronicle-network']['session_duration'] == '7s') + assert (network_obj['extensions']['x-gcp-chronicle-network']['session_id'] == '22588147') + assert (network_obj['extensions']['x-gcp-chronicle-network']['direction'] == 'OUTBOUND') + + ip_ref = network_obj['src_ref'] + assert (ip_ref in objects), f"src_ref with key {network_obj['src_ref']} not found" + ip_obj = objects[ip_ref] + assert (ip_obj.keys() == {'type', 'value', 'resolves_to_refs'}) + assert (ip_obj['type'] == 'ipv4-addr') + assert (ip_obj['value'] == '1.0.0.3') + + mac_ref = ip_obj['resolves_to_refs'] + for mac in mac_ref: + assert (mac in objects), f"resolves_to_refs with key {mac} not found" + mac_obj = objects[mac] + assert (mac_obj['type'] == 'mac-addr') + + ip_ref = network_obj['dst_ref'] + assert (ip_ref in objects), f"dst_ref with key {network_obj['dst_ref']} not found" + ip_obj = objects[ip_ref] + assert (ip_obj.keys() == {'type', 'value'}) + assert (ip_obj['type'] == 'ipv4-addr') + assert (ip_obj['value'] == '1.0.0.4') + + def test_process_and_file_object(self): + data = { + "event": { + 'metadata': { + 'eventTimestamp': '2022-06-06T11:50:45.777108800Z', + 'eventType': 'PROCESS_LAUNCH' + }, + "principal": { + "hostname": "alert-windows", + "user": { + "userid": "system", + "windowsSid": "S-1-5-18" + }, + "process": { + "pid": "1132", + "file": { + "sha256": "bc866cfcdda37e24dc2634dc282c7a0e6f55209da17a8fa105b07414c0e7c527", + "md5": "911d039e71583a07320b32bde22f8e22", + "sha1": "ded8fd7f36417f66eb6ada10e0c0d7c0022986e9", + "size": "278528", + "fullPath": "c:\\windows\\system32\\cmd.exe" + }, + "commandLine": "cmd.exe /c \"\"C:\\Packages\\Plugins\\Microsoft.Azure.AzureDefenderFor" + "Servers.MDE.Windows\\1.0.0.4\\MdeExtensionHandler.cmd\" enable\"", + "parentProcess": { + "pid": "3068", + "file": { + "fullPath": "WindowsAzureGuestAgent.exe" + } + } + }, + "administrativeDomain": "nt authority" + }, + "target": { + "process": { + "pid": "1428", + "file": { + "sha256": "de96a6e69944335375dc1ac238336066889d9ffc7d73628ef4fe1b1b160ab32c", + "md5": "7353f60b1739074eb17c5f4dddefe239", + "sha1": "6cbce4a295c163791b60fc23d285e6d84f28ee4c", + "size": "448000", + "fullPath": "powershell.exe" + }, + "commandLine": "Powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass " + "-File C:\\Packages\\Plugins\\Microsoft.Azure.AzureDefenderForServers." + "MDE.Windows\\1.0.0.4\\\\MdeExtensionHandler.ps1 -Action enable" + }, + "file": { + "fullPath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" + } + } + }} + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + proc_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'process') + + assert (proc_obj is not None), 'process object type not found' + assert (proc_obj.keys() == {'type', 'creator_user_ref', 'pid', 'binary_ref', 'name', 'command_line', + 'parent_ref'}) + + assert (proc_obj['type'] == 'process') + assert (proc_obj['pid'] == 1132) + assert (proc_obj['name'] == 'cmd.exe') + assert (proc_obj['command_line'] == 'cmd.exe /c ""C:\\Packages\\Plugins\\Microsoft.Azure.AzureDefender' + 'ForServers.MDE.Windows\\1.0.0.4\\MdeExtensionHandler.cmd" enable"') + + user_ref = proc_obj['creator_user_ref'] + assert (user_ref in objects), f"creator_user_ref with key {proc_obj['creator_user_ref']} not found" + + binary_ref = proc_obj['binary_ref'] + assert (binary_ref in objects), f"binary_ref with key {proc_obj['binary_ref']} not found" + + binary = objects[binary_ref] + assert (binary.keys() == {'type', 'parent_directory_ref', 'name', 'hashes', 'size'}) + assert (binary['name'] == 'cmd.exe') + assert (binary['hashes']['SHA-256'] == 'bc866cfcdda37e24dc2634dc282c7a0e6f55209da17a8fa105b07414c0e7c527') + assert (binary['size'] == 278528) + assert (binary['parent_directory_ref'] in objects), \ + f"binary.parent_directory_ref with key {binary['parent_directory_ref']} not found" + assert (objects[binary['parent_directory_ref']]['path'] == 'c:\\windows\\system32') + + parent_ref = proc_obj['parent_ref'] + + assert (parent_ref in objects), f"parent_ref with key {proc_obj['parent_ref']} not found" + parent = objects[parent_ref] + assert (parent['name'] == 'WindowsAzureGuestAgent.exe') + assert (objects[parent['binary_ref']]['name'] == 'WindowsAzureGuestAgent.exe') + + def test_x_oca_event(self): + + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(COMMON_DATA) + event = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'x-oca-event') + assert (event['type']) == 'x-oca-event' + assert (event['provider']) == 'Microsoft' + assert (event['created'] == '2022-06-13T14:14:54.409074800Z') + assert (event['action'] == 'NETWORK_CONNECTION') + assert (event['agent'] == 'AdvancedHunting-DeviceNetworkEvents') + assert (event['outcome'] == 'DeviceNetworkEvents') + for ref in ['host_ref', 'user_ref', 'file_ref', 'process_ref', 'parent_process_ref', 'ip_refs', 'network_ref']: + references = event[ref] + if isinstance(references, str): + assert (references in objects), f"{event[ref]} reference object not found" + else: + assert (all(ref_obj in objects for ref_obj in references)), f"one of reference object {event[ref]}" \ + f" not found " + + def test_x_ibm_finding(self): + data = { + 'event': { + 'metadata': { + 'productLogId': '287203', + 'eventTimestamp': '2022-07-28T07:57:16.074005Z', + 'eventType': 'NETWORK_CONNECTION' + }, + 'principal': { + 'ip': ['10.0.18.224'], + 'asset': { + 'ip': ['10.0.18.224'], + 'platformSoftware': { + 'platform': 'windows', + 'platformVersion': 'windows 10' + } + } + }, + 'target': { + 'ip': ['172.217.169.36'], + 'port': 443, + 'location': { + 'countryOrRegion': 'United Kingdom', + 'regionLatitude': 55.37805, + 'regionLongitude': -3.435973 + }, + 'asset': { + 'ip': ['172.217.169.36'] + } + }, + 'securityResult': [{ + 'about': [ + { + 'url': 'https://testurl.com' + } + ], + 'category': 'alert', + 'summary': 'Anomalous Connection/1 GiB Outbound', + 'action': ['UNKNOWN_ACTION'], + 'severity': 80 + }], + 'network': { + 'ipProtocol': 'TCP' + } + } + } + + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + finding = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'x-ibm-finding') + assert (finding is not None), "x-ibm-finding not found" + + assert (finding.keys() == {'type', 'src_ip_ref', 'src_os_ref', 'dst_ip_ref', 'extensions', 'finding_type', + 'name', 'severity'}) + assert (finding['type'] == "x-ibm-finding") + ip_ref = finding['src_ip_ref'] + assert (ip_ref in objects), f"src_ip_ref with key {finding['src_ip_ref']} not found" + ip_obj = objects[ip_ref] + assert (ip_obj.keys() == {'type', 'value'}) + assert (ip_obj['type'] == 'ipv4-addr') + assert (ip_obj['value'] == '10.0.18.224') + + ip_ref = finding['dst_ip_ref'] + assert (ip_ref in objects), f"dst_ip_ref with key {finding['dst_ip_ref']} not found" + ip_obj = objects[ip_ref] + assert (ip_obj.keys() == {'type', 'value'}) + assert (ip_obj['type'] == 'ipv4-addr') + assert (ip_obj['value'] == '172.217.169.36') + + os_ref = finding['src_os_ref'] + assert (os_ref in objects), f"src_os_ref with key {finding['src_os_ref']} not found" + os_obj = objects[os_ref] + assert (os_obj.keys() == {'type', 'name', 'version'}) + assert (os_obj['type'] == 'software') + assert (os_obj['name'] == 'windows') + assert (os_obj['version'] == 'windows 10') + + url_ref = finding['extensions']['x-gcp-chronicle-security-result']['url_ref'] + assert (url_ref in objects), f"url_ref with key " \ + f"{finding['extensions']['x-gcp-chronicle-security-result']['url_ref']} not found" + url_obj = objects[url_ref] + assert (url_obj.keys() == {'type', 'value'}) + assert (url_obj['type'] == 'url') + assert (url_obj['value'] == 'https://testurl.com') + + assert (finding['finding_type'] == 'alert') + assert (finding['name'] == 'Anomalous Connection/1 GiB Outbound') + assert (finding['extensions']['x-gcp-chronicle-security-result']['actions_taken'] == ['UNKNOWN_ACTION']) + assert (finding['severity'] == 80) + + def test_asset_property(self): + + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(COMMON_DATA) + principal_asset = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'x-oca-asset') + + assert (principal_asset is not None), "principal x-oca-asset not found" + assert (principal_asset.keys() == {'type', 'hostname', 'ip_refs', 'extensions'}) + assert (principal_asset['type'] == "x-oca-asset") + principal_ip_refs = principal_asset['ip_refs'] + assert (all( + ip in objects for ip in principal_ip_refs)), f"one of the ip refs with key {principal_asset['ip_refs']} " \ + f"not found" + for ip in principal_ip_refs: + assert (objects[ip]['type'] == 'ipv4-addr') + + assert principal_asset['extensions']['x-gcp-chronicle-asset']['asset_id'] == \ + 'DeviceId:4f22ab5dc4be96566ee3c9adb3b77280dc08bfdb' + assert principal_asset['extensions']['x-gcp-chronicle-asset']['cloud_environment'] == 'GOOGLE_CLOUD_PLATFORM' + assert principal_asset['extensions']['x-gcp-chronicle-asset']['country_or_region'] == 'United States' + + def test_http_network_and_resource_property(self): + data = { + 'event': { + 'metadata': { + 'productLogId': 'skyvfhe9a7a7', + 'eventTimestamp': '2022-07-11T06:59:02.567386Z', + 'collectedTimestamp': '2022-07-11T06:59:02.761440419Z', + 'eventType': 'NETWORK_HTTP', + 'vendorName': 'Google Cloud Platform', + 'productName': 'GCP BigQuery', + 'productEventType': 'google.cloud.bigquery.v2.JobService.InsertJob', + 'ingestedTimestamp': '2022-07-11T06:59:03.925957Z' + }, + 'principal': { + 'user': { + 'emailAddresses': ['1234@developer.gserviceaccount.com'] + }, + 'ip': ['1.0.0.5'], + 'location': { + 'countryOrRegion': 'United States', + 'regionLatitude': 37.09024, + 'regionLongitude': -95.71289 + }, + 'labels': [{ + 'key': 'requestMetadata.callerNetwork', + 'value': 'Request originated from a GCE VM or an on-prem VM behind a VPN, and the log was' + ' enhanced with additional information : REPLACED' + }] + }, + 'target': { + 'application': 'bigquery.googleapis.com', + 'resource': { + 'name': 'batch_JSON2bq', + 'parent': 'coe_dataset', + 'resourceType': 'TABLE', + 'resourceSubtype': 'bigquery' + }, + 'cloud': { + 'environment': 'GOOGLE_CLOUD_PLATFORM', + 'project': { + 'name': 'gdc-day0-data-poc' + } + } + }, + 'securityResult': [{ + "category": "alert", + 'categoryDetails': ['projects/gdc-day0-data-poc/logs/cloudaudit.googleapis.com%2Factivity'], + 'action': ['ALLOW'], + 'severity': '16', + 'detectionFields': [{ + 'key': 'resource', + 'value': 'projects/gdc-day0-data-poc/datasets/coe_dataset/tables/batch_JSON2bq' + }, { + 'key': 'resource_name', + 'value': 'projects/gdc-day0-data-poc/datasets/coe_dataset/tables/batch_JSON2bq' + }] + }], + 'network': { + 'applicationProtocol': 'http', + 'http': { + 'method': 'google.cloud.bigquery.v2.JobService.InsertJob', + 'userAgent': 'apache-beam-2.39.0,gzip(gfe)' + } + } + }} + + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + + http_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'network-traffic') + assert (http_obj is not None), "network object is not found" + assert (http_obj.keys() == {'type', 'src_ref', 'protocols', 'extensions'}) + assert (http_obj['type'] == 'network-traffic') + + assert (http_obj['extensions']['http-ext']['request_method'] == 'google.cloud.bigquery.v2.' + 'JobService.InsertJob') + assert (http_obj['extensions']['http-ext']['user_agent'] == 'apache-beam-2.39.0,gzip(gfe)') + + resource_ref = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'x-oca-event') + assert (resource_ref is not None), "event object is not found" + assert (resource_ref['extensions']['x-gcp-chronicle-event']['target_resource_ref'] in objects), \ + f"resource reference object is not found" + assert (objects[resource_ref['extensions']['x-gcp-chronicle-event']['target_resource_ref']]['type'] + == 'x-gcp-chronicle-resource') + + resource_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'x-gcp-chronicle-resource') + assert (resource_obj is not None), f"resource object is not found" + assert (resource_obj.keys() == {'type', 'name', 'resource_type', 'resource_subtype'}) + assert (resource_obj['type'] == 'x-gcp-chronicle-resource') + assert (resource_obj['name'] == 'batch_JSON2bq') + assert (resource_obj['resource_type'] == 'TABLE') + assert (resource_obj['resource_subtype'] == 'bigquery') + + def test_network_dns_property(self): + data = { + "event": { + "metadata": { + "productLogId": "1033u5mdga0v", + "eventTimestamp": "2022-07-04T22:24:03.915513Z", + "collectedTimestamp": "2022-07-04T22:24:05.197143232Z", + "eventType": "NETWORK_DNS", + "vendorName": "Google Cloud Platform", + "productName": "Google Cloud DNS", + "ingestedTimestamp": "2022-07-04T22:24:11.207624Z" + }, + "additional": { + "response_code": "NXDOMAIN" + }, + "principal": { + "ip": [ + "2405:200:1604:1969:78::5" + ], + "location": { + "name": "global" + }, + "resource": { + "resourceType": "VIRTUAL_MACHINE" + }, + "cloud": { + "environment": "GOOGLE_CLOUD_PLATFORM", + "project": { + "name": "hostproject-test" + } + } + }, + "target": { + "ip": [ + "2001:4860:4802:32::6e" + ], + "location": { + "countryOrRegion": "United States", + "regionLatitude": 37.09024, + "regionLongitude": -95.71289 + } + }, + "network": { + "ipProtocol": "UDP", + "applicationProtocol": "DNS", + "dns": { + "questions": [ + { + "name": "www.a2k2.in", + "type": 1 + } + ], + "authoritative": True, + "responseCode": 3 + } + } + }} + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + dns_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'network-traffic') + assert (dns_obj is not None), "network object is not found" + assert (dns_obj.keys() == {'type', 'src_ref', 'dst_ref', 'protocols', 'extensions'}) + assert (dns_obj['type'] == 'network-traffic') + assert (dns_obj['extensions']['dns-ext']['questions'][0]['type'] == 1) + assert (dns_obj['extensions']['dns-ext']['response_code'] == 3) + assert (dns_obj['extensions']['dns-ext']['questions'][0]['name'] == "www.a2k2.in") + + def test_email_property(self): + + data = { + 'event': { + 'metadata': { + 'productLogId': 'MYhRuvkA0N4NtUSOmiBIumilYcKRlBcy', + 'eventTimestamp': '2022-06-22T11:19:20Z', + 'eventType': 'EMAIL_TRANSACTION', + 'vendorName': 'PROOFPOINT', + 'productName': 'TAP', + 'productEventType': 'messagesDelivered', + 'ingestedTimestamp': '2022-06-22T11:43:29.036379Z' + }, + 'additional': { + 'headerFrom': 'test user1 ', + 'phishScore': 0, + 'spamScore': 0 + }, + 'principal': { + 'user': { + 'emailAddresses': ['user1@iscgalaxy.com'] + }, + 'ip': ['1.0.1.8'] + }, + 'target': { + 'user': { + 'emailAddresses': ['user2@iscgalaxy.com'] + } + }, + 'intermediary': [{ + 'user': { + 'emailAddresses': ['user123@iscgalaxy.com', 'user2@iscgalaxy.com'] + } + }], + 'about': [{ + 'file': { + 'sha256': 'b025e6114db79f3a891740c45ed264231ec77b07ab9e7ff9156b6d73eb35861e', + 'md5': 'ff357be90b834ed71da60df190124592', + 'fullPath': 'text.txt', + 'mimeType': 'text/plain' + } + }], + 'securityResult': [{ + 'about': { + 'url': 'https://testurl.com' + }, + 'category': 'threat', + 'categoryDetails': ['malware'], + 'threatName': 'url', + 'action': ['ALLOW_WITH_MODIFICATION'], + 'threatId': 'd00309e12e797021511111456056ff434c2b85c908d72b8c0dc138259182b9a0', + 'threatStatus': 'ACTIVE', + 'detectionFields': [{ + 'key': 'completelyRewritten', + 'value': 'True' + }] + }], + 'network': { + 'email': { + 'from': '010001818b22f94b-c1c1b98b-16f5-40d0-a911-a7f562a5d1d1-000000@amazonses.com', + 'to': ['user2@iscgalaxy.com'], + 'mailId': '010001818b22f94b-c1c1b98b-16f5-40d0-a911-a7f562a5d1d1-000000@email.amazonses.com', + 'subject': ['https://testurl.com'], + 'isMultipart': False + } + } + }} + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + email_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'email-message') + assert (email_obj is not None), " email message object is not found" + assert (email_obj.keys() == {'type', 'extensions', 'from_ref', 'to_refs', 'subject', 'is_multipart'}) + assert (email_obj['type'] == 'email-message') + assert (email_obj['subject'] == 'https://testurl.com') + assert (email_obj['is_multipart'] is not True) + + from_ref = email_obj['from_ref'] + assert (from_ref in objects), f"from_ref with key {email_obj['from_ref']} not found" + assert (objects[from_ref].keys() == {'type', 'value'}) + assert (objects[from_ref]['type'] == 'email-addr') + assert (objects[from_ref]['value'] == '010001818b22f94b-c1c1b98b-16f5-40d0-a911-a7f562a5d1d1-' + '000000@amazonses.com') + + to_refs = email_obj['to_refs'] + assert (all(to_r in objects for to_r in to_refs)), f"to_refs with key {email_obj['to_refs']} not found" + for to_obj in to_refs: + assert (objects[to_obj]['type'] == 'email-addr') + assert (objects[to_obj]['value'] == 'user2@iscgalaxy.com') + + file_ref = email_obj['extensions']['x-gcp-chronicle-email-message']['file_ref'] + assert (file_ref in objects), f"file_ref with key " \ + f"{email_obj['extensions']['x-gcp-chronicle-email-message']['file_ref']} " \ + f"not found" + assert (objects[file_ref].keys() == {'type', 'hashes', 'name', 'extensions'}) + assert (objects[file_ref]['type'] == 'file') + + def test_registry_property(self): + data = { + 'event': { + 'metadata': { + 'productLogId': '10303', + 'eventTimestamp': '2022-06-13T14:31:59.655075Z', + 'eventType': 'REGISTRY_MODIFICATION', + 'vendorName': 'Microsoft', + 'productName': 'AdvancedHunting-DeviceRegistryEvents', + 'productEventType': 'DeviceRegistryEvents', + 'ingestedTimestamp': '2022-06-14T04:48:32.535773Z' + }, + 'principal': { + 'hostname': 'alert-windows', + 'assetId': 'DeviceId:4f22ab5dc4be96566ee3c9adb3b77280dc08bfdb', + 'user': { + 'userid': 'system', + 'windowsSid': 'S-1-5-18' + }, + 'process': { + 'pid': '2272', + 'file': { + 'sha256': 'dc6995f97212edf6d7b73bdaa7f6cfc50430d0b081fa1bd48b995f4347f32aa7', + 'md5': '9079dd01c8d9e2ca2b0d10108f2feda9', + 'sha1': 'e60fe04143860f887b42fa368a4426856e892f41', + 'size': '7755352', + 'fullPath': 'c:\\program files\\windows defender advanced threat protection\\mssense.exe' + }, + 'commandLine': '"MsSense.exe"', + 'parentProcess': { + 'pid': '756', + 'file': { + 'fullPath': 'services.exe' + } + } + }, + 'administrativeDomain': 'nt authority' + }, + 'src': {}, + 'target': { + 'registry': { + 'registryKey': 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Advanced Threat Protection', + 'registryValues': [{'registryValueName': 'CrashHeartbeat', + 'registryValueData': '132996043194369129' + }] + + } + }, + 'observer': { + 'cloud': { + 'project': { + 'id': 'b73e5ba8-34d5-495a-9901-06bdb84cf13e' + } + } + }, + 'about': [{ + 'labels': [{ + 'key': 'InitiatingProcessTokenElevation', + 'value': 'TokenElevationTypeDefault' + }, { + 'key': 'InitiatingProcessIntegrityLevel', + 'value': 'System' + }] + }], + 'securityResult': [{ + "category": "alert", + 'summary': 'RegistryValueSet' + }] + } + } + + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + + registry_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'windows-registry-key') + + assert (registry_obj is not None), " windows registry key object is not found" + assert (registry_obj.keys() == {'type', 'key', 'values'}) + assert (registry_obj['type'] == 'windows-registry-key') + + assert (registry_obj['key'] == 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Advanced Threat Protection') + assert (registry_obj['values'][0]['data'] == '132996043194369129') + assert (registry_obj['values'][0]['name'] == 'CrashHeartbeat') + + def test_certificate_property(self): + data = {'event': { + 'metadata': { + 'productLogId': 'CH3QeG4kCxFL8eZrs1', + 'eventTimestamp': '2022-07-17T14:01:26.194646Z', + 'eventType': 'NETWORK_CONNECTION', + 'productEventType': 'bro_ssl', + 'description': 'SSL/TLS handshake info', + 'ingestedTimestamp': '2022-08-03T08:47:52.108774Z', + 'id': 'AAAAAAPkQFNb8/5zI4T/1aiVxAEAAAAABQAAAAIAAAA=' + }, + 'principal': { + 'ip': ['192.168.4.37'], + 'port': 58842 + }, + 'target': { + 'hostname': 'www.google.com', + 'ip': ['172.217.15.100'], + 'port': 443, + 'location': { + 'countryOrRegion': 'United States', + 'regionLatitude': 37.09024, + 'regionLongitude': -95.71289 + } + }, + 'network': { + 'tls': { + 'client': { + 'ja3': '3830b2a4fbcea64e74db382e467f5b3b' + }, + 'server': { + 'ja3s': '907bf3ecef1c987c889946b737b43de8', + 'certificate': { + 'subject': 'CN=www.taosecurity.com', + 'issuer': 'CN=Amazon,OU=Server CA 1B,O=Amazon,C=US' + } + }, + 'cipher': 'TLS_AES_256_GCM_SHA384', + 'curve': 'x25519', + 'version': 'TLSv13', + 'established': True, + 'resumed': True + }, + 'applicationProtocol': 'tls' + } + } + } + + objects = TestGcpChronicleResultsToStix.get_observed_data_objects(data) + + tls_obj = TestGcpChronicleResultsToStix.get_first_of_type(objects.values(), 'network-traffic') + assert (tls_obj is not None), "network-traffic object is not found" + assert (tls_obj.keys() == {'type', 'src_ref', 'src_port', 'dst_ref', 'dst_port', 'extensions', 'protocols'}) + assert (tls_obj['type'] == 'network-traffic') + assert (tls_obj['extensions']['tls-ext']['client_ja3_hash'] == '3830b2a4fbcea64e74db382e467f5b3b') + assert (tls_obj['extensions']['tls-ext']['server_ja3_hash'] == '907bf3ecef1c987c889946b737b43de8') + assert (tls_obj['extensions']['tls-ext']['cipher'] == 'TLS_AES_256_GCM_SHA384') + assert (tls_obj['extensions']['tls-ext']['elliptical_curve'] == 'x25519') + assert (tls_obj['extensions']['tls-ext']['version'] == 'TLSv13') + certificate_ref = tls_obj['extensions']['tls-ext']['server_certificate_ref'] + + assert (certificate_ref in objects), f"certificate reference object is not found" + certificate_obj = objects[certificate_ref] + assert (certificate_obj.keys() == {'type', 'subject', 'issuer'}) + assert (certificate_obj['type'] == 'x509-certificate') + + assert (certificate_obj['subject'] == 'CN=www.taosecurity.com') + assert (certificate_obj['issuer'] == 'CN=Amazon,OU=Server CA 1B,O=Amazon,C=US') diff --git a/stix_shifter_modules/gcp_chronicle/tests/stix_translation/test_gcp_chronicle_stix_to_query.py b/stix_shifter_modules/gcp_chronicle/tests/stix_translation/test_gcp_chronicle_stix_to_query.py new file mode 100644 index 000000000..24582bdbf --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/tests/stix_translation/test_gcp_chronicle_stix_to_query.py @@ -0,0 +1,540 @@ +from stix_shifter.stix_translation import stix_translation +from stix_shifter_utils.utils.error_response import ErrorCode +import unittest +import re + +translation = stix_translation.StixTranslation() + + +def _remove_timestamp_from_query(queries): + pattern1 = r"\s*rule_\d{0,10}" + pattern2 = r"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z)" + if isinstance(queries, list): + modified_queries = [] + for query in queries: + replace_pat1 = re.sub(pattern1, '', str(query)) + replace_pat2 = re.sub(pattern2, '{}', replace_pat1) + modified_queries.append(replace_pat2) + return modified_queries + elif isinstance(queries, str): + replace_pat1 = re.sub(pattern1, '', queries) + return re.sub(pattern2, '{}', replace_pat1) + + +class TestQueryTranslator(unittest.TestCase): + """ + class to perform unit test case gcp_chronicle translate query + """ + if __name__ == "__main__": + unittest.main() + + def _test_query_assertions(self, query, queries): + """ + to assert the each query in the list against expected result + """ + self.assertIsInstance(queries, list) + self.assertIsInstance(query, dict) + self.assertIsInstance(query['queries'], list) + for index, each_query in enumerate(query.get('queries'), start=0): + self.assertEqual(each_query, queries[index]) + + def test_ipv4_query(self): + stix_pattern = "[ipv4-addr:value = '168.149.184.42']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657866177 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: any $udm.src.ip = " + "\"168.149.184.42\" nocase or any $udm.target.ip = \"168.149.184.42\" nocase or any " + "$udm.principal.ip = \"168.149.184.42\" nocase condition: $udm}', 'startTime': " + "'2022-07-15T06:17:57.070Z', 'endTime': '2022-07-15T06:22:57.070Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_not_in_operator(self): + stix_pattern = "[x-ibm-finding:severity NOT IN (48,100)]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659622650 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ( all $udm.security_result.severity " + "!= \"LOW\" and all $udm.security_result.severity != \"UNKNOWN_SEVERITY\" ) and ( all " + "$udm.security_result.severity != \"CRITICAL\" and all $udm.security_result.severity != " + "\"UNKNOWN_SEVERITY\" ) condition: $udm}', 'startTime': '2022-08-04T14:12:30.243Z', 'endTime': " + "'2022-08-04T14:17:30.243Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_network_traffic_query(self): + stix_pattern = "[network-traffic:src_port = 52221]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657869892 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.src.port = 52221 or " + "$udm.principal.port = 52221 condition: $udm}', 'startTime': '2022-07-15T07:19:52.556Z', " + "'endTime': '2022-07-15T07:24:52.556Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_network_query_not_equals_operator(self): + stix_pattern = "[network-traffic:src_port != 52221]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659623807 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ( $udm.src.port != 52221 and " + "$udm.src.port != 0 ) or ( $udm.principal.port != 52221 and $udm.principal.port != 0 ) condition: " + "$udm}', 'startTime': '2022-06-03T00:00:00.000000Z', 'endTime': '2022-06-15T00:00:00.000000Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_network_query_greater_than_operator(self): + stix_pattern = "[network-traffic:src_port > 52221]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657870655 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.src.port > 52221 or " + "$udm.principal.port > 52221 condition: $udm}', 'startTime': '2022-07-15T07:32:35.696Z', " + "'endTime': '2022-07-15T07:37:35.696Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_network_query_not_greater_than_operator(self): + stix_pattern = "[network-traffic:src_port NOT > 52221]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659623971 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ( $udm.src.port <= 52221 and " + "$udm.src.port != 0 ) or ( $udm.principal.port <= 52221 and $udm.principal.port != 0 ) condition: " + "$udm}', 'startTime': '2022-08-04T14:34:31.496Z', 'endTime': '2022-08-04T14:39:31.496Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_network_query_less_than_operator(self): + stix_pattern = "[network-traffic:src_port < 52221]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659624239 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ( $udm.src.port < 52221 and " + "$udm.src.port != 0 ) or ( $udm.principal.port < 52221 and $udm.principal.port != 0 ) condition: " + "$udm}', 'startTime': '2022-08-04T14:38:59.430Z', 'endTime': '2022-08-04T14:43:59.430Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_network_query_less_than_or_equals(self): + stix_pattern = "[network-traffic:src_port <= 52221]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659624514 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ( $udm.src.port <= 52221 and " + "$udm.src.port != 0 ) or ( $udm.principal.port <= 52221 and $udm.principal.port != 0 ) condition: " + "$udm}', 'startTime': '2022-08-04T14:43:34.685Z', 'endTime': '2022-08-04T14:48:34.685Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_network_query_greater_than_or_equals(self): + stix_pattern = "[network-traffic:src_port >= 52221]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657871257 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.src.port >= 52221 or " + "$udm.principal.port >= 52221 condition: $udm}', 'startTime': '2022-07-15T07:42:37.847Z', " + "'endTime': '2022-07-15T07:47:37.847Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_query_in_operator(self): + stix_pattern = "[x-ibm-finding:finding_type IN ('threat'," \ + "'violation','alert','policy')] " + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1658996269 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: any $udm.security_result.category = " + "\"SOFTWARE_MALICIOUS\" or any $udm.security_result.category = \"SOFTWARE_PUA\" or any " + "$udm.security_result.category = \"NETWORK_MALICIOUS\" or any $udm.security_result.category = " + "\"MAIL_SPAM\" or any $udm.security_result.category = \"MAIL_PHISHING\" or any " + "$udm.security_result.category = \"MAIL_SPOOFING\" or any $udm.security_result.category = " + "\"ACL_VIOLATION\" or any $udm.security_result.category = \"AUTH_VIOLATION\" or any " + "$udm.security_result.category = \"SOFTWARE_SUSPICIOUS\" or any $udm.security_result.category = " + "\"NETWORK_SUSPICIOUS\" or any $udm.security_result.category = \"NETWORK_CATEGORIZED_CONTENT\" or " + "any $udm.security_result.category = \"NETWORK_DENIAL_OF_SERVICE\" or any " + "$udm.security_result.category = \"NETWORK_RECON\" or any $udm.security_result.category = " + "\"NETWORK_COMMAND_AND_CONTROL\" or any $udm.security_result.category = \"EXPLOIT\" or any " + "$udm.security_result.category = \"DATA_EXFILTRATION\" or any $udm.security_result.category = " + "\"DATA_AT_REST\" or any $udm.security_result.category = \"DATA_DESTRUCTION\" or any " + "$udm.security_result.category = \"POLICY_VIOLATION\" condition: $udm}', 'startTime': " + "'2022-07-28T08:12:49.775Z', 'endTime': '2022-07-28T08:17:49.775Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_file_query(self): + stix_pattern = "[file:hashes.'SHA-1' = '6cbce4a295c163791b60fc23d285e6d84f28ee4c']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657872913 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.src.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.src.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.principal.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.about.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase condition: $udm}', 'startTime': " + "'2022-07-15T08:10:13.278Z', 'endTime': '2022-07-15T08:15:13.278Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_valid_email_address_query(self): + stix_pattern = "[email-addr:value IN ('admin@hcl.com','user@hcl.com')]" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1658995859 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: (any " + "$udm.principal.user.email_addresses = \"admin@hcl.com\" nocase or any " + "$udm.principal.user.email_addresses = \"user@hcl.com\" nocase) or (any " + "$udm.src.user.email_addresses = \"admin@hcl.com\" nocase or any $udm.src.user.email_addresses = " + "\"user@hcl.com\" nocase) or (any $udm.target.user.email_addresses = \"admin@hcl.com\" nocase or " + "any $udm.target.user.email_addresses = \"user@hcl.com\" nocase) or ($udm.network.email.from = " + "\"admin@hcl.com\" nocase or $udm.network.email.from = \"user@hcl.com\" nocase) or (any " + "$udm.network.email.to = \"admin@hcl.com\" nocase or any $udm.network.email.to = \"user@hcl.com\" " + "nocase) or (any $udm.network.email.cc = \"admin@hcl.com\" nocase or any $udm.network.email.cc = " + "\"user@hcl.com\" nocase) or (any $udm.network.email.bcc = \"admin@hcl.com\" nocase or any " + "$udm.network.email.bcc = \"user@hcl.com\" nocase) or (any $udm.security_result.about.email = " + "\"admin@hcl.com\" nocase or any $udm.security_result.about.email = \"user@hcl.com\" nocase) " + "condition: $udm}', 'startTime': '2022-07-28T08:05:59.718Z', " + "'endTime': '2022-07-28T08:10:59.718Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_user_account_query(self): + stix_pattern = "[user-account:user_id = 'projectViewer:gdc-iac-day0-trng-03']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657873245 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.src.user.userid = " + "\"projectViewer:gdc-iac-day0-trng-03\" nocase or $udm.target.user.userid = " + "\"projectViewer:gdc-iac-day0-trng-03\" nocase or $udm.principal.user.userid = " + "\"projectViewer:gdc-iac-day0-trng-03\" nocase condition: $udm}', 'startTime': " + "'2022-07-15T08:15:45.325Z', 'endTime': '2022-07-15T08:20:45.325Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_mac_address_query(self): + stix_pattern = "[mac-addr:value ='12:83:0e:be:f3:1d']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657873407 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: any $udm.src.mac = " + "\"12:83:0e:be:f3:1d\" nocase or any $udm.target.mac = \"12:83:0e:be:f3:1d\" nocase or any " + "$udm.principal.mac = \"12:83:0e:be:f3:1d\" nocase condition: $udm}', 'startTime': " + "'2022-07-15T08:18:27.969Z', 'endTime': '2022-07-15T08:23:27.969Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_process_like_operator(self): + stix_pattern = "[process:name LIKE 'powershell.exe']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657873647 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.src.process.file.full_path = " + "/(?s)powershell\\\\.exe/ nocase or $udm.target.process.file.full_path = /(?s)powershell\\\\.exe/ " + "nocase or $udm.principal.process.file.full_path = /(?s)powershell\\\\.exe/ nocase or " + "$udm.target.process.parent_process.file.full_path = /(?s)powershell\\\\.exe/ nocase or " + "$udm.principal.process.parent_process.file.full_path = /(?s)powershell\\\\.exe/ nocase condition: " + "$udm}', 'startTime': '2022-07-15T08:22:27.039Z', 'endTime': '2022-07-15T08:27:27.039Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_http_network_matches_operator(self): + stix_pattern = "[network-traffic:extensions.'http-ext'.user_agent MATCHES '.\\\\w{5}/\\\\w{3}\\\\d\\\\d.']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657895850 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.network.http.user_agent = /(" + "?s).\\\\w{5}\\\\/\\\\w{3}\\\\d\\\\d./ nocase condition: " + "$udm}', 'startTime': '2022-07-15T14:32:30.572Z', 'endTime': '2022-07-15T14:37:30.572Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_http_network_not_matches_operator(self): + stix_pattern = "[network-traffic:extensions.'http-ext'.request_method NOT MATCHES " \ + "'v1.compute.instances.setMetadata'] " + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659625111 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: $udm.network.http.method != /(" + "?s)v1.compute.instances.setMetadata/ nocase and $udm.network.http.method != \"\" condition: " + "$udm}', 'startTime': '2022-08-04T14:53:31.720Z', 'endTime': '2022-08-04T14:58:31.720Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_format_timestamp_fields(self): + stix_pattern = "[file:modified >= '2022-04-01T11:00:00.000Z']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1658994681 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: " + "$udm.src.file.last_modification_time.seconds >= 1648810800 or " + "$udm.target.file.last_modification_time.seconds >= 1648810800 or " + "$udm.src.process.file.last_modification_time.seconds >= 1648810800 or " + "$udm.target.process.file.last_modification_time.seconds >= 1648810800 or " + "$udm.principal.process.file.last_modification_time.seconds >= 1648810800 or " + "$udm.about.file.last_modification_time.seconds >= 1648810800 condition: $udm}', 'startTime': " + "'2022-07-28T07:46:21.331Z', 'endTime': '2022-07-28T07:51:21.331Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_query_from_multiple_comparison_expressions_joined_by_OR(self): + stix_pattern = "[ipv4-addr:value = '10.0.1.4' OR network-traffic:src_port = '52221']" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1657883991 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ($udm.src.port = 52221 or " + "$udm.principal.port = 52221) or (any $udm.src.ip = \"10.0.1.4\" nocase or any $udm.target.ip = " + "\"10.0.1.4\" nocase or any $udm.principal.ip = \"10.0.1.4\" nocase) condition: $udm}', " + "'startTime': '2022-07-15T11:14:51.186Z', 'endTime': '2022-07-15T11:19:51.186Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_query_for_morethan_two_comparison_expressions_joined_by_OR(self): + stix_pattern = "[x-ibm-finding:name = 'user_change_password' OR file:hashes.'SHA-1' = " \ + "'6cbce4a295c163791b60fc23d285e6d84f28ee4c' OR process:name = 'powershell.exe'] " + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1660217055 { meta: author = \"ibm cp4s user\" description " + "= \"Create event rule that should generate detections\" events: ($udm.src.process.file.full_path " + "= /(?s)powershell\\\\.exe/ nocase or $udm.target.process.file.full_path = /(?s)powershell\\\\.exe/ " + "nocase or $udm.principal.process.file.full_path = /(?s)powershell\\\\.exe/ nocase or " + "$udm.target.process.parent_process.file.full_path = /(?s)powershell\\\\.exe/ nocase or " + "$udm.principal.process.parent_process.file.full_path = /(?s)powershell\\\\.exe/ nocase) or ((" + "$udm.src.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.file.sha1 " + "= \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.src.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.principal.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.about.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase) or any $udm.security_result.summary = " + "\"user_change_password\" nocase) condition: $udm}', 'startTime': '2022-08-11T11:19:15.254Z', " + "'endTime': '2022-08-11T11:24:15.254Z'}"] + + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_multiple_observation_with_and_without_qualifier_query(self): + stix_pattern = "[process:command_line != '/usr/sbin/freeradius -f']START t'2022-04-01T00:00:00.030Z' STOP " \ + "t'2022-04-07T00:00:00.030Z' AND [file:size >10 OR network-traffic:src_port <= 52221] " + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659678676 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ( $udm.src.process.command_line != " + "\"/usr/sbin/freeradius -f\" nocase and $udm.src.process.command_line != \"\" ) or ( " + "$udm.target.process.command_line != \"/usr/sbin/freeradius -f\" nocase and " + "$udm.target.process.command_line != \"\" ) or ( $udm.principal.process.command_line != " + "\"/usr/sbin/freeradius -f\" nocase and $udm.principal.process.command_line != \"\" ) or ( " + "$udm.target.process.parent_process.command_line != \"/usr/sbin/freeradius -f\" nocase and " + "$udm.target.process.parent_process.command_line != \"\" ) or ( " + "$udm.principal.process.parent_process.command_line != \"/usr/sbin/freeradius -f\" nocase and " + "$udm.principal.process.parent_process.command_line != \"\" ) condition: $udm}', 'startTime': " + "'2022-04-01T00:00:00.030Z', 'endTime': '2022-04-07T00:00:00.030Z'}", "{'ruleText': 'rule " + "cp4s_gcp_udi_rule_1659678676 { meta: author = \"ibm cp4s user\" description = \"Create event " + "rule that should generate detections\" events: (( $udm.src.port <= 52221 and $udm.src.port != 0 ) " + "or ( $udm.principal.port <= 52221 and $udm.principal.port != 0 )) or ($udm.src.file.size > 10 " + "or $udm.target.file.size > 10 or $udm.src.process.file.size > 10 or $udm.target.process.file.size " + "> 10 or $udm.principal.process.file.size > 10 or $udm.about.file.size > 10) condition: $udm}', " + "'startTime': '2022-08-05T05:46:16.557Z', 'endTime': '2022-08-05T05:51:16.557Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_combined_observation_AND(self): + stix_pattern = "[ipv4-addr:value = '10.0.1.4' AND network-traffic:src_port = '52221'] AND " \ + "[network-traffic:extensions.'http-ext'.user_agent = " \ + "'v1.compute.instances.setMetadata' AND process:command_line != '/usr/sbin/freeradius " \ + "-f'] START t'2022-04-01T11:00:00.000Z' STOP t'2022-04-07T11:00:00.003Z'" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659679133 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ($udm.src.port = 52221 or " + "$udm.principal.port = 52221) and (any $udm.src.ip = \"10.0.1.4\" nocase or any $udm.target.ip = " + "\"10.0.1.4\" nocase or any $udm.principal.ip = \"10.0.1.4\" nocase) condition: $udm}', " + "'startTime': '2022-08-05T05:53:53.500Z', 'endTime': '2022-08-05T05:58:53.500Z'}", "{'ruleText': " + "'rule cp4s_gcp_udi_rule_1659679133 { meta: author = \"ibm cp4s user\" description = \"Create " + "event rule that should generate detections\" events: (( $udm.src.process.command_line != " + "\"/usr/sbin/freeradius -f\" nocase and $udm.src.process.command_line != \"\" ) or " + "( $udm.target.process.command_line != \"/usr/sbin/freeradius -f\" nocase and " + "$udm.target.process.command_line != \"\" ) or ( $udm.principal.process.command_line != " + "\"/usr/sbin/freeradius -f\" nocase and $udm.principal.process.command_line != \"\" ) or " + "( $udm.target.process.parent_process.command_line != \"/usr/sbin/freeradius -f\" nocase and " + "$udm.target.process.parent_process.command_line != \"\" ) or ( $udm.principal.process." + "parent_process.command_line != \"/usr/sbin/freeradius -f\" nocase and $udm.principal.process." + "parent_process.command_line != \"\" )) and $udm.network.http.user_agent = \"v1.compute.instances." + "setMetadata\" nocase condition: $udm}', 'startTime': '2022-04-01T11:00:00.000Z', " + "'endTime': '2022-04-07T11:00:00.003Z'}"] + + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_combined_observation_OR(self): + stix_pattern = "[file:hashes.'SHA-1' = '6cbce4a295c163791b60fc23d285e6d84f28ee4c' OR process:name = " \ + "'powershell.exe'] OR [ipv4-addr:value = '10.0.1.4' OR network-traffic:src_port = " \ + "'52221']START t'2022-05-10T11:00:00.000Z' STOP t'2022-05-15T11:00:00.003Z'" + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659679530 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ($udm.src.process.file.full_path = " + "/(?s)powershell\\\\.exe/ nocase or $udm.target.process.file.full_path = /(?s)powershell\\\\.exe/ " + "nocase or $udm.principal.process.file.full_path = /(?s)powershell\\\\.exe/ nocase or " + "$udm.target.process.parent_process.file.full_path = /(?s)powershell\\\\.exe/ nocase or " + "$udm.principal.process.parent_process.file.full_path = /(?s)powershell\\\\.exe/ nocase) or (" + "$udm.src.file.sha1 = \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.file.sha1 " + "= \"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.src.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.principal.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.about.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase) condition: $udm}', 'startTime': " + "'2022-08-05T06:00:30.958Z', 'endTime': '2022-08-05T06:05:30.958Z'}", "{'ruleText': 'rule " + "cp4s_gcp_udi_rule_1659679530 { meta: author = \"ibm cp4s user\" description = \"Create event " + "rule that should generate detections\" events: ($udm.src.port = 52221 or $udm.principal.port = " + "52221) or (any $udm.src.ip = \"10.0.1.4\" nocase or any $udm.target.ip = \"10.0.1.4\" nocase or " + "any $udm.principal.ip = \"10.0.1.4\" nocase) condition: $udm}', 'startTime': " + "'2022-05-10T11:00:00.000Z', 'endTime': '2022-05-15T11:00:00.003Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) + + def test_integer_field_with_invalid_operator(self): + stix_pattern = "[network-traffic:dst_port LIKE '53996']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert 'LIKE operator is not supported for int type input' in result['error'] + + def test_invalid_value_for_matches_operator(self): + stix_pattern = "[network-traffic:src_port MATCHES '52221']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert 'MATCHES operator is not supported for int type input' in result['error'] + + def test_integer_field_with_invalid_string_input(self): + stix_pattern = "[network-traffic:dst_port = 'TCP']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert 'string type input - TCP is not supported for integer type fields' in result['error'] + + def test_unsupported_enum_value_with_equal(self): + stix_pattern = "[network-traffic:protocols[*] = 'tc']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert "Unsupported ENUM values provided. Possible supported enum values are ['UNKNOWN_IP_PROTOCOL', 'EIGRP', "\ + "'ESP', 'ETHERIP', 'GRE', 'ICMP', 'IGMP', 'IP6IN4', 'PIM', 'TCP', 'UDP', 'VRRP', " \ + "'UNKNOWN_APPLICATION_PROTOCOL', 'AFP', 'APPC', 'AMQP', 'ATOM', 'BEEP', 'BITCOIN', 'BIT_TORRENT', " \ + "'CFDP', 'COAP', 'DDS', 'DEVICE_NET', 'DHCP', 'DNS', 'E_DONKEY', 'ENRP', 'FAST_TRACK', 'FINGER', " \ + "'FREENET', 'FTAM', 'GOPHER', 'HL7', 'H323', 'HTTP', 'HTTPS', 'IRCP', 'KADEMLIA', 'LDAP', 'LPD', " \ + "'MIME', 'MODBUS', 'MQTT', 'NETCONF', 'NFS', 'NIS', 'NNTP', 'NTCIP', 'NTP', 'OSCAR', 'PNRP', 'QUIC', " \ + "'RDP', 'RELP', 'RIP', 'RLOGIN', 'RPC', 'RTMP', 'RTP', 'RTPS', 'RTSP', 'SAP', 'SDP', 'SIP', 'SLP', " \ + "'SMB', 'SMTP', 'SNTP', 'SSH', 'SSMS', 'STYX', 'TCAP', 'TDS', 'TOR', 'TSP', 'VTP', 'WHOIS', 'WEB_DAV', "\ + "'X400', 'X500', 'XMPP']" in result['error'] + + def test_unsupported_enum_value_with_in(self): + stix_pattern = "[network-traffic:protocols[*] IN ('tc','ud')]" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert "Unsupported ENUM values provided. Possible supported enum values are ['UNKNOWN_IP_PROTOCOL', 'EIGRP', "\ + "'ESP', 'ETHERIP', 'GRE', 'ICMP', 'IGMP', 'IP6IN4', 'PIM', 'TCP', 'UDP', 'VRRP', " \ + "'UNKNOWN_APPLICATION_PROTOCOL', 'AFP', 'APPC', 'AMQP', 'ATOM', 'BEEP', 'BITCOIN', 'BIT_TORRENT', " \ + "'CFDP', 'COAP', 'DDS', 'DEVICE_NET', 'DHCP', 'DNS', 'E_DONKEY', 'ENRP', 'FAST_TRACK', 'FINGER', " \ + "'FREENET', 'FTAM', 'GOPHER', 'HL7', 'H323', 'HTTP', 'HTTPS', 'IRCP', 'KADEMLIA', 'LDAP', 'LPD', " \ + "'MIME', 'MODBUS', 'MQTT', 'NETCONF', 'NFS', 'NIS', 'NNTP', 'NTCIP', 'NTP', 'OSCAR', 'PNRP', 'QUIC', " \ + "'RDP', 'RELP', 'RIP', 'RLOGIN', 'RPC', 'RTMP', 'RTP', 'RTPS', 'RTSP', 'SAP', 'SDP', 'SIP', 'SLP', " \ + "'SMB', 'SMTP', 'SNTP', 'SSH', 'SSMS', 'STYX', 'TCAP', 'TDS', 'TOR', 'TSP', 'VTP', 'WHOIS', 'WEB_DAV', "\ + "'X400', 'X500', 'XMPP']" in result['error'] + + def test_unsupported_enum_operator(self): + stix_pattern = "[network-traffic:protocols[*] > 'TCP']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert '> operator is not supported for Enum type input. Possible supported operator are ' \ + '[ =, !=, IN, NOT IN ]' in result['error'] + + def test_invalid_stix_pattern(self): + stix_pattern = "[not_a_valid_pattern]" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern, {'validate_pattern': 'true'}) + assert result['success'] is False + assert ErrorCode.TRANSLATION_STIX_VALIDATION.value == result['code'] + assert stix_pattern[1:-1] in result['error'] + + def test_invalid_mac_address(self): + stix_pattern = "[mac-addr:value = '00:00:00']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert 'Invalid mac address' in result['error'] + + def test_invalid_operator_for_string_input(self): + stix_pattern = "[process:name < 'powershell.exe']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert 'operator is not supported for string type input' in result['error'] + + def test_invalid_qualifier(self): + stix_pattern = "[file:size >10]START t'2021-11-28T00:00:00.000000Z' STOP " \ + "t'2021-10-27T00:00:00.000000Z' " + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert "invalid_parameter" == result['code'] + assert 'Start time should be lesser than Stop time' in result['error'] + + def test_invalid_email_address(self): + stix_pattern = "[email-addr:value = 'Administrator_gmail.com']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert 'Invalid email address' in result['error'] + + def test_future_value_for_timestamp_field(self): + stix_pattern = "[file:size >10]START t'2022-04-28T00:00:00.000000Z' STOP " \ + "t'2022-10-27T00:00:00.000000Z' " + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert "invalid_parameter" == result['code'] + assert ' Start/Stop time should not be in the future UTC timestamp' in result['error'] + + def test_invalid_value_for_timestamp_field(self): + stix_pattern = "[file:modified >= '2022-04-0111:00:00.000Z']" + result = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + assert result['success'] is False + assert ErrorCode.TRANSLATION_NOTIMPLEMENTED_MODE.value == result['code'] + assert 'cannot convert the timestamp' in result['error'] + + def test_query_for_multiple_observation(self): + stix_pattern = "[file:hashes.'SHA-1' = '6cbce4a295c163791b60fc23d285e6d84f28ee4c'] OR [file:size >10 OR " \ + "network-traffic:src_port <= 52221] AND [process:command_line ='\"MsMpEng.exe\"']START " \ + "t'2022-05-01T00:00:00.030Z' STOP t'2022-05-05T00:00:00.030Z' OR [ipv4-addr:value = " \ + "'168.149.184.42']START t'2022-05-01T00:00:00Z' STOP t'2022-05-10T00:00:00.030Z' " + query = translation.translate('gcp_chronicle', 'query', '{}', stix_pattern) + query['queries'] = _remove_timestamp_from_query(query['queries']) + queries = ["{'ruleText': 'rule cp4s_gcp_udi_rule_1659680259 { meta: author = \"ibm cp4s user\" description = " + "\"Create event rule that should generate detections\" events: ($udm.src.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.src.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.target.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.principal.process.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase or $udm.about.file.sha1 = " + "\"6cbce4a295c163791b60fc23d285e6d84f28ee4c\" nocase) or ((( $udm.src.port <= 52221 and " + "$udm.src.port != 0 ) or ( $udm.principal.port <= 52221 and $udm.principal.port != 0 )) or (" + "$udm.src.file.size > 10 or $udm.target.file.size > 10 or $udm.src.process.file.size > 10 or " + "$udm.target.process.file.size > 10 or $udm.principal.process.file.size > 10 or " + "$udm.about.file.size > 10)) condition: $udm}', 'startTime': '2022-08-05T06:12:39.734Z', " + "'endTime': '2022-08-05T06:17:39.734Z'}", "{'ruleText': 'rule cp4s_gcp_udi_rule_1659680259 { meta: " + "author = \"ibm cp4s user\" description = \"Create event rule that should generate detections\" " + "events: $udm.src.process.command_line = \"\\\\\"MsMpEng.exe\\\\\"\" nocase or $udm.target.process." + "command_line = \"\\\\\"MsMpEng.exe\\\\\"\" nocase or $udm.principal.process.command_line = " + "\"\\\\\"MsMpEng.exe\\\\\"\" nocase or $udm.target.process.parent_process.command_line = " + "\"\\\\\"MsMpEng.exe\\\\\"\" nocase or $udm.principal.process.parent_process.command_line = " + "\"\\\\\"MsMpEng.exe\\\\\"\" nocase condition: $udm}', 'startTime': '2022-05-01T00:00:00.030Z', " + "'endTime': '2022-05-05T00:00:00.030Z'}", "{'ruleText': 'rule cp4s_gcp_udi_rule_1659680259 " + "{ meta: author = \"ibm cp4s user\" description = \"Create event rule that should generate " + "detections\" events: any $udm.src.ip = \"168.149.184.42\" nocase or any $udm.target.ip = " + "\"168.149.184.42\" nocase or any $udm.principal.ip = \"168.149.184.42\" nocase condition: $udm}', " + "'startTime': '2022-05-01T00:00:00Z', 'endTime': '2022-05-10T00:00:00.030Z'}"] + queries = _remove_timestamp_from_query(queries) + self._test_query_assertions(query, queries) diff --git a/stix_shifter_modules/gcp_chronicle/tests/stix_transmission/test_gcp_chronicle.py b/stix_shifter_modules/gcp_chronicle/tests/stix_transmission/test_gcp_chronicle.py new file mode 100644 index 000000000..54017dbbd --- /dev/null +++ b/stix_shifter_modules/gcp_chronicle/tests/stix_transmission/test_gcp_chronicle.py @@ -0,0 +1,1264 @@ +from stix_shifter_modules.gcp_chronicle.entry_point import EntryPoint +import unittest +from unittest.mock import patch +from stix_shifter.stix_transmission import stix_transmission +import json +from google.auth.exceptions import RefreshError +from httplib2 import ServerNotFoundError + + +class MockCodeResponse: + def __init__(self, code): + self.status = code + + +@patch('googleapiclient._auth.authorized_http') +@patch('google.oauth2.service_account.Credentials.from_service_account_info') +class TestGCPChronicleConnection(unittest.TestCase, object): + + @staticmethod + def connection(): + return { + "host": "hostbla", + "selfSignedCert": "hostbla" + } + + @staticmethod + def configuration(): + return { + "auth": { + "client_email": "hostbla" + } + } + + @patch('stix_shifter_modules.gcp_chronicle.stix_transmission.api_client.APIClient.__init__') + def test_is_async(self, mock_api, *args): + mock_api.return_value = None + entry_point = EntryPoint(self.connection(), self.configuration()) + check_async = entry_point.is_async() + assert check_async + + def test_ping(self, mock_credentials, mock_auth): + """test ping connection""" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mock_http.request.return_value = (mock_code_response, json.dumps({"rules": [{"ruleId": "ru_123abcd"}]})) + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + ping_response = transmission.ping() + assert ping_response is not None + assert ping_response['success'] is True + + def test_transmit_query(self, mock_credentials, mock_auth): + """test query connection""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + + mock_code_response = MockCodeResponse(200) + mock_query_output_response = json.dumps({"retrohuntId": "oh_1234", "ruleId": "ru_1234", "versionId": "ru_1234", + "eventStartTime": "2022-06-28T00:00:00.030Z", + "eventEndTime": "2022-06-29T00:00:00.030Z", + "retrohuntStartTime": "2022-07-07T06:43:40.983372Z", + "state": "RUNNING"}) + mocked_create_rule = mock_code_response, json.dumps({"ruleId": "ru_1234"}) + + mocked_create_search = (mock_code_response, mock_query_output_response) + + mock_http.request.side_effect = [mocked_create_rule, mocked_create_search] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is True + assert query_response['search_id'] == "oh_1234:ru_1234" + + def test_status_response(self, mock_credentials, mock_auth): + """test status response """ + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mock_status_output = json.dumps({"retrohuntId": "oh_1234", "ruleId": "ru_1234", "versionId": "ru_1234", + "eventStartTime": "2022-06-28T00:00:00.030Z", + "eventEndTime": "2022-06-29T00:00:00.030Z", + "retrohuntStartTime": "2022-07-07T06:43:40.983372Z", + "retrohuntEndTime": "2022-07-07T06:43:58.750611Z", "state": "DONE", + "progressPercentage": 100}) + mocked_search_status = (mock_code_response, mock_status_output) + mock_http.request.return_value = mocked_search_status + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['success'] is True + assert status_response['progress'] == 100 + assert status_response['status'] == "COMPLETED" + + def test_result_response(self, mock_credentials, mock_auth): + """test result response connection""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + output = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657020065", + "urlBackToProduct": "url", + "ruleId": "ru_1234", + "ruleVersion": "ru_1234", + "alertState": "NOT_ALERTING", + "ruleType": "SINGLE_EVENT", + "ruleLabels": [{ + "key": "author", + "value": "ibm cp4s user" + }, { + "key": "description", + "value": "Create event rule that should generate detections" + }] + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + "metadata": { + "productLogId": "823rb4e123k4" + }, + "securityResult": [ + { + "severity": "LOW" + } + ], + "network": { + "email": { + "from": "010001818b271091-e32fa873-1a72-4d6f-8eab-29c09637402f-000000" + "@amazonses.com", + "mailId": "010001818b271091-e32fa873-1a72-4d6f-8eab-29c09637402f-000000@email" + ".amazonses.com", + "subject": [ + "https://testurl.com test" + ], + "to": [ + "ravithummala@iscgalaxy.com" + ] + }, + "dns": { + "authoritative": True, + "questions": [{ + "name": "www.a2k2.in", + "type": 1 + }], + "responseCode": 3 + }, + "ipProtocol": "UDP" + }, + "target": { + "hostname": "v20.events.data.microsoft.com", + "ip": [ + "13.89.178.26" + ], + "url": "v20.events.data.microsoft.com", + "registry": { + "registryKey": "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Advanced " + "Threat Protection", + "registryValueData": "132996043194369129", + "registryValueName": "CrashHeartbeat" + } + } + + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }]}) + mock_code_response = MockCodeResponse(200) + mocked_result_response = (mock_code_response, output) + mocked_delete_response = (MockCodeResponse(200), json.dumps({})) + mock_http.request.side_effect = [mocked_result_response, mocked_delete_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is True + assert result_response["data"][0]["event"]["metadata"]["productLogId"] == "823rb4e123k4" + assert result_response["data"][0]["detection"]["ruleName"] == "rule_1657020065" + assert result_response["data"][0]["event"]["network"]["email"]["isMultipart"] is False + assert result_response["data"][0]["event"]["network"]["ipProtocol"] == "UDP" + assert result_response["data"][0]["event"]["securityResult"][0]["severity"] == 48 + + def test_delete_response(self, mock_credentials, mock_auth): + """test delete response connection""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.return_value = (MockCodeResponse(200), json.dumps({})) + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + delete_response = transmission.delete(search_id) + assert delete_response is not None + assert delete_response['success'] is True + + def test_result_response_with_next_page_token(self, mock_credentials, mock_auth): + """test result response connection having next page token""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + output_1 = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657020065" + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + "metadata": { + "productLogId": "823rb4e123k4" + } + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }], + "nextPageToken": "12345" + } + ) + output_2 = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657032243" + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + "metadata": { + "productLogId": "2js34kn45b3n7" + } + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }] + } + ) + + mock_code_response = MockCodeResponse(200) + mocked_result_response_1 = (mock_code_response, output_1) + mocked_result_response_2 = (mock_code_response, output_2) + mocked_delete_response = (MockCodeResponse(200), json.dumps({})) + mock_http.request.side_effect = [mocked_result_response_1, mocked_result_response_2, mocked_delete_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is True + assert result_response["data"][0]["event"]["metadata"]["productLogId"] == "823rb4e123k4" + assert result_response["data"][0]["detection"]["ruleName"] == "rule_1657020065" + assert result_response["data"][1]["event"]["metadata"]["productLogId"] == "2js34kn45b3n7" + assert result_response["data"][1]["detection"]["ruleName"] == "rule_1657032243" + + def test_invalid_client_email_for_ping(self, mock_credentials, mock_auth): + """ test invalid client email for ping""" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = RefreshError("Invalid Client Email") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + ping_response = transmission.ping() + assert ping_response is not None + assert ping_response['success'] is False + assert "Invalid Client Email" in ping_response['error'] + assert ping_response['code'] == "authentication_fail" + + def test_invalid_host_for_ping(self, mock_credentials, mock_auth): + """ test invalid host for ping""" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ServerNotFoundError("Invalid Host") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + ping_response = transmission.ping() + assert ping_response is not None + assert ping_response['success'] is False + assert "Invalid Host" in ping_response['error'] + assert ping_response['code'] == "service_unavailable" + + def test_invalid_client_email_for_transmit_query(self, mock_credentials, mock_auth): + """ test invalid client email for transmit query""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = RefreshError("Invalid Client Email") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Invalid Client Email" in query_response['error'] + assert query_response['code'] == "authentication_fail" + + def test_invalid_host_for_transmit_query(self, mock_credentials, mock_auth): + """ test invalid host for transmit query""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ServerNotFoundError("Invalid Host") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Invalid Host" in query_response['error'] + assert query_response['code'] == "service_unavailable" + + def test_query_raise_create_rule_query_error(self, mock_credentials, mock_auth): + """raise 400 invalid argument error by applying any to field type other than list""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: (any $udm.network.http.user_agent " + "= /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.return_value = (MockCodeResponse(400), json.dumps({ + "error": {"code": 400, + "status": "INVALID_ARGUMENT", + "message": "generic::invalid_argument: " + "compiling rule: validating repeated fields: applying any/all to a non-repeated field" + }})) + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "invalid_argument" in query_response['error'] + assert query_response['code'] == "invalid_parameter" + + def test_value_error_for_transmit_query(self, mock_credentials, mock_auth): + """test query value error connection""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mocked_create_rule = (mock_code_response, json.dumps({"ruleId": "ru_1234"})) + mock_http.request.side_effect = [mocked_create_rule, ValueError("Invalid json")] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Invalid json" in query_response["error"] + assert query_response['code'] == "unknown" + + def test_transmit_query_with_key_error(self, mock_credentials, mock_auth): + """test key error for input query having invalid key """ + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "start_Time": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mocked_create_rule = (mock_code_response, json.dumps({"ruleId": "ru_1234"})) + + mock_http.request.return_value = mocked_create_rule + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "startTime" in query_response["error"] + assert query_response['code'] == "unknown" + + def test_query_with_base_exception(self, mock_credentials, mock_auth): + """test transmit query with base exception""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mocked_create_rule = (mock_code_response, json.dumps({"ruleId": "ru_1234"})) + mock_http.request.side_effect = [mocked_create_rule, Exception("Unknown Error")] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Unknown Error" in query_response["error"] + assert query_response['code'] == "unknown" + + def test_400_error_with_invalid_timestamp_format(self, mock_credentials, mock_auth): + """test 400 code with invalid timestamp error from run retrohunt api """ + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29TT00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_create_rule_response = (MockCodeResponse(200), json.dumps({"ruleId": "ru_1234"})) + mock_create_search_error_response = (MockCodeResponse(400), json.dumps({ + "error": {"code": 400, + "status": "INVALID_ARGUMENT", + "message": "Invalid time format" + }})) + mock_http.request.side_effect = [mock_create_rule_response, mock_create_search_error_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Invalid time format" in query_response['error'] + assert query_response['code'] == "invalid_parameter" + + def test_create_search_with_return_200_and_invalid_response(self, mock_credentials, mock_auth): + """ test Run retrohunt with 200 response code and invalid json response""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_create_rule_response = (MockCodeResponse(200), json.dumps({"ruleId": "ru_1234"})) + mock_create_search_error_response = (MockCodeResponse(200), json.dumps({"Invalid": "Response"})) + mock_http.request.side_effect = [mock_create_rule_response, mock_create_search_error_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "InvalidResponse" in query_response['error'] + assert query_response['code'] == "invalid_parameter" + + def test_invalid_query_for_retrohunt_api(self, mock_credentials, mock_auth): + """ test invalid host for run retorhunt api""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_create_rule_response = (MockCodeResponse(200), json.dumps({"ruleId": "ru_1234"})) + mock_http.request.side_effect = [mock_create_rule_response, ServerNotFoundError("Invalid Host")] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Invalid Host" in query_response['error'] + assert query_response['code'] == "service_unavailable" + + def test_invalid_client_email_for_retrohunt(self, mock_credentials, mock_auth): + """ test invalid client email for run retrohunt api call""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_create_rule_response = (MockCodeResponse(200), json.dumps({"ruleId": "ru_1234"})) + mock_http.request.side_effect = [mock_create_rule_response, RefreshError("Invalid Client Email")] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Invalid Client Email" in query_response['error'] + assert query_response['code'] == "authentication_fail" + + def test_invalid_response_for_400_error_code(self, mock_credentials, mock_auth): + """test value error exception in create search through json loads providing + invalid json in retrohunt response""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mocked_create_rule = (mock_code_response, json.dumps({"ruleId": "ru_1234"})) + mocked_retrohunt_value_error = (MockCodeResponse(400), "Invalid_Response") + mock_http.request.side_effect = [mocked_create_rule, mocked_retrohunt_value_error] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "cannot parse Expecting value" in query_response["error"] + assert query_response['code'] == "unknown" + + def test_timeout_error_in_ping(self, mock_credentials, mock_auth): + """test connection/timeout error in transmit ping""" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = TimeoutError("connection attempt failed") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + ping_response = transmission.ping() + assert ping_response is not None + assert ping_response['success'] is False + assert "connection attempt failed" in ping_response['error'] + assert ping_response['code'] == "unknown" + + def test_ping_value_error(self, mock_credentials, mock_auth): + """test value error in transmit ping""" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.return_value = (MockCodeResponse(400), "Invalid_Response") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + ping_response = transmission.ping() + assert ping_response is not None + assert ping_response['success'] is False + assert "cannot parse" in ping_response['error'] + assert ping_response['code'] == "unknown" + + def test_ping_raise_429_error(self, mock_credentials, mock_auth): + """test resource exhausted error in ping""" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.return_value = (MockCodeResponse(429), + json.dumps({"error": {"code": 429, "message": "RESOURCE_EXHAUSTED"}})) + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + ping_response = transmission.ping() + assert ping_response is not None + assert ping_response['success'] is False + assert "RESOURCE_EXHAUSTED" in ping_response['error'] + assert ping_response['code'] == "service_unavailable" + + def test_delete_query_404_error(self, mock_credentials, mock_auth): + """test 404 error with invalid rule id in transmit delete""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.return_value = (MockCodeResponse(404), + json.dumps({"error": { + "code": 404, + "message": "rule with ID ru_1234 could not be found", + "status": "NOT_FOUND"}})) + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + delete_response = transmission.delete(search_id) + assert delete_response is not None + assert delete_response['success'] is False + assert "rule with ID ru_1234 could not be found" in delete_response['error'] + assert delete_response['code'] == "invalid_parameter" + + def test_delete_query_with_invalid_client_email(self, mock_credentials, mock_auth): + """ test invalid email in transmit delete""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = RefreshError("Invalid Client Email") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + delete_response = transmission.delete(search_id) + assert delete_response is not None + assert delete_response['success'] is False + assert "Invalid Client Email" in delete_response['error'] + assert delete_response['code'] == "authentication_fail" + + def test_delete_with_server_not_found_error(self, mock_credentials, mock_auth): + """ test invalid host in transmit delete""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ServerNotFoundError("Invalid Host") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + delete_response = transmission.delete(search_id) + assert delete_response is not None + assert delete_response['success'] is False + assert "Invalid Host" in delete_response['error'] + assert delete_response['code'] == "service_unavailable" + + def test_delete_with_timeout_exception(self, mock_credentials, mock_auth): + """ test timeout error in transmit delete """ + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = TimeoutError("connection attempt failed") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + delete_response = transmission.delete(search_id) + assert delete_response is not None + assert delete_response['success'] is False + assert "connection attempt failed" in delete_response['error'] + assert delete_response['code'] == "unknown" + + def test_results_with_invalid_client_email(self, mock_credentials, mock_auth): + """ test invalid client email in transmit results""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = RefreshError("Invalid Client Email") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is False + assert "Invalid Client Email" in result_response['error'] + assert result_response['code'] == "authentication_fail" + + def test_results_with_invalid_host(self, mock_credentials, mock_auth): + """ test invalid host in transmit results""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ServerNotFoundError("Invalid Host") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is False + assert "Invalid Host" in result_response['error'] + assert result_response['code'] == "service_unavailable" + + def test_results_with_404_error(self, mock_credentials, mock_auth): + """test 404 error with invalid rule id in transmit results""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.return_value = (MockCodeResponse(404), + json.dumps({"error": { + "code": 404, + "message": "rule with ID ru_1234 could not be found", + "status": "NOT_FOUND"}})) + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is False + assert "rule with ID ru_1234 could not be found" in result_response['error'] + assert result_response['code'] == "invalid_parameter" + + def test_404_in_next_page_result(self, mock_credentials, mock_auth): + """test 404 error in result response during pagination in transmit results""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + output_1 = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657020065" + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + "metadata": { + "productLogId": "823rb4e123k4" + } + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }], + "nextPageToken": "12345" + } + ) + mock_code_response = MockCodeResponse(200) + mocked_result_response_1 = (mock_code_response, output_1) + mocked_delete_response = (MockCodeResponse(200), json.dumps({})) + mocked_result_with_invalid_rule_id = (MockCodeResponse(404), json.dumps({"error": { + "code": 404, + "message": "rule with ID ru_1234 could not be found", + "status": "NOT_FOUND"}})) + mock_http.request.side_effect = [mocked_result_response_1, mocked_result_with_invalid_rule_id, + mocked_delete_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is False + assert "rule with ID ru_1234 could not be found" in result_response['error'] + + def test_status_response_with_state_running(self, mock_credentials, mock_auth): + """test running status for transmit status""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mock_status_output = json.dumps({"retrohuntId": "oh_1234", "ruleId": "ru_1234", "versionId": "ru_1234", + "eventStartTime": "2022-06-28T00:00:00.030Z", + "eventEndTime": "2022-06-29T00:00:00.030Z", + "retrohuntStartTime": "2022-07-07T06:43:40.983372Z", + "retrohuntEndTime": "2022-07-07T06:43:58.750611Z", "state": "RUNNING", + "progressPercentage": 42.86}) + + mocked_search_status = (mock_code_response, mock_status_output) + mock_http.request.return_value = mocked_search_status + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['success'] is True + assert status_response['progress'] == 42.86 + assert status_response['status'] == "RUNNING" + + def test_status_response_with_state_cancelled(self, mock_credentials, mock_auth): + """test status response with cancelled state""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mock_status_output = json.dumps({"retrohuntId": "oh_1234", "ruleId": "ru_1234", "versionId": "ru_1234", + "eventStartTime": "2022-06-28T00:00:00.030Z", + "eventEndTime": "2022-06-29T00:00:00.030Z", + "retrohuntStartTime": "2022-07-07T06:43:40.983372Z", + "retrohuntEndTime": "2022-07-07T06:43:58.750611Z", "state": "CANCELLED", + "progressPercentage": 77.45}) + mocked_search_status = (mock_code_response, mock_status_output) + mock_http.request.return_value = mocked_search_status + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['success'] is True + assert status_response['progress'] == 77.45 + assert status_response['status'] == "CANCELED" + + def test_status_with_invalid_client_email(self, mock_credentials, mock_auth): + """ test invalid client email for transmit status""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = RefreshError("Invalid Client Email") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['success'] is False + assert "Invalid Client Email" in status_response['error'] + assert status_response['code'] == "authentication_fail" + + def test_status_with_invalid_host(self, mock_credentials, mock_auth): + """ test invalid host for transmit status""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ServerNotFoundError("Invalid Host") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['success'] is False + assert "Invalid Host" in status_response['error'] + assert status_response['code'] == "service_unavailable" + + def test_status_response_with_no_state_response(self, mock_credentials, mock_auth): + """test error when status response having response code 200 without state as key""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_code_response = MockCodeResponse(200) + mock_status_output = json.dumps({"retrohuntId": "oh_1234", "ruleId": "ru_1234", "versionId": "ru_1234", + "eventStartTime": "2022-06-28T00:00:00.030Z", + "eventEndTime": "2022-06-29T00:00:00.030Z", + "retrohuntStartTime": "2022-07-07T06:43:40.983372Z", + "retrohuntEndTime": "2022-07-07T06:43:58.750611Z" + }) + mocked_search_status = (mock_code_response, mock_status_output) + mock_http.request.return_value = mocked_search_status + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['success'] is False + assert "Invalid Response" in status_response['error'] + assert status_response['code'] == "invalid_parameter" + + def test_status_response_429_resource_exhausted(self, mock_credentials, mock_auth): + """status with 429 resource exhausted""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + resource_exhausted_response = json.dumps({'error': {'code': 429, + 'message': 'generic::resource_exhausted: ' + 'insufficient quota for method ' + 'quota is 60 queries per 60 seconds', + 'status': 'RESOURCE_EXHAUSTED'}}) + mocked_resource_exhausted_response = (MockCodeResponse(429), resource_exhausted_response) + mock_http.request.return_value = mocked_resource_exhausted_response + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['status'] == "RUNNING" + + def test_security_result_response(self, mock_credentials, mock_auth): + """test security result response """ + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + output = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657020065", + "urlBackToProduct": "url", + "ruleId": "ru_1234", + "ruleVersion": "ru_1234", + "alertState": "NOT_ALERTING", + "ruleType": "SINGLE_EVENT", + "ruleLabels": [{ + "key": "author", + "value": "ibm cp4s user" + }, { + "key": "description", + "value": "Create event rule that should generate detections" + }] + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + "metadata": { + "productLogId": "823rb4e123k4", + "eventType": "GENERIC_EVENT" + }, + "principal": { + "asset": { + "platformSoftware": { + "platformVersion": "windows10" + }, + "software": { + "version": "5.6" + } + } + }, + "securityResult": [ + { + "severity": "LOW", + "category": ["SOFTWARE_SUSPICIOUS"] + } + ], + "network": { + "http": { + "userAgent": "hhtpuseragent" + } + }, + "target": { + "hostname": "v20.events.data.microsoft.com", + "ip": [ + "13.89.178.26" + ], + "url": "v20.events.data.microsoft.com" + } + + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }]}) + mock_code_response = MockCodeResponse(200) + mocked_result_response = (mock_code_response, output) + mocked_delete_response = (MockCodeResponse(200), json.dumps({})) + mock_http.request.side_effect = [mocked_result_response, mocked_delete_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is True + assert result_response["data"][0]["event"]["metadata"]["productLogId"] == "823rb4e123k4" + assert result_response["data"][0]["event"]["securityResult"][0]["category"] == "alert" + assert result_response["data"][0]["event"]["securityResult"][0]["severity"] == 48 + assert result_response["data"][0]["event"]["metadata"]["eventType"] == "GENERIC_EVENT" + + def test_security_and_registry_response(self, mock_credentials, mock_auth): + """test security result and registry response""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + output = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657020065", + "urlBackToProduct": "url", + "ruleId": "ru_1234", + "ruleVersion": "ru_1234", + "alertState": "NOT_ALERTING", + "ruleType": "SINGLE_EVENT", + "ruleLabels": [{ + "key": "author", + "value": "ibm cp4s user" + }, { + "key": "description", + "value": "Create event rule that should generate detections" + }] + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + 'metadata': { + 'eventTimestamp': '2022-06-24T11:29:28.336Z', + 'eventType': 'REGISTRY_MODIFICATION' + }, + "principal": { + 'hostname': 'alert-windows', + 'assetId': 'DeviceId:4f22ab5dc4be96566ee3c9adb3b77280dc08bfdb', + 'user': { + 'windowsSid': 'S-1-5-18' + } + }, + "securityResult": [ + { + "summary": "registry modified" + } + ], + "target": { + 'registry': { + 'registryValueName': 'CrashHeartbeat', + 'registryValueData': '132996043194369129' + } + } + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }]}) + mock_code_response = MockCodeResponse(200) + mocked_result_response = (mock_code_response, output) + mocked_delete_response = (MockCodeResponse(200), json.dumps({})) + mock_http.request.side_effect = [mocked_result_response, mocked_delete_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is True + assert result_response["data"][0]["event"]["securityResult"][0]['summary'] == "registry modified" + assert "registry" not in result_response["data"][0]["event"]["target"] + assert result_response["data"][0]["event"]["principal"]["user"]["userid"] == "UNAVAILABLE" + + def test_generic_event_with_protocol_response(self, mock_credentials, mock_auth): + """test generic event response """ + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + output = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657020065", + "urlBackToProduct": "url", + "ruleId": "ru_1234", + "ruleVersion": "ru_1234", + "alertState": "NOT_ALERTING", + "ruleType": "SINGLE_EVENT", + "ruleLabels": [{ + "key": "author", + "value": "ibm cp4s user" + }, { + "key": "description", + "value": "Create event rule that should generate detections" + }] + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + "metadata": { + "productLogId": "823rb4e123k4", + "eventType": "GENERIC_EVENT" + }, + "principal": { + "asset": { + "platformSoftware": { + "platformVersion": "windows10" + }, + "software": { + "version": "5.6" + } + } + }, + "securityResult": [ + { + "severity": "LOW", + "category": ["SOFTWARE_SUSPICIOUS"] + } + ], + "network": { + "http": { + "userAgent": "httpuseragent" + }, + 'applicationProtocol': 'HTTP' + }, + "target": { + "hostname": "v20.events.data.microsoft.com", + "url": "v20.events.data.microsoft.com" + } + + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }]}) + mock_code_response = MockCodeResponse(200) + mocked_result_response = (mock_code_response, output) + mocked_delete_response = (MockCodeResponse(200), json.dumps({})) + mock_http.request.side_effect = [mocked_result_response, mocked_delete_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is True + assert result_response["data"][0]["event"]["network"] == {} + + def test_invalid_email_addresses_response(self, mock_credentials, mock_auth): + """test email addresses response """ + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + output = json.dumps({ + "detections": + [{ + "type": "RULE_DETECTION", + "detection": [{ + "ruleName": "rule_1657020065", + "urlBackToProduct": "url", + "ruleId": "ru_1234", + "ruleVersion": "ru_1234", + "alertState": "NOT_ALERTING", + "ruleType": "SINGLE_EVENT", + "ruleLabels": [{ + "key": "author", + "value": "ibm cp4s user" + }, { + "key": "description", + "value": "Create event rule that should generate detections" + }] + }], + "createdTime": "2022-07-07T08:01:24.869956Z", + "id": "de_38c2972e-ec99-8c0c-4dbe-3b350294b2bb", + "timeWindow": { + "startTime": "2022-06-28T09:49:09.460001Z", + "endTime": "2022-06-28T09:49:09.460001Z" + }, + "collectionElements": [{ + "references": [{ + "event": { + "metadata": { + "productLogId": "823rb4e123k4" + }, + 'principal': { + 'user': { + 'emailAddresses': ['user:user1iscgalaxy.com'] + }, + 'ip': ['54.240.11.121'] + }, + 'target': { + 'user': { + 'emailAddresses': ['user:user2@iscgalaxy.com', + "targetuser.com"] + } + }, + "securityResult": [ + { + "severity": "LOW" + } + ], + "network": { + "email": { + "from": "010001818b271091-e32fa873-1a72-4d6f-8eab-29c09637402f-000000" + "@amazonses.com", + "mailId": "010001818b271091-e32fa873-1a72-4d6f-8eab-29c09637402f-000000@email" + ".amazonses.com", + "subject": [ + "https://testurl.com test" + ], + "to": [ + "xyziscgalaxy.com" + ], + "cc":[ + "abc@user.com", + "service:test@galaxy.com", + "ccuser.com" + ] + }, + "dns": { + "authoritative": True, + "questions": [{ + "name": "www.a2k2.in", + "type": 1 + }], + "responseCode": 3 + }, + "ipProtocol": "UDP" + } + + } + }], + "label": "udm" + }], + "detectionTime": "2022-06-28T09:49:09.460001Z" + }]}) + mock_code_response = MockCodeResponse(200) + mocked_result_response = (mock_code_response, output) + mocked_delete_response = (MockCodeResponse(200), json.dumps({})) + mock_http.request.side_effect = [mocked_result_response, mocked_delete_response] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 1) + assert result_response is not None + assert result_response['success'] is True + assert result_response["data"][0]["event"]["target"]["user"]["emailAddresses"] == ['user2@iscgalaxy.com'] + assert result_response["data"][0]["event"]["network"]["email"]["cc"] == ['abc@user.com', 'test@galaxy.com'] + assert 'to' not in result_response["data"][0]["event"]["network"]["email"].keys() + assert 'emailAddresses' not in result_response["data"][0]["event"]["principal"]["user"].keys() + + def test_invalid_private_key_for_ping(self, mock_credentials, mock_auth): + """ test invalid private key for ping""" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ValueError("Could not deserialize key data") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + ping_response = transmission.ping() + assert ping_response is not None + assert ping_response['success'] is False + assert "Could not deserialize key data" in ping_response['error'] + assert ping_response['code'] == "authentication_fail" + + def test_invalid_private_key_for_transmit_query(self, mock_credentials, mock_auth): + """ test invalid private key for transmit query""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ValueError("Could not deserialize key data") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Could not deserialize key data" in query_response['error'] + assert query_response['code'] == "authentication_fail" + + def test_results_with_invalid_private_key(self, mock_credentials, mock_auth): + """ test invalid private key in transmit results""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ValueError("Could not deserialize key data") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + result_response = transmission.results(search_id, 0, 2) + assert result_response is not None + assert result_response['success'] is False + assert "Could not deserialize key data" in result_response['error'] + assert result_response['code'] == "authentication_fail" + + def test_invalid_private_key_for_retrohunt(self, mock_credentials, mock_auth): + """ test invalid private key for run retrohunt api call""" + query = json.dumps({ + "ruleText": "rule rule_1657176728 { meta: author = \"ibm cp4s user\" description = \"Create event rule " + "that should generate detections\" events: ($udm.network.http.user_agent = /(?s)glbc\\/v0.0.0 " + "\\(linux\\/amd64\\) kubernetes\\/\\$Format\\/leader-election/ nocase) condition: $udm}", + "startTime": "2022-06-28T00:00:00.030Z", + "endTime": "2022-06-29T00:00:00.030Z" + }) + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_create_rule_response = (MockCodeResponse(200), json.dumps({"ruleId": "ru_1234"})) + mock_http.request.side_effect = [mock_create_rule_response, ValueError("Could not deserialize key data")] + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + query_response = transmission.query(query) + assert query_response is not None + assert query_response['success'] is False + assert "Could not deserialize key data" in query_response['error'] + assert query_response['code'] == "authentication_fail" + + def test_status_with_invalid_private_key(self, mock_credentials, mock_auth): + """ test invalid private key for transmit status""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ValueError("Could not deserialize key data") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + status_response = transmission.status(search_id) + assert status_response is not None + assert status_response['success'] is False + assert "Could not deserialize key data" in status_response['error'] + assert status_response['code'] == "authentication_fail" + + def test_delete_query_with_invalid_private_key(self, mock_credentials, mock_auth): + """ test invalid private key in transmit delete""" + search_id = "oh_1234:ru_1234" + mock_credentials.return_value = None + mock_http = mock_auth.return_value + mock_http.request.side_effect = ValueError("Could not deserialize key data") + transmission = stix_transmission.StixTransmission('gcp_chronicle', self.connection(), self.configuration()) + delete_response = transmission.delete(search_id) + assert delete_response is not None + assert delete_response['success'] is False + assert "Could not deserialize key data" in delete_response['error'] + assert delete_response['code'] == "authentication_fail"