From 2ddc5bc7afe03c5af8bbb1fd618a17df72334471 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Mon, 27 Apr 2026 18:08:13 +0200 Subject: [PATCH 1/2] Enlighten SignFile for multithreaded mode Add [MSBuildMultiThreadableTask] and implement IMultiThreadableTask. Absolutize SigningTarget.ItemSpec before passing to SecurityUtilities.SignFile(). Fixes #13618 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Tasks/SignFile.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Tasks/SignFile.cs b/src/Tasks/SignFile.cs index e2870e80240..61d3ec49a0c 100644 --- a/src/Tasks/SignFile.cs +++ b/src/Tasks/SignFile.cs @@ -22,8 +22,11 @@ namespace Microsoft.Build.Tasks /// It can sign ClickOnce manifests as well as exe's. /// [SupportedOSPlatform("windows")] - public sealed class SignFile : Task + [MSBuildMultiThreadableTask] + public sealed class SignFile : Task, IMultiThreadableTask { + public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; + public SignFile() : base(AssemblyResources.PrimaryResources, "MSBuild.") { @@ -51,10 +54,11 @@ public override bool Execute() } try { + AbsolutePath signingTargetPath = TaskEnvironment.GetAbsolutePath(SigningTarget.ItemSpec); SecurityUtilities.SignFile( CertificateThumbprint, TimestampUrl == null ? null : new Uri(TimestampUrl), - SigningTarget.ItemSpec, + signingTargetPath, TargetFrameworkVersion, TargetFrameworkIdentifier, DisallowMansignTimestampFallback); From e78f448a365d83f825a5a420bca6e0aa8e6c2610 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Mon, 27 Apr 2026 22:03:52 +0200 Subject: [PATCH 2/2] Address SignFile review feedback: preserve original path in error messages - Hoist signingTargetPath declaration above try so it's in scope of all catch blocks (Sin 4). - MSB3484 (FileNotFoundException): log signingTargetPath.OriginalValue instead of ex.FileName, which would otherwise leak the absolutized path. - MSB3482/MSB3483: sanitize ex.Message from SecurityUtilities by replacing the absolutized path with OriginalValue, so signtool error text continues to display the user's original input (Sin 2). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Tasks/SignFile.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Tasks/SignFile.cs b/src/Tasks/SignFile.cs index 61d3ec49a0c..f38c03c9572 100644 --- a/src/Tasks/SignFile.cs +++ b/src/Tasks/SignFile.cs @@ -52,9 +52,10 @@ public override bool Execute() Log.LogErrorWithCodeFromResources("General.TaskRequiresWindows", nameof(SignFile)); return false; } + AbsolutePath signingTargetPath = TaskEnvironment.GetAbsolutePath(SigningTarget.ItemSpec); + string SanitizeMessage(string msg) => msg?.Replace((string)signingTargetPath, signingTargetPath.OriginalValue) ?? msg; try { - AbsolutePath signingTargetPath = TaskEnvironment.GetAbsolutePath(SigningTarget.ItemSpec); SecurityUtilities.SignFile( CertificateThumbprint, TimestampUrl == null ? null : new Uri(TimestampUrl), @@ -69,29 +70,29 @@ public override bool Execute() Log.LogErrorWithCodeFromResources("SignFile.CertNotInStore"); return false; } - catch (FileNotFoundException ex) + catch (FileNotFoundException) { - Log.LogErrorWithCodeFromResources("SignFile.TargetFileNotFound", ex.FileName); + Log.LogErrorWithCodeFromResources("SignFile.TargetFileNotFound", signingTargetPath.OriginalValue); return false; } catch (ApplicationException ex) { - Log.LogErrorWithCodeFromResources("SignFile.SignToolError", ex.Message.Trim()); + Log.LogErrorWithCodeFromResources("SignFile.SignToolError", SanitizeMessage(ex.Message).Trim()); return false; } catch (WarningException ex) { - Log.LogWarningWithCodeFromResources("SignFile.SignToolWarning", ex.Message.Trim()); + Log.LogWarningWithCodeFromResources("SignFile.SignToolWarning", SanitizeMessage(ex.Message).Trim()); return true; } catch (CryptographicException ex) { - Log.LogErrorWithCodeFromResources("SignFile.SignToolError", ex.Message.Trim()); + Log.LogErrorWithCodeFromResources("SignFile.SignToolError", SanitizeMessage(ex.Message).Trim()); return false; } catch (Win32Exception ex) { - Log.LogErrorWithCodeFromResources("SignFile.SignToolError", ex.Message.Trim()); + Log.LogErrorWithCodeFromResources("SignFile.SignToolError", SanitizeMessage(ex.Message).Trim()); return false; } catch (UriFormatException ex)