\" for relay mail. It is not the Exchange Online NetworkMessageId.\nFilewallExchange_CL\n| extend IngestionTime = ingestion_time()\n| extend\n direction = column_ifexists('direction', ''),\n source_folder = column_ifexists('source_folder', ''),\n internet_message_id = column_ifexists('internet_message_id', ''),\n report_url = column_ifexists('report_url', '')\n| mv-apply Attachment = attachments on (\n extend AttachmentStatus = tolower(tostring(Attachment.status)), AttachmentReason = tolower(tostring(Attachment.block_reason))\n | extend Verdict = case(\n AttachmentReason == \"antivirus\", \"Malicious\",\n AttachmentReason in (\"static_analysis\", \"type_validation\"), \"Suspicious\",\n AttachmentReason == \"cdr_init_failed\" or AttachmentStatus == \"failed\", \"ProcessingFailed\",\n AttachmentStatus in (\"not_supported\", \"not_sanitized\"), \"NotSanitized\",\n AttachmentReason == \"cdr\", \"Unsanitizable\",\n AttachmentReason in (\"file_policy\", \"size_limitation\", \"encryption_filter\"), \"PolicyBlocked\",\n AttachmentStatus == \"blocked\", \"Blocked\",\n AttachmentStatus in (\"succeed\", \"sanitized\"), \"Sanitized\",\n \"Unknown\")\n | extend VerdictRank = case(Verdict == \"Malicious\", 8, Verdict == \"Suspicious\", 7, Verdict == \"ProcessingFailed\", 6, Verdict == \"NotSanitized\", 5, Verdict == \"Unsanitizable\", 4, Verdict == \"Blocked\", 3, Verdict == \"PolicyBlocked\", 2, Verdict == \"Sanitized\", 1, 0)\n | extend VerdictSeverity = case(VerdictRank == 8, \"High\", VerdictRank == 7, \"Medium\", VerdictRank >= 3, \"Low\", \"Informational\")\n | summarize arg_max(VerdictRank, Verdict, VerdictSeverity), EmailAttachments = make_list(bag_merge(Attachment, bag_pack(\"verdict\", Verdict, \"severity\", VerdictSeverity)))\n )\n| extend\n EventStartTime = TimeGenerated,\n EventEndTime = TimeGenerated,\n EventOriginalType = tostring(event_type),\n ThreatVerdict = coalesce(Verdict, \"Unknown\"),\n EventSeverity = coalesce(VerdictSeverity, \"Informational\")\n| extend\n EventType = iff(ThreatVerdict in (\"Malicious\", \"Suspicious\"), \"ThreatInfo\", \"Informational\"),\n EventResult = case(status =~ \"blocked\", \"Blocked\", status =~ \"partial_blocked\", \"Partial\", status =~ \"failed\", \"Failure\", status in~ (\"succeed\", \"sanitized\"), \"Success\", \"NA\"),\n EventOriginalResultDetails = coalesce(state, summary),\n EventVendor = \"ODI-X\",\n EventProduct = \"Filewall for Microsoft 365\",\n EventUid = tostring(event_id),\n EmailSubject = subject,\n EmailSenderFrom = sender,\n EmailRecipientTo = recipient,\n EmailDirection = case(direction =~ \"incoming\", \"Inbound\", direction =~ \"outgoing\", \"Outbound\", \"\"),\n EmailInternetMessageId = internet_message_id,\n FilewallMessageId = mail_id,\n EventReportUrl = report_url,\n AlertPolicyId = policy_id,\n AlertPolicyName = policy_name,\n User = recipient,\n ActorUsername = sender,\n Dvc = application,\n DvcHostname = application,\n EventSchema = \"Alert\",\n EventSchemaVersion = \"0.1.0\",\n EventCount = int(1),\n timestamp = todatetime(timestamp),\n release_timestamp = todatetime(release_timestamp),\n release_by_admin = tobool(release_by_admin)\n| project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventUid, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, ThreatVerdict, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, User, ActorUsername, EmailSubject, EmailSenderFrom, EmailRecipientTo, EmailDirection, EmailInternetMessageId, FilewallMessageId, EventReportUrl, AlertPolicyId, AlertPolicyName, application, event_id, timestamp, status, state, summary, attachments, EmailAttachments, direction, source_folder, release_timestamp, release_by_admin, release_reason\n",
"functionParameters": "",
"version": 2,
"tags": [
@@ -1506,7 +1617,8 @@
"value": ""
}
]
- }
+ },
+ "condition": "[parameters('DeployParser')]"
},
{
"type": "Microsoft.OperationalInsights/workspaces/providers/metadata",
@@ -1536,7 +1648,8 @@
"tier": "Partner",
"link": "https://www.odi-x.com/contact-filewall-support/"
}
- }
+ },
+ "condition": "[parameters('DeployParser')]"
},
{
"type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates",
@@ -1547,7 +1660,7 @@
"[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]"
],
"properties": {
- "description": "FilewallM365FileEvent Data Parser with template version 3.0.0",
+ "description": "FilewallM365FileEvent Data Parser with template version 3.2.2",
"mainTemplate": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "[variables('parserObject2').parserVersion2]",
@@ -1564,7 +1677,7 @@
"displayName": "Parser for File events",
"category": "Microsoft Sentinel Parser",
"functionAlias": "FilewallM365FileEvent",
- "query": "FilewallFile_CL\n| extend \n EventStartTime = TimeGenerated, \n EventEndTime = TimeGenerated,\n EventOriginalType = tostring(event_type),\n EventType = case(status =~ \"blocked\", \"FileBlocked\", status =~ \"succeed\", \"FileModified\", status =~ \"failed\", \"FileError\", \"FileEvent\"),\n EventResult = case(status =~ \"blocked\", 'Blocked', status =~ \"failed\", \"Failure\", status =~ \"succeed\", \"Success\", \"NA\"),\n EventOriginalResultDetails = coalesce(block_reason, state, summary),\n EventSeverity = case(status =~ \"blocked\", \"High\", status =~ \"failed\", \"Medium\", \"Informational\"),\n EventVendor = \"ODI-X\",\n EventProduct = \"Filewall for Microsoft 365\",\n TargetFileName = name,\n TargetFilePath = path,\n TargetFilePathType = application,\n TargetFileExtension = file_type,\n TargetFileSize = tolong(size),\n TargetFileHashSha1 = coalesce(new_sha1, original_sha1),\n TargetFileHashSha256 = coalesce(new_sha256, original_sha256),\n ActorUsername = owner,\n ActorUserType = \"Regular\",\n User = owner,\n DvcHostname = coalesce(site_name, drive_name),\n Dvc = coalesce(site_name, drive_name),\n EventSchema = \"FileEvent\",\n EventSchemaVersion = \"0.2.2\",\n EventCount = int(1)\n| project TimeGenerated, EventStartTime, EventEndTime, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, ActorUsername, ActorUserType, User, TargetFileName, TargetFilePath, TargetFilePathType, TargetFileExtension, TargetFileSize, TargetFileHashSha1, TargetFileHashSha256, application, event_id, timestamp, status, state, policy_id, policy_name, block_reason, summary, drive_name, site_name, new_sha1, original_sha1, new_sha256, original_sha256, version\n",
+ "query": "// One row per SharePoint, OneDrive or Teams file Filewall processed. ThreatVerdict and\n// EventSeverity follow what a Filewall engine found, not the block alone (see\n// FilewallM365ExchangeEvent for the verdicts).\n// The original and sanitized hashes are kept apart: OriginalFile* is the file the user uploaded,\n// the one to look for elsewhere; SanitizedFile* is the copy Filewall put in its place, empty when\n// there is none. TargetFileHashSha1/Sha256 are the file in place now: the sanitized copy if\n// there is one, else the original.\nFilewallFile_CL\n| extend IngestionTime = ingestion_time()\n| extend report_url = column_ifexists('report_url', '')\n| extend FileStatus = tolower(status), FileReason = tolower(block_reason)\n| extend ThreatVerdict = case(\n FileReason == \"antivirus\", \"Malicious\",\n FileReason in (\"static_analysis\", \"type_validation\"), \"Suspicious\",\n FileReason == \"cdr_init_failed\" or FileStatus == \"failed\", \"ProcessingFailed\",\n FileStatus in (\"not_supported\", \"not_sanitized\"), \"NotSanitized\",\n FileReason == \"cdr\", \"Unsanitizable\",\n FileReason in (\"file_policy\", \"size_limitation\", \"encryption_filter\"), \"PolicyBlocked\",\n FileStatus == \"blocked\", \"Blocked\",\n FileStatus in (\"succeed\", \"sanitized\"), \"Sanitized\",\n \"Unknown\")\n| extend\n EventStartTime = TimeGenerated,\n EventEndTime = TimeGenerated,\n EventOriginalType = tostring(event_type),\n EventType = case(status =~ \"blocked\", \"FileBlocked\", status =~ \"succeed\", \"FileModified\", status =~ \"failed\", \"FileError\", \"FileEvent\"),\n EventResult = case(status =~ \"blocked\", 'Blocked', status =~ \"failed\", \"Failure\", status =~ \"succeed\", \"Success\", \"NA\"),\n EventOriginalResultDetails = coalesce(block_reason, state, summary),\n EventSeverity = case(ThreatVerdict == \"Malicious\", \"High\", ThreatVerdict == \"Suspicious\", \"Medium\", ThreatVerdict in (\"ProcessingFailed\", \"NotSanitized\", \"Unsanitizable\", \"Blocked\"), \"Low\", \"Informational\"),\n EventVendor = \"ODI-X\",\n EventProduct = \"Filewall for Microsoft 365\",\n TargetFileName = name,\n TargetFilePath = path,\n TargetFileDirectory = iff(isnotempty(name) and path endswith strcat(\"/\", name), substring(path, 0, strlen(path) - strlen(name) - 1), \"\"),\n TargetFilePathType = application,\n TargetFileExtension = file_type,\n TargetFileSize = tolong(size),\n TargetFileHashSha1 = coalesce(new_sha1, original_sha1),\n TargetFileHashSha256 = coalesce(new_sha256, original_sha256),\n OriginalFileSha256 = tolower(original_sha256),\n OriginalFileSha1 = tolower(original_sha1),\n SanitizedFileSha256 = tolower(new_sha256),\n SanitizedFileSha1 = tolower(new_sha1),\n EventReportUrl = report_url,\n ActorUsername = owner,\n ActorUserType = \"Regular\",\n User = owner,\n DvcHostname = coalesce(site_name, drive_name),\n Dvc = coalesce(site_name, drive_name),\n EventSchema = \"FileEvent\",\n EventSchemaVersion = \"0.2.2\",\n EventCount = int(1)\n| project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, ThreatVerdict, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, ActorUsername, ActorUserType, User, TargetFileName, TargetFilePath, TargetFileDirectory, TargetFilePathType, TargetFileExtension, TargetFileSize, TargetFileHashSha1, TargetFileHashSha256, OriginalFileSha256, OriginalFileSha1, SanitizedFileSha256, SanitizedFileSha1, EventReportUrl, application, event_id, timestamp, status, state, policy_id, policy_name, block_reason, summary, drive_name, site_name, new_sha1, original_sha1, new_sha256, original_sha256, version\n",
"functionParameters": "",
"version": 2,
"tags": [
@@ -1614,10 +1727,11 @@
"contentId": "[variables('parserObject2').parserContentId2]",
"contentKind": "Parser",
"displayName": "Parser for File events",
- "contentProductId": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject2').parserContentId2,'-', '1.0.0')))]",
- "id": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject2').parserContentId2,'-', '1.0.0')))]",
+ "contentProductId": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject2').parserContentId2,'-', '1.1.0')))]",
+ "id": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject2').parserContentId2,'-', '1.1.0')))]",
"version": "[variables('parserObject2').parserVersion2]"
- }
+ },
+ "condition": "[parameters('DeployParser')]"
},
{
"type": "Microsoft.OperationalInsights/workspaces/savedSearches",
@@ -1629,7 +1743,7 @@
"displayName": "Parser for File events",
"category": "Microsoft Sentinel Parser",
"functionAlias": "FilewallM365FileEvent",
- "query": "FilewallFile_CL\n| extend \n EventStartTime = TimeGenerated, \n EventEndTime = TimeGenerated,\n EventOriginalType = tostring(event_type),\n EventType = case(status =~ \"blocked\", \"FileBlocked\", status =~ \"succeed\", \"FileModified\", status =~ \"failed\", \"FileError\", \"FileEvent\"),\n EventResult = case(status =~ \"blocked\", 'Blocked', status =~ \"failed\", \"Failure\", status =~ \"succeed\", \"Success\", \"NA\"),\n EventOriginalResultDetails = coalesce(block_reason, state, summary),\n EventSeverity = case(status =~ \"blocked\", \"High\", status =~ \"failed\", \"Medium\", \"Informational\"),\n EventVendor = \"ODI-X\",\n EventProduct = \"Filewall for Microsoft 365\",\n TargetFileName = name,\n TargetFilePath = path,\n TargetFilePathType = application,\n TargetFileExtension = file_type,\n TargetFileSize = tolong(size),\n TargetFileHashSha1 = coalesce(new_sha1, original_sha1),\n TargetFileHashSha256 = coalesce(new_sha256, original_sha256),\n ActorUsername = owner,\n ActorUserType = \"Regular\",\n User = owner,\n DvcHostname = coalesce(site_name, drive_name),\n Dvc = coalesce(site_name, drive_name),\n EventSchema = \"FileEvent\",\n EventSchemaVersion = \"0.2.2\",\n EventCount = int(1)\n| project TimeGenerated, EventStartTime, EventEndTime, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, ActorUsername, ActorUserType, User, TargetFileName, TargetFilePath, TargetFilePathType, TargetFileExtension, TargetFileSize, TargetFileHashSha1, TargetFileHashSha256, application, event_id, timestamp, status, state, policy_id, policy_name, block_reason, summary, drive_name, site_name, new_sha1, original_sha1, new_sha256, original_sha256, version\n",
+ "query": "// One row per SharePoint, OneDrive or Teams file Filewall processed. ThreatVerdict and\n// EventSeverity follow what a Filewall engine found, not the block alone (see\n// FilewallM365ExchangeEvent for the verdicts).\n// The original and sanitized hashes are kept apart: OriginalFile* is the file the user uploaded,\n// the one to look for elsewhere; SanitizedFile* is the copy Filewall put in its place, empty when\n// there is none. TargetFileHashSha1/Sha256 are the file in place now: the sanitized copy if\n// there is one, else the original.\nFilewallFile_CL\n| extend IngestionTime = ingestion_time()\n| extend report_url = column_ifexists('report_url', '')\n| extend FileStatus = tolower(status), FileReason = tolower(block_reason)\n| extend ThreatVerdict = case(\n FileReason == \"antivirus\", \"Malicious\",\n FileReason in (\"static_analysis\", \"type_validation\"), \"Suspicious\",\n FileReason == \"cdr_init_failed\" or FileStatus == \"failed\", \"ProcessingFailed\",\n FileStatus in (\"not_supported\", \"not_sanitized\"), \"NotSanitized\",\n FileReason == \"cdr\", \"Unsanitizable\",\n FileReason in (\"file_policy\", \"size_limitation\", \"encryption_filter\"), \"PolicyBlocked\",\n FileStatus == \"blocked\", \"Blocked\",\n FileStatus in (\"succeed\", \"sanitized\"), \"Sanitized\",\n \"Unknown\")\n| extend\n EventStartTime = TimeGenerated,\n EventEndTime = TimeGenerated,\n EventOriginalType = tostring(event_type),\n EventType = case(status =~ \"blocked\", \"FileBlocked\", status =~ \"succeed\", \"FileModified\", status =~ \"failed\", \"FileError\", \"FileEvent\"),\n EventResult = case(status =~ \"blocked\", 'Blocked', status =~ \"failed\", \"Failure\", status =~ \"succeed\", \"Success\", \"NA\"),\n EventOriginalResultDetails = coalesce(block_reason, state, summary),\n EventSeverity = case(ThreatVerdict == \"Malicious\", \"High\", ThreatVerdict == \"Suspicious\", \"Medium\", ThreatVerdict in (\"ProcessingFailed\", \"NotSanitized\", \"Unsanitizable\", \"Blocked\"), \"Low\", \"Informational\"),\n EventVendor = \"ODI-X\",\n EventProduct = \"Filewall for Microsoft 365\",\n TargetFileName = name,\n TargetFilePath = path,\n TargetFileDirectory = iff(isnotempty(name) and path endswith strcat(\"/\", name), substring(path, 0, strlen(path) - strlen(name) - 1), \"\"),\n TargetFilePathType = application,\n TargetFileExtension = file_type,\n TargetFileSize = tolong(size),\n TargetFileHashSha1 = coalesce(new_sha1, original_sha1),\n TargetFileHashSha256 = coalesce(new_sha256, original_sha256),\n OriginalFileSha256 = tolower(original_sha256),\n OriginalFileSha1 = tolower(original_sha1),\n SanitizedFileSha256 = tolower(new_sha256),\n SanitizedFileSha1 = tolower(new_sha1),\n EventReportUrl = report_url,\n ActorUsername = owner,\n ActorUserType = \"Regular\",\n User = owner,\n DvcHostname = coalesce(site_name, drive_name),\n Dvc = coalesce(site_name, drive_name),\n EventSchema = \"FileEvent\",\n EventSchemaVersion = \"0.2.2\",\n EventCount = int(1)\n| project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, ThreatVerdict, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, ActorUsername, ActorUserType, User, TargetFileName, TargetFilePath, TargetFileDirectory, TargetFilePathType, TargetFileExtension, TargetFileSize, TargetFileHashSha1, TargetFileHashSha256, OriginalFileSha256, OriginalFileSha1, SanitizedFileSha256, SanitizedFileSha1, EventReportUrl, application, event_id, timestamp, status, state, policy_id, policy_name, block_reason, summary, drive_name, site_name, new_sha1, original_sha1, new_sha256, original_sha256, version\n",
"functionParameters": "",
"version": 2,
"tags": [
@@ -1638,7 +1752,8 @@
"value": ""
}
]
- }
+ },
+ "condition": "[parameters('DeployParser')]"
},
{
"type": "Microsoft.OperationalInsights/workspaces/providers/metadata",
@@ -1668,7 +1783,143 @@
"tier": "Partner",
"link": "https://www.odi-x.com/contact-filewall-support/"
}
- }
+ },
+ "condition": "[parameters('DeployParser')]"
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates",
+ "apiVersion": "2023-04-01-preview",
+ "name": "[variables('parserObject3').parserTemplateSpecName3]",
+ "location": "[parameters('workspace-location')]",
+ "dependsOn": [
+ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]"
+ ],
+ "properties": {
+ "description": "FilewallM365EmailAttachmentEvent Data Parser with template version 3.2.2",
+ "mainTemplate": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "[variables('parserObject3').parserVersion3]",
+ "parameters": {},
+ "variables": {},
+ "resources": [
+ {
+ "name": "[variables('parserObject3')._parserName3]",
+ "apiVersion": "2025-07-01",
+ "type": "Microsoft.OperationalInsights/workspaces/savedSearches",
+ "location": "[parameters('workspace-location')]",
+ "properties": {
+ "eTag": "*",
+ "displayName": "Parser for Exchange email attachment events",
+ "category": "Microsoft Sentinel Parser",
+ "functionAlias": "FilewallM365EmailAttachmentEvent",
+ "query": "// One row per email attachment Filewall processed, with its message's columns and its own\n// ThreatVerdict and EventSeverity (see FilewallM365ExchangeEvent for the verdicts).\n// The original and sanitized hashes are kept apart: OriginalFile* is the file the sender sent,\n// the one to look for elsewhere; SanitizedFile* is the copy Filewall delivered in its place,\n// empty when nothing was delivered.\nFilewallM365ExchangeEvent()\n| mv-expand Attachment = EmailAttachments\n| extend\n AttachmentName = tostring(Attachment.name),\n AttachmentFileType = tostring(Attachment.type),\n AttachmentSize = tostring(Attachment.size),\n AttachmentStatus = tostring(Attachment.status),\n AttachmentBlockReason = tostring(Attachment.block_reason),\n AttachmentSummary = tostring(Attachment.summary),\n ThreatVerdict = tostring(Attachment.verdict),\n EventSeverity = tostring(Attachment.severity),\n OriginalFileSha256 = tolower(tostring(Attachment.original_sha256)),\n OriginalFileSha1 = tolower(tostring(Attachment.original_sha1)),\n SanitizedFileSha256 = tolower(tostring(Attachment.new_sha256)),\n SanitizedFileSha1 = tolower(tostring(Attachment.new_sha1))\n| extend\n EventType = iff(ThreatVerdict in (\"Malicious\", \"Suspicious\"), \"ThreatInfo\", \"Informational\"),\n EventResult = case(AttachmentStatus =~ \"blocked\", \"Blocked\", AttachmentStatus =~ \"failed\", \"Failure\", AttachmentStatus in~ (\"succeed\", \"sanitized\"), \"Success\", \"NA\")\n| project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventUid, EventType, EventOriginalType, EventResult, EventSeverity, ThreatVerdict, EventVendor, EventProduct, User, ActorUsername, EmailSubject, EmailSenderFrom, EmailRecipientTo, EmailDirection, EmailInternetMessageId, FilewallMessageId, EventReportUrl, AlertPolicyId, AlertPolicyName, AttachmentName, AttachmentFileType, AttachmentSize, AttachmentStatus, AttachmentBlockReason, AttachmentSummary, OriginalFileSha256, OriginalFileSha1, SanitizedFileSha256, SanitizedFileSha1, application, event_id, timestamp\n",
+ "functionParameters": "",
+ "version": 2,
+ "tags": [
+ {
+ "name": "description",
+ "value": ""
+ }
+ ]
+ }
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/providers/metadata",
+ "apiVersion": "2022-01-01-preview",
+ "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Parser-', last(split(variables('parserObject3')._parserId3,'/'))))]",
+ "dependsOn": [
+ "[variables('parserObject3')._parserId3]"
+ ],
+ "properties": {
+ "parentId": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'FilewallM365EmailAttachmentEvent')]",
+ "contentId": "[variables('parserObject3').parserContentId3]",
+ "kind": "Parser",
+ "version": "[variables('parserObject3').parserVersion3]",
+ "source": {
+ "name": "Filewall for Microsoft 365",
+ "kind": "Solution",
+ "sourceId": "[variables('_solutionId')]"
+ },
+ "author": {
+ "name": "Filewall",
+ "email": "[variables('_email')]"
+ },
+ "support": {
+ "name": "Filewall",
+ "email": "support@filewall.com",
+ "tier": "Partner",
+ "link": "https://www.odi-x.com/contact-filewall-support/"
+ }
+ }
+ }
+ ]
+ },
+ "packageKind": "Solution",
+ "packageVersion": "[variables('_solutionVersion')]",
+ "packageName": "[variables('_solutionName')]",
+ "packageId": "[variables('_solutionId')]",
+ "contentSchemaVersion": "3.0.0",
+ "contentId": "[variables('parserObject3').parserContentId3]",
+ "contentKind": "Parser",
+ "displayName": "Parser for Exchange email attachment events",
+ "contentProductId": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject3').parserContentId3,'-', '1.0.0')))]",
+ "id": "[concat(take(variables('_solutionId'),50),'-','pr','-', uniqueString(concat(variables('_solutionId'),'-','Parser','-',variables('parserObject3').parserContentId3,'-', '1.0.0')))]",
+ "version": "[variables('parserObject3').parserVersion3]"
+ },
+ "condition": "[parameters('DeployParser')]"
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/savedSearches",
+ "apiVersion": "2025-07-01",
+ "name": "[variables('parserObject3')._parserName3]",
+ "location": "[parameters('workspace-location')]",
+ "properties": {
+ "eTag": "*",
+ "displayName": "Parser for Exchange email attachment events",
+ "category": "Microsoft Sentinel Parser",
+ "functionAlias": "FilewallM365EmailAttachmentEvent",
+ "query": "// One row per email attachment Filewall processed, with its message's columns and its own\n// ThreatVerdict and EventSeverity (see FilewallM365ExchangeEvent for the verdicts).\n// The original and sanitized hashes are kept apart: OriginalFile* is the file the sender sent,\n// the one to look for elsewhere; SanitizedFile* is the copy Filewall delivered in its place,\n// empty when nothing was delivered.\nFilewallM365ExchangeEvent()\n| mv-expand Attachment = EmailAttachments\n| extend\n AttachmentName = tostring(Attachment.name),\n AttachmentFileType = tostring(Attachment.type),\n AttachmentSize = tostring(Attachment.size),\n AttachmentStatus = tostring(Attachment.status),\n AttachmentBlockReason = tostring(Attachment.block_reason),\n AttachmentSummary = tostring(Attachment.summary),\n ThreatVerdict = tostring(Attachment.verdict),\n EventSeverity = tostring(Attachment.severity),\n OriginalFileSha256 = tolower(tostring(Attachment.original_sha256)),\n OriginalFileSha1 = tolower(tostring(Attachment.original_sha1)),\n SanitizedFileSha256 = tolower(tostring(Attachment.new_sha256)),\n SanitizedFileSha1 = tolower(tostring(Attachment.new_sha1))\n| extend\n EventType = iff(ThreatVerdict in (\"Malicious\", \"Suspicious\"), \"ThreatInfo\", \"Informational\"),\n EventResult = case(AttachmentStatus =~ \"blocked\", \"Blocked\", AttachmentStatus =~ \"failed\", \"Failure\", AttachmentStatus in~ (\"succeed\", \"sanitized\"), \"Success\", \"NA\")\n| project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventUid, EventType, EventOriginalType, EventResult, EventSeverity, ThreatVerdict, EventVendor, EventProduct, User, ActorUsername, EmailSubject, EmailSenderFrom, EmailRecipientTo, EmailDirection, EmailInternetMessageId, FilewallMessageId, EventReportUrl, AlertPolicyId, AlertPolicyName, AttachmentName, AttachmentFileType, AttachmentSize, AttachmentStatus, AttachmentBlockReason, AttachmentSummary, OriginalFileSha256, OriginalFileSha1, SanitizedFileSha256, SanitizedFileSha1, application, event_id, timestamp\n",
+ "functionParameters": "",
+ "version": 2,
+ "tags": [
+ {
+ "name": "description",
+ "value": ""
+ }
+ ]
+ },
+ "condition": "[parameters('DeployParser')]"
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/providers/metadata",
+ "apiVersion": "2022-01-01-preview",
+ "location": "[parameters('workspace-location')]",
+ "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Parser-', last(split(variables('parserObject3')._parserId3,'/'))))]",
+ "dependsOn": [
+ "[variables('parserObject3')._parserId3]"
+ ],
+ "properties": {
+ "parentId": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('workspace'), 'FilewallM365EmailAttachmentEvent')]",
+ "contentId": "[variables('parserObject3').parserContentId3]",
+ "kind": "Parser",
+ "version": "[variables('parserObject3').parserVersion3]",
+ "source": {
+ "kind": "Solution",
+ "name": "Filewall for Microsoft 365",
+ "sourceId": "[variables('_solutionId')]"
+ },
+ "author": {
+ "name": "Filewall",
+ "email": "[variables('_email')]"
+ },
+ "support": {
+ "name": "Filewall",
+ "email": "support@filewall.com",
+ "tier": "Partner",
+ "link": "https://www.odi-x.com/contact-filewall-support/"
+ }
+ },
+ "condition": "[parameters('DeployParser')]"
},
{
"type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates",
@@ -1679,7 +1930,7 @@
"[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]"
],
"properties": {
- "description": "BlockedEmails_AnalyticalRules Analytics Rule with template version 3.0.0",
+ "description": "BlockedEmails_AnalyticalRules Analytics Rule with template version 3.2.2",
"mainTemplate": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "[variables('analyticRuleObject1').analyticRuleVersion1]",
@@ -1693,12 +1944,12 @@
"kind": "Scheduled",
"location": "[parameters('workspace-location')]",
"properties": {
- "description": "Identifies emails blocked by Filewall for Microsoft 365 (Exchange).",
- "displayName": "Filewall - Blocked emails",
- "enabled": false,
- "query": "FilewallM365ExchangeEvent()\n| where EventVendor == 'ODI-X' and EventProduct == 'Filewall for Microsoft 365'\n| where EventType == 'ThreatInfo' and EventResult == 'Blocked'\n",
+ "description": "Identifies attachments in inbound email that Filewall for Microsoft 365 blocked because an antivirus engine found malware (High severity), or because static analysis flagged the file or its content does not match its file type (Medium severity).\nAttachments blocked only by a policy rule (file type, size or encryption) and attachments sanitized by CDR are not malware verdicts and do not raise this alert; they stay available in the FilewallM365EmailAttachmentEvent parser.\nThe alert maps the recipient, the message and the original file hashes, so Microsoft Defender XDR can correlate it with other evidence for the same file or user, and it links to the message in the Filewall console.",
+ "displayName": "Filewall - Malicious or suspicious email attachment blocked",
+ "enabled": true,
+ "query": "// TimeGenerated is the email's received time, which can be well before Filewall finished with it,\n// so the rule looks back an hour and takes each attachment once, by when it was ingested.\nFilewallM365EmailAttachmentEvent()\n| where IngestionTime > ago(5m)\n| where ThreatVerdict in (\"Malicious\", \"Suspicious\")\n| where EmailDirection != \"Outbound\"\n| extend RecipientAccountName = tostring(split(EmailRecipientTo, \"@\")[0]), RecipientUPNSuffix = tostring(split(EmailRecipientTo, \"@\")[1])\n| extend Sha256Algorithm = \"SHA256\", Sha1Algorithm = \"SHA1\"\n| project TimeGenerated, ThreatVerdict, EventSeverity, AttachmentName, AttachmentFileType, AttachmentBlockReason, AttachmentSummary, OriginalFileSha256, OriginalFileSha1, Sha256Algorithm, Sha1Algorithm, EmailSubject, EmailSenderFrom, EmailRecipientTo, RecipientAccountName, RecipientUPNSuffix, EmailDirection, EmailInternetMessageId, FilewallMessageId, AlertPolicyName, EventReportUrl\n",
"queryFrequency": "PT5M",
- "queryPeriod": "PT5M",
+ "queryPeriod": "PT1H",
"severity": "High",
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
@@ -1707,24 +1958,31 @@
"status": "Available",
"requiredDataConnectors": [
{
+ "connectorId": "FilewallM365",
"dataTypes": [
"FilewallExchange_CL"
- ],
- "connectorId": "FilewallM365"
+ ]
}
],
"tactics": [
- "Exfiltration"
+ "InitialAccess"
+ ],
+ "subTechniques": [
+ "T1566.001"
],
"techniques": [
- "T1048"
+ "T1566"
],
"entityMappings": [
{
"fieldMappings": [
{
- "columnName": "ActorUsername",
- "identifier": "FullName"
+ "identifier": "Name",
+ "columnName": "RecipientAccountName"
+ },
+ {
+ "identifier": "UPNSuffix",
+ "columnName": "RecipientUPNSuffix"
}
],
"entityType": "Account"
@@ -1732,30 +1990,98 @@
{
"fieldMappings": [
{
- "columnName": "EmailRecipientTo",
- "identifier": "Recipient"
+ "identifier": "MailboxPrimaryAddress",
+ "columnName": "EmailRecipientTo"
+ }
+ ],
+ "entityType": "Mailbox"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Recipient",
+ "columnName": "EmailRecipientTo"
},
{
- "columnName": "EmailSubject",
- "identifier": "Subject"
+ "identifier": "Sender",
+ "columnName": "EmailSenderFrom"
},
{
- "columnName": "EmailSenderFrom",
- "identifier": "Sender"
+ "identifier": "InternetMessageId",
+ "columnName": "EmailInternetMessageId"
}
],
"entityType": "MailMessage"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Name",
+ "columnName": "AttachmentName"
+ }
+ ],
+ "entityType": "File"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Algorithm",
+ "columnName": "Sha256Algorithm"
+ },
+ {
+ "identifier": "Value",
+ "columnName": "OriginalFileSha256"
+ }
+ ],
+ "entityType": "FileHash"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Algorithm",
+ "columnName": "Sha1Algorithm"
+ },
+ {
+ "identifier": "Value",
+ "columnName": "OriginalFileSha1"
+ }
+ ],
+ "entityType": "FileHash"
}
],
"eventGroupingSettings": {
"aggregationKind": "AlertPerResult"
},
+ "customDetails": {
+ "Detail": "AttachmentSummary",
+ "Subject": "EmailSubject",
+ "BlockReason": "AttachmentBlockReason",
+ "OriginalSHA256": "OriginalFileSha256",
+ "PolicyName": "AlertPolicyName",
+ "Verdict": "ThreatVerdict",
+ "FilewallReport": "EventReportUrl",
+ "FilewallMessageId": "FilewallMessageId"
+ },
+ "alertDetailsOverride": {
+ "alertDisplayNameFormat": "Filewall blocked a {{ThreatVerdict}} email attachment - {{AttachmentName}}",
+ "alertDynamicProperties": [
+ {
+ "alertProperty": "AlertLink",
+ "value": "EventReportUrl"
+ }
+ ],
+ "alertDescriptionFormat": "Filewall for Microsoft 365 blocked the attachment {{AttachmentName}} in an email to {{EmailRecipientTo}}, block reason {{AttachmentBlockReason}}. The custom details hold the original file hash and policy, and the alert link opens the message in the Filewall console.",
+ "alertSeverityColumnName": "EventSeverity"
+ },
"incidentConfiguration": {
"groupingConfiguration": {
+ "enabled": true,
+ "matchingMethod": "Selected",
+ "lookbackDuration": "PT5H",
"reopenClosedIncident": false,
- "enabled": false,
- "matchingMethod": "AllEntities",
- "lookbackDuration": "PT1H"
+ "groupByEntities": [
+ "FileHash"
+ ]
},
"createIncident": true
}
@@ -1797,11 +2123,12 @@
"contentSchemaVersion": "3.0.0",
"contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]",
"contentKind": "AnalyticsRule",
- "displayName": "Filewall - Blocked emails",
+ "displayName": "Filewall - Malicious or suspicious email attachment blocked",
"contentProductId": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]",
"id": "[variables('analyticRuleObject1')._analyticRulecontentProductId1]",
"version": "[variables('analyticRuleObject1').analyticRuleVersion1]"
- }
+ },
+ "condition": "[parameters('DeployAnalyticsRule')]"
},
{
"type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates",
@@ -1812,7 +2139,7 @@
"[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]"
],
"properties": {
- "description": "BlockedFiles_AnalyticalRules Analytics Rule with template version 3.0.0",
+ "description": "BlockedFiles_AnalyticalRules Analytics Rule with template version 3.2.2",
"mainTemplate": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "[variables('analyticRuleObject2').analyticRuleVersion2]",
@@ -1826,12 +2153,12 @@
"kind": "Scheduled",
"location": "[parameters('workspace-location')]",
"properties": {
- "description": "Identifies files blocked by Filewall for Microsoft 365 (SharePoint/OneDrive/Teams).",
- "displayName": "Filewall - Blocked files",
- "enabled": false,
- "query": "FilewallM365FileEvent()\n| where EventVendor == 'ODI-X' and EventProduct == 'Filewall for Microsoft 365'\n| where EventType == 'FileBlocked'\n",
+ "description": "Identifies SharePoint, OneDrive and Teams files that Filewall for Microsoft 365 blocked because an antivirus engine found malware (High severity), or because static analysis flagged the file or its content does not match its file type (Medium severity).\nFiles blocked only by a policy rule (file type, size or encryption) and files sanitized by CDR are not malware verdicts and do not raise this alert; they stay available in the FilewallM365FileEvent parser.\nThe alert maps the file owner, the file and its original hashes, so Microsoft Defender XDR can correlate it with other evidence for the same file or user, and it links to the file in the Filewall console.",
+ "displayName": "Filewall - Malicious or suspicious file blocked in SharePoint, OneDrive or Teams",
+ "enabled": true,
+ "query": "// A file can be ingested well after its TimeGenerated, so the rule looks back an hour and takes\n// each file once, by when it was ingested.\nFilewallM365FileEvent()\n| where IngestionTime > ago(5m)\n| where ThreatVerdict in (\"Malicious\", \"Suspicious\")\n| extend OwnerAccountName = tostring(split(ActorUsername, \"@\")[0]), OwnerUPNSuffix = tostring(split(ActorUsername, \"@\")[1])\n| extend Sha256Algorithm = \"SHA256\", Sha1Algorithm = \"SHA1\"\n| project TimeGenerated, ThreatVerdict, EventSeverity, TargetFileName, TargetFileDirectory, TargetFilePathType, TargetFileExtension, block_reason, summary, OriginalFileSha256, OriginalFileSha1, Sha256Algorithm, Sha1Algorithm, ActorUsername, OwnerAccountName, OwnerUPNSuffix, site_name, drive_name, policy_name, EventReportUrl\n",
"queryFrequency": "PT5M",
- "queryPeriod": "PT5M",
+ "queryPeriod": "PT1H",
"severity": "High",
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
@@ -1840,24 +2167,28 @@
"status": "Available",
"requiredDataConnectors": [
{
+ "connectorId": "FilewallM365",
"dataTypes": [
"FilewallFile_CL"
- ],
- "connectorId": "FilewallM365"
+ ]
}
],
"tactics": [
- "Exfiltration"
+ "LateralMovement"
],
"techniques": [
- "T1048"
+ "T1080"
],
"entityMappings": [
{
"fieldMappings": [
{
- "columnName": "ActorUsername",
- "identifier": "FullName"
+ "identifier": "Name",
+ "columnName": "OwnerAccountName"
+ },
+ {
+ "identifier": "UPNSuffix",
+ "columnName": "OwnerUPNSuffix"
}
],
"entityType": "Account"
@@ -1865,26 +2196,77 @@
{
"fieldMappings": [
{
- "columnName": "TargetFileName",
- "identifier": "Name"
+ "identifier": "Name",
+ "columnName": "TargetFileName"
},
{
- "columnName": "TargetFilePath",
- "identifier": "Directory"
+ "identifier": "Directory",
+ "columnName": "TargetFileDirectory"
}
],
"entityType": "File"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Algorithm",
+ "columnName": "Sha256Algorithm"
+ },
+ {
+ "identifier": "Value",
+ "columnName": "OriginalFileSha256"
+ }
+ ],
+ "entityType": "FileHash"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Algorithm",
+ "columnName": "Sha1Algorithm"
+ },
+ {
+ "identifier": "Value",
+ "columnName": "OriginalFileSha1"
+ }
+ ],
+ "entityType": "FileHash"
}
],
"eventGroupingSettings": {
"aggregationKind": "AlertPerResult"
},
+ "customDetails": {
+ "Site": "site_name",
+ "Detail": "summary",
+ "Workload": "TargetFilePathType",
+ "BlockReason": "block_reason",
+ "OriginalSHA256": "OriginalFileSha256",
+ "Drive": "drive_name",
+ "PolicyName": "policy_name",
+ "Verdict": "ThreatVerdict",
+ "FilewallReport": "EventReportUrl"
+ },
+ "alertDetailsOverride": {
+ "alertDisplayNameFormat": "Filewall blocked a {{ThreatVerdict}} file in {{TargetFilePathType}} - {{TargetFileName}}",
+ "alertDynamicProperties": [
+ {
+ "alertProperty": "AlertLink",
+ "value": "EventReportUrl"
+ }
+ ],
+ "alertDescriptionFormat": "Filewall for Microsoft 365 blocked the file {{TargetFileName}} owned by {{ActorUsername}}, block reason {{block_reason}}. The custom details hold the original file hash, site and policy, and the alert link opens the file in the Filewall console.",
+ "alertSeverityColumnName": "EventSeverity"
+ },
"incidentConfiguration": {
"groupingConfiguration": {
+ "enabled": true,
+ "matchingMethod": "Selected",
+ "lookbackDuration": "PT5H",
"reopenClosedIncident": false,
- "enabled": false,
- "matchingMethod": "AllEntities",
- "lookbackDuration": "PT1H"
+ "groupByEntities": [
+ "FileHash"
+ ]
},
"createIncident": true
}
@@ -1926,23 +2308,319 @@
"contentSchemaVersion": "3.0.0",
"contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]",
"contentKind": "AnalyticsRule",
- "displayName": "Filewall - Blocked files",
+ "displayName": "Filewall - Malicious or suspicious file blocked in SharePoint, OneDrive or Teams",
"contentProductId": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]",
"id": "[variables('analyticRuleObject2')._analyticRulecontentProductId2]",
"version": "[variables('analyticRuleObject2').analyticRuleVersion2]"
- }
+ },
+ "condition": "[parameters('DeployAnalyticsRule')]"
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates",
+ "apiVersion": "2023-04-01-preview",
+ "name": "[variables('analyticRuleObject3').analyticRuleTemplateSpecName3]",
+ "location": "[parameters('workspace-location')]",
+ "dependsOn": [
+ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]"
+ ],
+ "properties": {
+ "description": "BlockedFileSeenOnDevice_AnalyticalRules Analytics Rule with template version 3.2.2",
+ "mainTemplate": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "[variables('analyticRuleObject3').analyticRuleVersion3]",
+ "parameters": {},
+ "variables": {},
+ "resources": [
+ {
+ "type": "Microsoft.SecurityInsights/AlertRuleTemplates",
+ "name": "[variables('analyticRuleObject3')._analyticRulecontentId3]",
+ "apiVersion": "2023-02-01-preview",
+ "kind": "Scheduled",
+ "location": "[parameters('workspace-location')]",
+ "properties": {
+ "description": "Correlates files that Filewall for Microsoft 365 blocked as malicious or suspicious, in email attachments or in SharePoint, OneDrive and Teams, with Microsoft Defender for Endpoint file events.\nA match means the original file reached a device by another path after Filewall blocked it, for example a direct download, a removable drive, another mail flow or a copy released from quarantine.\nThe match is on the original file hash Filewall recorded; the sanitized hash is kept in the custom details. Requires the Microsoft Defender XDR connector to stream DeviceFileEvents into the workspace.",
+ "displayName": "Filewall - File Filewall blocked as malicious or suspicious seen on a device",
+ "enabled": true,
+ "query": "// Files Filewall blocked as malicious or suspicious in the last 14 days, keyed by original hash,\n// matched with device file events ingested in the last hour that happened after the block.\nlet deviceLookback = 1h;\nlet FilewallBlocked = union\n (FilewallM365EmailAttachmentEvent()\n | where ThreatVerdict in (\"Malicious\", \"Suspicious\") and isnotempty(OriginalFileSha256)\n | project FilewallTime = TimeGenerated, SHA256 = OriginalFileSha256, SanitizedSHA256 = SanitizedFileSha256, ThreatVerdict, EventSeverity, FilewallFileName = AttachmentName, FilewallUser = EmailRecipientTo, FilewallSource = \"exchange\", BlockReason = AttachmentBlockReason, EventReportUrl),\n (FilewallM365FileEvent()\n | where ThreatVerdict in (\"Malicious\", \"Suspicious\") and isnotempty(OriginalFileSha256)\n | project FilewallTime = TimeGenerated, SHA256 = OriginalFileSha256, SanitizedSHA256 = SanitizedFileSha256, ThreatVerdict, EventSeverity, FilewallFileName = TargetFileName, FilewallUser = ActorUsername, FilewallSource = TargetFilePathType, BlockReason = block_reason, EventReportUrl)\n | summarize arg_min(FilewallTime, *) by SHA256;\nDeviceFileEvents\n| where TimeGenerated > ago(deviceLookback + 1h)\n| where ingestion_time() > ago(deviceLookback)\n| where isnotempty(SHA256)\n| extend SHA256 = tolower(SHA256)\n| join kind=inner FilewallBlocked on SHA256\n| where TimeGenerated > FilewallTime\n| summarize arg_min(TimeGenerated, *) by SHA256, DeviceId\n| extend DeviceAccountUpn = tostring(InitiatingProcessAccountUpn)\n| extend\n DeviceAccountName = iff(isnotempty(DeviceAccountUpn), tostring(split(DeviceAccountUpn, \"@\")[0]), InitiatingProcessAccountName),\n DeviceAccountUPNSuffix = iff(isnotempty(DeviceAccountUpn), tostring(split(DeviceAccountUpn, \"@\")[1]), \"\"),\n DeviceAccountNTDomain = iff(isempty(DeviceAccountUpn), InitiatingProcessAccountDomain, \"\"),\n FilewallAccountName = tostring(split(FilewallUser, \"@\")[0]),\n FilewallUPNSuffix = tostring(split(FilewallUser, \"@\")[1]),\n FileDirectory = iff(FolderPath endswith FileName, substring(FolderPath, 0, strlen(FolderPath) - strlen(FileName) - 1), FolderPath),\n Sha256Algorithm = \"SHA256\"\n| project TimeGenerated, DeviceId, DeviceName, ActionType, FileName, FileDirectory, FolderPath, SHA256, Sha256Algorithm, SanitizedSHA256, InitiatingProcessFileName, DeviceAccountName, DeviceAccountUPNSuffix, DeviceAccountNTDomain, ThreatVerdict, EventSeverity, BlockReason, FilewallTime, FilewallFileName, FilewallUser, FilewallAccountName, FilewallUPNSuffix, FilewallSource, EventReportUrl\n",
+ "queryFrequency": "PT1H",
+ "queryPeriod": "P14D",
+ "severity": "High",
+ "suppressionDuration": "PT1H",
+ "suppressionEnabled": false,
+ "triggerOperator": "GreaterThan",
+ "triggerThreshold": 0,
+ "status": "Available",
+ "requiredDataConnectors": [
+ {
+ "connectorId": "FilewallM365",
+ "dataTypes": [
+ "FilewallExchange_CL",
+ "FilewallFile_CL"
+ ]
+ },
+ {
+ "connectorId": "MicrosoftThreatProtection",
+ "dataTypes": [
+ "DeviceFileEvents"
+ ]
+ }
+ ],
+ "tactics": [
+ "Execution"
+ ],
+ "subTechniques": [
+ "T1204.002"
+ ],
+ "techniques": [
+ "T1204"
+ ],
+ "entityMappings": [
+ {
+ "fieldMappings": [
+ {
+ "identifier": "FullName",
+ "columnName": "DeviceName"
+ }
+ ],
+ "entityType": "Host"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Name",
+ "columnName": "DeviceAccountName"
+ },
+ {
+ "identifier": "UPNSuffix",
+ "columnName": "DeviceAccountUPNSuffix"
+ },
+ {
+ "identifier": "NTDomain",
+ "columnName": "DeviceAccountNTDomain"
+ }
+ ],
+ "entityType": "Account"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Name",
+ "columnName": "FilewallAccountName"
+ },
+ {
+ "identifier": "UPNSuffix",
+ "columnName": "FilewallUPNSuffix"
+ }
+ ],
+ "entityType": "Account"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Name",
+ "columnName": "FileName"
+ },
+ {
+ "identifier": "Directory",
+ "columnName": "FileDirectory"
+ }
+ ],
+ "entityType": "File"
+ },
+ {
+ "fieldMappings": [
+ {
+ "identifier": "Algorithm",
+ "columnName": "Sha256Algorithm"
+ },
+ {
+ "identifier": "Value",
+ "columnName": "SHA256"
+ }
+ ],
+ "entityType": "FileHash"
+ }
+ ],
+ "eventGroupingSettings": {
+ "aggregationKind": "AlertPerResult"
+ },
+ "customDetails": {
+ "SanitizedSHA256": "SanitizedSHA256",
+ "FilewallBlockedAt": "FilewallTime",
+ "BlockReason": "BlockReason",
+ "FilewallSource": "FilewallSource",
+ "Verdict": "ThreatVerdict",
+ "InitiatingProcess": "InitiatingProcessFileName",
+ "FilewallReport": "EventReportUrl",
+ "FilewallFileName": "FilewallFileName",
+ "FilewallUser": "FilewallUser",
+ "DeviceAction": "ActionType"
+ },
+ "alertDetailsOverride": {
+ "alertDisplayNameFormat": "File Filewall blocked as {{ThreatVerdict}} seen on {{DeviceName}}",
+ "alertDynamicProperties": [
+ {
+ "alertProperty": "AlertLink",
+ "value": "EventReportUrl"
+ }
+ ],
+ "alertDescriptionFormat": "Filewall for Microsoft 365 blocked this file as {{ThreatVerdict}} ({{BlockReason}}), and Microsoft Defender for Endpoint then recorded it on {{DeviceName}}. The original file reached the device by another path; the alert link opens the blocked item in the Filewall console.",
+ "alertSeverityColumnName": "EventSeverity"
+ },
+ "incidentConfiguration": {
+ "groupingConfiguration": {
+ "enabled": true,
+ "matchingMethod": "Selected",
+ "lookbackDuration": "PT5H",
+ "reopenClosedIncident": false,
+ "groupByEntities": [
+ "FileHash"
+ ]
+ },
+ "createIncident": true
+ }
+ }
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/providers/metadata",
+ "apiVersion": "2022-01-01-preview",
+ "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleObject3').analyticRuleId3,'/'))))]",
+ "properties": {
+ "description": "Filewall for Microsoft 365 Analytics Rule 3",
+ "parentId": "[variables('analyticRuleObject3').analyticRuleId3]",
+ "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]",
+ "kind": "AnalyticsRule",
+ "version": "[variables('analyticRuleObject3').analyticRuleVersion3]",
+ "source": {
+ "kind": "Solution",
+ "name": "Filewall for Microsoft 365",
+ "sourceId": "[variables('_solutionId')]"
+ },
+ "author": {
+ "name": "Filewall",
+ "email": "[variables('_email')]"
+ },
+ "support": {
+ "name": "Filewall",
+ "email": "support@filewall.com",
+ "tier": "Partner",
+ "link": "https://www.odi-x.com/contact-filewall-support/"
+ }
+ }
+ }
+ ]
+ },
+ "packageKind": "Solution",
+ "packageVersion": "[variables('_solutionVersion')]",
+ "packageName": "[variables('_solutionName')]",
+ "packageId": "[variables('_solutionId')]",
+ "contentSchemaVersion": "3.0.0",
+ "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]",
+ "contentKind": "AnalyticsRule",
+ "displayName": "Filewall - File Filewall blocked as malicious or suspicious seen on a device",
+ "contentProductId": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]",
+ "id": "[variables('analyticRuleObject3')._analyticRulecontentProductId3]",
+ "version": "[variables('analyticRuleObject3').analyticRuleVersion3]"
+ },
+ "condition": "[parameters('DeployAnalyticsRule')]"
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates",
+ "apiVersion": "2023-04-01-preview",
+ "name": "[variables('huntingQueryObject1').huntingQueryTemplateSpecName1]",
+ "location": "[parameters('workspace-location')]",
+ "dependsOn": [
+ "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]"
+ ],
+ "properties": {
+ "description": "SanitizedOriginalSeenOnDevice_HuntingQueries Hunting Query with template version 3.2.2",
+ "mainTemplate": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "[variables('huntingQueryObject1').huntingQueryVersion1]",
+ "parameters": {},
+ "variables": {},
+ "resources": [
+ {
+ "type": "Microsoft.OperationalInsights/savedSearches",
+ "apiVersion": "2025-07-01",
+ "name": "Filewall_for_Microsoft_365_Hunting_Query_1",
+ "location": "[parameters('workspace-location')]",
+ "properties": {
+ "eTag": "*",
+ "displayName": "Filewall - Original of a file Filewall sanitized seen on a device",
+ "category": "Hunting Queries",
+ "query": "let lookback = 14d;\n// only files whose content Filewall changed: the original and the sanitized hash differ\nlet FilewallSanitized = union\n (FilewallM365EmailAttachmentEvent()\n | where TimeGenerated > ago(lookback)\n | where ThreatVerdict == \"Sanitized\" and EmailDirection != \"Outbound\"\n | where isnotempty(OriginalFileSha256) and isnotempty(SanitizedFileSha256) and OriginalFileSha256 != SanitizedFileSha256\n | project FilewallTime = TimeGenerated, SHA256 = OriginalFileSha256, SanitizedSHA256 = SanitizedFileSha256, FilewallFileName = AttachmentName, FilewallUser = EmailRecipientTo, FilewallSource = \"exchange\", Sender = EmailSenderFrom, EventReportUrl),\n (FilewallM365FileEvent()\n | where TimeGenerated > ago(lookback)\n | where ThreatVerdict == \"Sanitized\"\n | where isnotempty(OriginalFileSha256) and isnotempty(SanitizedFileSha256) and OriginalFileSha256 != SanitizedFileSha256\n | project FilewallTime = TimeGenerated, SHA256 = OriginalFileSha256, SanitizedSHA256 = SanitizedFileSha256, FilewallFileName = TargetFileName, FilewallUser = ActorUsername, FilewallSource = TargetFilePathType, Sender = \"\", EventReportUrl)\n | summarize arg_min(FilewallTime, *) by SHA256;\nDeviceFileEvents\n| where TimeGenerated > ago(lookback)\n| where isnotempty(SHA256)\n| extend SHA256 = tolower(SHA256)\n| join kind=inner FilewallSanitized on SHA256\n| where TimeGenerated > FilewallTime\n| summarize FirstSeenOnDevice = min(TimeGenerated), LastSeenOnDevice = max(TimeGenerated), DeviceActions = make_set(ActionType), arg_min(TimeGenerated, DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessAccountUpn, FilewallTime, FilewallFileName, FilewallUser, FilewallSource, Sender, SanitizedSHA256, EventReportUrl) by SHA256, DeviceId\n| extend\n DeviceAccountName = tostring(split(InitiatingProcessAccountUpn, \"@\")[0]),\n DeviceAccountUPNSuffix = tostring(split(InitiatingProcessAccountUpn, \"@\")[1]),\n FileDirectory = iff(FolderPath endswith FileName, substring(FolderPath, 0, strlen(FolderPath) - strlen(FileName) - 1), FolderPath),\n Sha256Algorithm = \"SHA256\"\n| project FirstSeenOnDevice, LastSeenOnDevice, DeviceId, DeviceName, DeviceActions, FileName, FileDirectory, SHA256, Sha256Algorithm, SanitizedSHA256, InitiatingProcessFileName, DeviceAccountName, DeviceAccountUPNSuffix, FilewallTime, FilewallFileName, FilewallUser, FilewallSource, Sender, EventReportUrl\n| order by FirstSeenOnDevice desc\n",
+ "version": 2,
+ "tags": [
+ {
+ "name": "description",
+ "value": "Finds devices holding the original, unsanitized copy of a file that Filewall for Microsoft 365 sanitized, in inbound email or in SharePoint, OneDrive and Teams.\nFilewall removed active content from these files before delivering them, so a device with the original hash got the file another way: a direct download, a removable drive, another mail flow or a copy released from Filewall.\nSanitizing is a policy action, not a malware verdict, so this is a hunting query rather than an alert. The sanitized hash is shown next to the original, and the report link opens the item in the Filewall console. Requires the Microsoft Defender XDR connector to stream DeviceFileEvents into the workspace."
+ },
+ {
+ "name": "tactics",
+ "value": "Execution"
+ },
+ {
+ "name": "techniques",
+ "value": "T1204.002"
+ }
+ ]
+ }
+ },
+ {
+ "type": "Microsoft.OperationalInsights/workspaces/providers/metadata",
+ "apiVersion": "2022-01-01-preview",
+ "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('HuntingQuery-', last(split(resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1),'/'))))]",
+ "properties": {
+ "description": "Filewall for Microsoft 365 Hunting Query 1",
+ "parentId": "[resourceId('Microsoft.OperationalInsights/savedSearches', variables('huntingQueryObject1')._huntingQuerycontentId1)]",
+ "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]",
+ "kind": "HuntingQuery",
+ "version": "[variables('huntingQueryObject1').huntingQueryVersion1]",
+ "source": {
+ "kind": "Solution",
+ "name": "Filewall for Microsoft 365",
+ "sourceId": "[variables('_solutionId')]"
+ },
+ "author": {
+ "name": "Filewall",
+ "email": "[variables('_email')]"
+ },
+ "support": {
+ "name": "Filewall",
+ "email": "support@filewall.com",
+ "tier": "Partner",
+ "link": "https://www.odi-x.com/contact-filewall-support/"
+ }
+ }
+ }
+ ]
+ },
+ "packageKind": "Solution",
+ "packageVersion": "[variables('_solutionVersion')]",
+ "packageName": "[variables('_solutionName')]",
+ "packageId": "[variables('_solutionId')]",
+ "contentSchemaVersion": "3.0.0",
+ "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]",
+ "contentKind": "HuntingQuery",
+ "displayName": "Filewall - Original of a file Filewall sanitized seen on a device",
+ "contentProductId": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]",
+ "id": "[concat(take(variables('_solutionId'),50),'-','hq','-', uniqueString(concat(variables('_solutionId'),'-','HuntingQuery','-',variables('huntingQueryObject1')._huntingQuerycontentId1,'-', '1.0.0')))]",
+ "version": "1.0.0"
+ },
+ "condition": "[parameters('DeployHuntingQuery')]"
},
{
"type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages",
"apiVersion": "2023-04-01-preview",
"location": "[parameters('workspace-location')]",
"properties": {
- "version": "3.0.0",
+ "version": "3.2.2",
"kind": "Solution",
"contentSchemaVersion": "3.0.0",
"displayName": "Filewall for Microsoft 365",
"publisherDisplayName": "Filewall",
- "descriptionHtml": "Note: Please refer to the following before installing the solution:
\n• Review the solution Release Notes
\n• There may be known issues pertaining to this Solution, please refer to them before installing.
\nFilewall for Microsoft 365 solution installs a Codeless (CCF/CLv2) connector that ingests Filewall activity logs for Exchange, SharePoint, OneDrive and Teams into custom tables. It also installs parsers, analytics rules and a workbook for visibility.
\nData Connectors: 1, Parsers: 2, Workbooks: 1, Analytic Rules: 2
\nLearn more about Microsoft Sentinel | Learn more about Solutions
\n",
+ "descriptionHtml": "Note: Please refer to the following before installing the solution:
\n• Review the solution Release Notes
\n• There may be known issues pertaining to this Solution, please refer to them before installing.
\nFilewall for Microsoft 365 solution installs a Codeless (CCF/CLv2) connector that ingests Filewall activity logs for Exchange, SharePoint, OneDrive and Teams into custom tables. It also installs parsers, a workbook, analytics rules that alert on malicious and suspicious files by verdict, and rules and hunting queries that correlate Filewall file hashes with Microsoft Defender for Endpoint device file events.
\nData Connectors: 1, Parsers: 3, Workbooks: 1, Analytic Rules: 3, Hunting Queries: 1
\nLearn more about Microsoft Sentinel | Learn more about Solutions
\n",
"contentKind": "Solution",
"contentProductId": "[variables('_solutioncontentProductId')]",
"id": "[variables('_solutioncontentProductId')]",
@@ -1987,6 +2665,11 @@
"contentId": "[variables('parserObject2').parserContentId2]",
"version": "[variables('parserObject2').parserVersion2]"
},
+ {
+ "kind": "Parser",
+ "contentId": "[variables('parserObject3').parserContentId3]",
+ "version": "[variables('parserObject3').parserVersion3]"
+ },
{
"kind": "AnalyticsRule",
"contentId": "[variables('analyticRuleObject1')._analyticRulecontentId1]",
@@ -1996,11 +2679,21 @@
"kind": "AnalyticsRule",
"contentId": "[variables('analyticRuleObject2')._analyticRulecontentId2]",
"version": "[variables('analyticRuleObject2').analyticRuleVersion2]"
+ },
+ {
+ "kind": "AnalyticsRule",
+ "contentId": "[variables('analyticRuleObject3')._analyticRulecontentId3]",
+ "version": "[variables('analyticRuleObject3').analyticRuleVersion3]"
+ },
+ {
+ "kind": "HuntingQuery",
+ "contentId": "[variables('huntingQueryObject1')._huntingQuerycontentId1]",
+ "version": "[variables('huntingQueryObject1').huntingQueryVersion1]"
}
]
},
"firstPublishDate": "2026-01-06",
- "lastPublishDate": "2026-01-06",
+ "lastPublishDate": "2026-10-26",
"providers": [
"Filewall"
],
@@ -2011,6 +2704,19 @@
}
},
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]"
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2025-04-01",
+ "name": "pid-c8004b1b-c813-4de4-8a40-55327766dd90-partnercenter",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": []
+ }
+ }
}
],
"outputs": {}
diff --git a/Solutions/Filewall for Microsoft 365/Package/testParameters.json b/Solutions/Filewall for Microsoft 365/Package/testParameters.json
index de66003a3bd..43a94b64315 100644
--- a/Solutions/Filewall for Microsoft 365/Package/testParameters.json
+++ b/Solutions/Filewall for Microsoft 365/Package/testParameters.json
@@ -42,5 +42,40 @@
"metadata": {
"description": "Name for the workbook"
}
+ },
+ "DeployAnalyticsRule": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Deploy the Microsoft Sentinel analytic rules carried by this solution (ContentKind AnalyticsRule and its AnalyticsRuleTemplate registration face)."
+ }
+ },
+ "DeployWorkbook": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Deploy the Microsoft Sentinel workbook content and its Content Hub registration carried by this solution (ContentKind Workbook and WorkbookTemplate)."
+ }
+ },
+ "DeployDataConnector": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Deploy the data connectors carried by this solution, together with their metadata and catalog entries (ContentKind DataConnector, ResourceDataConnector and DataType)."
+ }
+ },
+ "DeployHuntingQuery": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Deploy the Microsoft Sentinel hunting queries carried by this solution."
+ }
+ },
+ "DeployParser": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Deploy the parsers and their registrations carried by this solution."
+ }
}
}
diff --git a/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365EmailAttachmentEvent.yaml b/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365EmailAttachmentEvent.yaml
new file mode 100644
index 00000000000..2980529b5cc
--- /dev/null
+++ b/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365EmailAttachmentEvent.yaml
@@ -0,0 +1,33 @@
+id: 51c781c1-ba93-401e-a1c1-e34d4e407e54
+Function:
+ Title: Parser for Exchange email attachment events
+ Version: '1.0.0'
+ LastUpdated: '2026-10-26'
+Category: Microsoft Sentinel Parser
+FunctionName: FilewallM365EmailAttachmentEvent
+FunctionAlias: FilewallM365EmailAttachmentEvent
+FunctionQuery: |
+ // One row per email attachment Filewall processed, with its message's columns and its own
+ // ThreatVerdict and EventSeverity (see FilewallM365ExchangeEvent for the verdicts).
+ // The original and sanitized hashes are kept apart: OriginalFile* is the file the sender sent,
+ // the one to look for elsewhere; SanitizedFile* is the copy Filewall delivered in its place,
+ // empty when nothing was delivered.
+ FilewallM365ExchangeEvent()
+ | mv-expand Attachment = EmailAttachments
+ | extend
+ AttachmentName = tostring(Attachment.name),
+ AttachmentFileType = tostring(Attachment.type),
+ AttachmentSize = tostring(Attachment.size),
+ AttachmentStatus = tostring(Attachment.status),
+ AttachmentBlockReason = tostring(Attachment.block_reason),
+ AttachmentSummary = tostring(Attachment.summary),
+ ThreatVerdict = tostring(Attachment.verdict),
+ EventSeverity = tostring(Attachment.severity),
+ OriginalFileSha256 = tolower(tostring(Attachment.original_sha256)),
+ OriginalFileSha1 = tolower(tostring(Attachment.original_sha1)),
+ SanitizedFileSha256 = tolower(tostring(Attachment.new_sha256)),
+ SanitizedFileSha1 = tolower(tostring(Attachment.new_sha1))
+ | extend
+ EventType = iff(ThreatVerdict in ("Malicious", "Suspicious"), "ThreatInfo", "Informational"),
+ EventResult = case(AttachmentStatus =~ "blocked", "Blocked", AttachmentStatus =~ "failed", "Failure", AttachmentStatus in~ ("succeed", "sanitized"), "Success", "NA")
+ | project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventUid, EventType, EventOriginalType, EventResult, EventSeverity, ThreatVerdict, EventVendor, EventProduct, User, ActorUsername, EmailSubject, EmailSenderFrom, EmailRecipientTo, EmailDirection, EmailInternetMessageId, FilewallMessageId, EventReportUrl, AlertPolicyId, AlertPolicyName, AttachmentName, AttachmentFileType, AttachmentSize, AttachmentStatus, AttachmentBlockReason, AttachmentSummary, OriginalFileSha256, OriginalFileSha1, SanitizedFileSha256, SanitizedFileSha1, application, event_id, timestamp
diff --git a/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365ExchangeEvent.yaml b/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365ExchangeEvent.yaml
index d8a45aac0b3..2cc28a783c4 100644
--- a/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365ExchangeEvent.yaml
+++ b/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365ExchangeEvent.yaml
@@ -1,28 +1,68 @@
id: 378781a9-aef0-446a-9822-a8beb70835e7
Function:
Title: Parser for Exchange events
- Version: '1.0.0'
- LastUpdated: '2026-01-01'
+ Version: '1.1.0'
+ LastUpdated: '2026-10-26'
Category: Microsoft Sentinel Parser
FunctionName: FilewallM365ExchangeEvent
FunctionAlias: FilewallM365ExchangeEvent
FunctionQuery: |
+ // One row per email Filewall processed.
+ // ThreatVerdict separates what a Filewall engine found from what policy did, so severity follows
+ // the verdict and not the block alone:
+ // Malicious - an antivirus engine found malware (High)
+ // Suspicious - static analysis flagged the file, or its content does not match its type (Medium)
+ // ProcessingFailed / NotSanitized - the file was not processed or not sanitized (Low)
+ // Unsanitizable - CDR could not rebuild the file, so it was blocked (Low)
+ // PolicyBlocked - blocked by a file type, size or encryption rule, not a malware verdict (Informational)
+ // Sanitized - CDR rebuilt the file and it was delivered (Informational)
+ // A message takes the most severe verdict of its attachments. EmailAttachments carries each
+ // attachment with its own verdict and severity; FilewallM365EmailAttachmentEvent expands it.
+ // FilewallMessageId is Filewall's id for the message: the Graph item id for mailbox mail, and
+ // "@@" for relay mail. It is not the Exchange Online NetworkMessageId.
FilewallExchange_CL
- | extend
- EventStartTime = TimeGenerated,
+ | extend IngestionTime = ingestion_time()
+ | extend
+ direction = column_ifexists('direction', ''),
+ source_folder = column_ifexists('source_folder', ''),
+ internet_message_id = column_ifexists('internet_message_id', ''),
+ report_url = column_ifexists('report_url', '')
+ | mv-apply Attachment = attachments on (
+ extend AttachmentStatus = tolower(tostring(Attachment.status)), AttachmentReason = tolower(tostring(Attachment.block_reason))
+ | extend Verdict = case(
+ AttachmentReason == "antivirus", "Malicious",
+ AttachmentReason in ("static_analysis", "type_validation"), "Suspicious",
+ AttachmentReason == "cdr_init_failed" or AttachmentStatus == "failed", "ProcessingFailed",
+ AttachmentStatus in ("not_supported", "not_sanitized"), "NotSanitized",
+ AttachmentReason == "cdr", "Unsanitizable",
+ AttachmentReason in ("file_policy", "size_limitation", "encryption_filter"), "PolicyBlocked",
+ AttachmentStatus == "blocked", "Blocked",
+ AttachmentStatus in ("succeed", "sanitized"), "Sanitized",
+ "Unknown")
+ | extend VerdictRank = case(Verdict == "Malicious", 8, Verdict == "Suspicious", 7, Verdict == "ProcessingFailed", 6, Verdict == "NotSanitized", 5, Verdict == "Unsanitizable", 4, Verdict == "Blocked", 3, Verdict == "PolicyBlocked", 2, Verdict == "Sanitized", 1, 0)
+ | extend VerdictSeverity = case(VerdictRank == 8, "High", VerdictRank == 7, "Medium", VerdictRank >= 3, "Low", "Informational")
+ | summarize arg_max(VerdictRank, Verdict, VerdictSeverity), EmailAttachments = make_list(bag_merge(Attachment, bag_pack("verdict", Verdict, "severity", VerdictSeverity)))
+ )
+ | extend
+ EventStartTime = TimeGenerated,
EventEndTime = TimeGenerated,
EventOriginalType = tostring(event_type),
- EventType = case(status =~ "blocked" or status =~ "failed", "ThreatInfo", status =~ "succeed", "Informational", "Informational"),
- EventResult = case(status =~ "blocked", 'Blocked', status =~ "failed", "Failure", status =~ "succeed", "Success", "NA"),
+ ThreatVerdict = coalesce(Verdict, "Unknown"),
+ EventSeverity = coalesce(VerdictSeverity, "Informational")
+ | extend
+ EventType = iff(ThreatVerdict in ("Malicious", "Suspicious"), "ThreatInfo", "Informational"),
+ EventResult = case(status =~ "blocked", "Blocked", status =~ "partial_blocked", "Partial", status =~ "failed", "Failure", status in~ ("succeed", "sanitized"), "Success", "NA"),
EventOriginalResultDetails = coalesce(state, summary),
- EventSeverity = case(status =~ "blocked", "High", status =~ "failed", "Medium", "Informational"),
EventVendor = "ODI-X",
EventProduct = "Filewall for Microsoft 365",
EventUid = tostring(event_id),
EmailSubject = subject,
EmailSenderFrom = sender,
EmailRecipientTo = recipient,
- EmailNetworkMessageId = mail_id,
+ EmailDirection = case(direction =~ "incoming", "Inbound", direction =~ "outgoing", "Outbound", ""),
+ EmailInternetMessageId = internet_message_id,
+ FilewallMessageId = mail_id,
+ EventReportUrl = report_url,
AlertPolicyId = policy_id,
AlertPolicyName = policy_name,
User = recipient,
@@ -35,4 +75,4 @@ FunctionQuery: |
timestamp = todatetime(timestamp),
release_timestamp = todatetime(release_timestamp),
release_by_admin = tobool(release_by_admin)
- | project TimeGenerated, EventStartTime, EventEndTime, EventUid, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, User, ActorUsername, EmailSubject, EmailSenderFrom, EmailRecipientTo, EmailNetworkMessageId, AlertPolicyId, AlertPolicyName, application, event_id, timestamp, status, state, summary, attachments, release_timestamp, release_by_admin, release_reason
\ No newline at end of file
+ | project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventUid, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, ThreatVerdict, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, User, ActorUsername, EmailSubject, EmailSenderFrom, EmailRecipientTo, EmailDirection, EmailInternetMessageId, FilewallMessageId, EventReportUrl, AlertPolicyId, AlertPolicyName, application, event_id, timestamp, status, state, summary, attachments, EmailAttachments, direction, source_folder, release_timestamp, release_by_admin, release_reason
diff --git a/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365FileEvent.yaml b/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365FileEvent.yaml
index 15d8db473b8..e470eaeb240 100644
--- a/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365FileEvent.yaml
+++ b/Solutions/Filewall for Microsoft 365/Parsers/FilewallM365FileEvent.yaml
@@ -1,30 +1,56 @@
id: d890d753-96fe-4439-9103-b75040758924
Function:
Title: Parser for File events
- Version: '1.0.0'
- LastUpdated: '2026-01-01'
+ Version: '1.1.0'
+ LastUpdated: '2026-10-26'
Category: Microsoft Sentinel Parser
FunctionName: FilewallM365FileEvent
FunctionAlias: FilewallM365FileEvent
FunctionQuery: |
+ // One row per SharePoint, OneDrive or Teams file Filewall processed. ThreatVerdict and
+ // EventSeverity follow what a Filewall engine found, not the block alone (see
+ // FilewallM365ExchangeEvent for the verdicts).
+ // The original and sanitized hashes are kept apart: OriginalFile* is the file the user uploaded,
+ // the one to look for elsewhere; SanitizedFile* is the copy Filewall put in its place, empty when
+ // there is none. TargetFileHashSha1/Sha256 are the file in place now: the sanitized copy if
+ // there is one, else the original.
FilewallFile_CL
- | extend
- EventStartTime = TimeGenerated,
+ | extend IngestionTime = ingestion_time()
+ | extend report_url = column_ifexists('report_url', '')
+ | extend FileStatus = tolower(status), FileReason = tolower(block_reason)
+ | extend ThreatVerdict = case(
+ FileReason == "antivirus", "Malicious",
+ FileReason in ("static_analysis", "type_validation"), "Suspicious",
+ FileReason == "cdr_init_failed" or FileStatus == "failed", "ProcessingFailed",
+ FileStatus in ("not_supported", "not_sanitized"), "NotSanitized",
+ FileReason == "cdr", "Unsanitizable",
+ FileReason in ("file_policy", "size_limitation", "encryption_filter"), "PolicyBlocked",
+ FileStatus == "blocked", "Blocked",
+ FileStatus in ("succeed", "sanitized"), "Sanitized",
+ "Unknown")
+ | extend
+ EventStartTime = TimeGenerated,
EventEndTime = TimeGenerated,
EventOriginalType = tostring(event_type),
EventType = case(status =~ "blocked", "FileBlocked", status =~ "succeed", "FileModified", status =~ "failed", "FileError", "FileEvent"),
EventResult = case(status =~ "blocked", 'Blocked', status =~ "failed", "Failure", status =~ "succeed", "Success", "NA"),
EventOriginalResultDetails = coalesce(block_reason, state, summary),
- EventSeverity = case(status =~ "blocked", "High", status =~ "failed", "Medium", "Informational"),
+ EventSeverity = case(ThreatVerdict == "Malicious", "High", ThreatVerdict == "Suspicious", "Medium", ThreatVerdict in ("ProcessingFailed", "NotSanitized", "Unsanitizable", "Blocked"), "Low", "Informational"),
EventVendor = "ODI-X",
EventProduct = "Filewall for Microsoft 365",
TargetFileName = name,
TargetFilePath = path,
+ TargetFileDirectory = iff(isnotempty(name) and path endswith strcat("/", name), substring(path, 0, strlen(path) - strlen(name) - 1), ""),
TargetFilePathType = application,
TargetFileExtension = file_type,
TargetFileSize = tolong(size),
TargetFileHashSha1 = coalesce(new_sha1, original_sha1),
TargetFileHashSha256 = coalesce(new_sha256, original_sha256),
+ OriginalFileSha256 = tolower(original_sha256),
+ OriginalFileSha1 = tolower(original_sha1),
+ SanitizedFileSha256 = tolower(new_sha256),
+ SanitizedFileSha1 = tolower(new_sha1),
+ EventReportUrl = report_url,
ActorUsername = owner,
ActorUserType = "Regular",
User = owner,
@@ -33,4 +59,4 @@ FunctionQuery: |
EventSchema = "FileEvent",
EventSchemaVersion = "0.2.2",
EventCount = int(1)
- | project TimeGenerated, EventStartTime, EventEndTime, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, ActorUsername, ActorUserType, User, TargetFileName, TargetFilePath, TargetFilePathType, TargetFileExtension, TargetFileSize, TargetFileHashSha1, TargetFileHashSha256, application, event_id, timestamp, status, state, policy_id, policy_name, block_reason, summary, drive_name, site_name, new_sha1, original_sha1, new_sha256, original_sha256, version
+ | project TimeGenerated, IngestionTime, EventStartTime, EventEndTime, EventType, EventOriginalType, EventResult, EventOriginalResultDetails, EventSeverity, ThreatVerdict, EventVendor, EventProduct, EventSchema, EventSchemaVersion, EventCount, Dvc, DvcHostname, ActorUsername, ActorUserType, User, TargetFileName, TargetFilePath, TargetFileDirectory, TargetFilePathType, TargetFileExtension, TargetFileSize, TargetFileHashSha1, TargetFileHashSha256, OriginalFileSha256, OriginalFileSha1, SanitizedFileSha256, SanitizedFileSha1, EventReportUrl, application, event_id, timestamp, status, state, policy_id, policy_name, block_reason, summary, drive_name, site_name, new_sha1, original_sha1, new_sha256, original_sha256, version
diff --git a/Solutions/Filewall for Microsoft 365/ReleaseNotes.md b/Solutions/Filewall for Microsoft 365/ReleaseNotes.md
index 8d2597b0644..7d358877554 100644
--- a/Solutions/Filewall for Microsoft 365/ReleaseNotes.md
+++ b/Solutions/Filewall for Microsoft 365/ReleaseNotes.md
@@ -1,3 +1,4 @@
| **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** |
|------------|-------------------------------|--------------------|
+| 3.2.2 | 26-10-2026 | Analytic rules alert by Filewall verdict (malware High, suspicious Medium) instead of on every block, with matching MITRE techniques; policy blocks and CDR sanitization no longer alert. Rules map file hashes, mailbox, message (InternetMessageId) and account entities, link each alert to the Filewall console, and are enabled with incident grouping by file hash. New parser FilewallM365EmailAttachmentEvent (one row per attachment). Parsers keep original and sanitized hashes in separate columns and add ThreatVerdict, EmailDirection, EmailInternetMessageId and EventReportUrl; EmailNetworkMessageId is replaced by FilewallMessageId, since it never held the Exchange NetworkMessageId. New analytic rule and hunting query correlate Filewall file hashes with Defender for Endpoint DeviceFileEvents. FilewallExchange_CL gains direction, source_folder, internet_message_id and report_url columns, and FilewallFile_CL gains report_url. |
| 3.0.0 | 20-01-2026 | Includes all CCF connector definitions and configurations. |
diff --git a/Solutions/Filewall for Microsoft 365/SolutionMetadata.json b/Solutions/Filewall for Microsoft 365/SolutionMetadata.json
index 6e839c4993c..12606a5f338 100644
--- a/Solutions/Filewall for Microsoft 365/SolutionMetadata.json
+++ b/Solutions/Filewall for Microsoft 365/SolutionMetadata.json
@@ -1,8 +1,9 @@
{
+ "trackingId": "pid-c8004b1b-c813-4de4-8a40-55327766dd90-partnercenter",
"publisherId": "odix",
"offerId": "azure-sentinel-solution-filewallm365",
"firstPublishDate": "2026-01-06",
- "lastPublishDate": "2026-01-06",
+ "lastPublishDate": "2026-10-26",
"providers": [
"Filewall"
],