From 89a7a10e91b0f85146d5ea5a8f7e83911c86451d Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 30 Apr 2026 18:44:05 -0700 Subject: [PATCH 1/2] Suppress SM05220 in `src/DurableTask.AzureStorage/MessageManager.cs` Resolving https://msazure.visualstudio.com/Antares/_workitems/edit/37181656 Co-authored-by: Copilot --- src/DurableTask.AzureStorage/MessageManager.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/DurableTask.AzureStorage/MessageManager.cs b/src/DurableTask.AzureStorage/MessageManager.cs index 48f46ffac..31b2dacc5 100644 --- a/src/DurableTask.AzureStorage/MessageManager.cs +++ b/src/DurableTask.AzureStorage/MessageManager.cs @@ -14,6 +14,7 @@ namespace DurableTask.AzureStorage { using System; + using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; using System.Linq; @@ -51,6 +52,14 @@ class MessageManager bool containerInitialized; + [SuppressMessage( + "Security", + "CA2326:Do not use TypeNameHandling values other than None", + Justification = "Required to round-trip polymorphic HistoryEvent payloads through customer-owned Azure Storage. See inline CodeQL suppression comment below.")] + [SuppressMessage( + "Security", + "CA2327:Do not use insecure deserializer settings", + Justification = "Required to round-trip polymorphic HistoryEvent payloads through customer-owned Azure Storage. See inline CodeQL suppression comment below.")] public MessageManager( AzureStorageOrchestrationServiceSettings settings, AzureStorageClient azureStorageClient, @@ -59,6 +68,14 @@ public MessageManager( this.settings = settings; this.azureStorageClient = azureStorageClient; this.blobContainer = this.azureStorageClient.GetBlobContainerReference(blobContainerName); + // CodeQL [SM05220] TypeNameHandling.Objects with TypeNameSerializationBinder is required to round-trip + // polymorphic HistoryEvent payloads (and dictionary types like ExecutionStartedEvent.Tags) through + // customer-owned Azure Storage queues/blobs. The DTFx worker and the Storage account sit on the same + // side of the trust boundary: both are authenticated with the customer's tenant credentials, so any + // attacker capable of writing a malicious $type into the queue/blob has already breached the data-plane + // auth boundary that protects the Storage account. The public ICustomTypeBinder extensibility point + // (CustomMessageTypeBinder) lets security-sensitive customers plug in their own allowlist; tightening + // the default binder to a hard-coded allowlist would be a breaking change for the DTFx public API. this.taskMessageSerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects, From a962037ced7aa94d450d09366a7a6baefac9d02d Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Fri, 1 May 2026 11:52:33 -0700 Subject: [PATCH 2/2] Fix suppression syntax Co-authored-by: Copilot --- .../MessageManager.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/DurableTask.AzureStorage/MessageManager.cs b/src/DurableTask.AzureStorage/MessageManager.cs index 31b2dacc5..1fc6d0785 100644 --- a/src/DurableTask.AzureStorage/MessageManager.cs +++ b/src/DurableTask.AzureStorage/MessageManager.cs @@ -14,7 +14,6 @@ namespace DurableTask.AzureStorage { using System; - using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; using System.Linq; @@ -52,14 +51,6 @@ class MessageManager bool containerInitialized; - [SuppressMessage( - "Security", - "CA2326:Do not use TypeNameHandling values other than None", - Justification = "Required to round-trip polymorphic HistoryEvent payloads through customer-owned Azure Storage. See inline CodeQL suppression comment below.")] - [SuppressMessage( - "Security", - "CA2327:Do not use insecure deserializer settings", - Justification = "Required to round-trip polymorphic HistoryEvent payloads through customer-owned Azure Storage. See inline CodeQL suppression comment below.")] public MessageManager( AzureStorageOrchestrationServiceSettings settings, AzureStorageClient azureStorageClient, @@ -68,14 +59,6 @@ public MessageManager( this.settings = settings; this.azureStorageClient = azureStorageClient; this.blobContainer = this.azureStorageClient.GetBlobContainerReference(blobContainerName); - // CodeQL [SM05220] TypeNameHandling.Objects with TypeNameSerializationBinder is required to round-trip - // polymorphic HistoryEvent payloads (and dictionary types like ExecutionStartedEvent.Tags) through - // customer-owned Azure Storage queues/blobs. The DTFx worker and the Storage account sit on the same - // side of the trust boundary: both are authenticated with the customer's tenant credentials, so any - // attacker capable of writing a malicious $type into the queue/blob has already breached the data-plane - // auth boundary that protects the Storage account. The public ICustomTypeBinder extensibility point - // (CustomMessageTypeBinder) lets security-sensitive customers plug in their own allowlist; tightening - // the default binder to a hard-coded allowlist would be a breaking change for the DTFx public API. this.taskMessageSerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects, @@ -389,6 +372,7 @@ public void BindToName(Type serializedType, out string assemblyName, out string TypeNameSerializationHelper.BindToName(customBinder, serializedType, out assemblyName, out typeName); } + // CodeQL [SM05220] False positive: customer-owned Storage is inside the DTFx trust boundary. public Type BindToType(string assemblyName, string typeName) { return TypeNameSerializationHelper.BindToType(customBinder, assemblyName, typeName); @@ -408,6 +392,7 @@ public override void BindToName(Type serializedType, out string assemblyName, ou TypeNameSerializationHelper.BindToName(customBinder, serializedType, out assemblyName, out typeName); } + // CodeQL [SM05220] False positive: customer-owned Storage is inside the DTFx trust boundary. public override Type BindToType(string assemblyName, string typeName) { return TypeNameSerializationHelper.BindToType(customBinder, assemblyName, typeName);