Skip to content

Enlighten SignFile for multithreaded mode#13621

Open
jankratochvilcz wants to merge 2 commits intomainfrom
jankratochvilcz/multithreaded/sign-file
Open

Enlighten SignFile for multithreaded mode#13621
jankratochvilcz wants to merge 2 commits intomainfrom
jankratochvilcz/multithreaded/sign-file

Conversation

@jankratochvilcz
Copy link
Copy Markdown
Contributor

Adds [MSBuildMultiThreadableTask] and IMultiThreadableTask to SignFile. Absolutizes SigningTarget.ItemSpec before passing to SecurityUtilities.SignFile().

Fixes #13618
Parent epic: #11834

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>
@jankratochvilcz jankratochvilcz marked this pull request as ready for review April 27, 2026 19:18
Copilot AI review requested due to automatic review settings April 27, 2026 19:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the built-in SignFile task to be compatible with MSBuild’s multithreaded execution model by ensuring file I/O uses project-directory-relative path resolution rather than process-wide Environment.CurrentDirectory.

Changes:

  • Marks SignFile as [MSBuildMultiThreadableTask] and implements IMultiThreadableTask with a TaskEnvironment property.
  • Absolutizes SigningTarget.ItemSpec via TaskEnvironment.GetAbsolutePath(...) before calling SecurityUtilities.SignFile(...).

Comment thread src/Tasks/SignFile.cs Outdated
Comment on lines +57 to +61
AbsolutePath signingTargetPath = TaskEnvironment.GetAbsolutePath(SigningTarget.ItemSpec);
SecurityUtilities.SignFile(
CertificateThumbprint,
TimestampUrl == null ? null : new Uri(TimestampUrl),
SigningTarget.ItemSpec,
signingTargetPath,
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because SigningTarget.ItemSpec is now converted to an absolute path before calling SecurityUtilities.SignFile, any downstream FileNotFoundException.FileName will also be absolute. The task currently logs ex.FileName for MSB3484, which will leak the absolutized path to the user and change existing error text. Consider logging SigningTarget.ItemSpec (or signingTargetPath.OriginalValue) instead of ex.FileName so the message preserves the original input.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — Sin 2. Fixed in the latest commit: hoisted signingTargetPath above the try (Sin 4 also) and the FileNotFoundException catch now logs signingTargetPath.OriginalValue instead of ex.FileName.

Comment thread src/Tasks/SignFile.cs
Comment on lines 58 to 64
SecurityUtilities.SignFile(
CertificateThumbprint,
TimestampUrl == null ? null : new Uri(TimestampUrl),
SigningTarget.ItemSpec,
signingTargetPath,
TargetFrameworkVersion,
TargetFrameworkIdentifier,
DisallowMansignTimestampFallback);
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing an absolute path into SecurityUtilities.SignFile changes the contents of exceptions thrown by SecurityUtilities (e.g., signtool failure/warning messages include the {path} argument). The task logs ex.Message.Trim() verbatim for MSB3482/MSB3483, so this will now surface the absolute path (and may violate the "no absolute path in user-visible output" requirement). Consider rewriting/normalizing the exception message before logging (e.g., replace the absolutized path with the original input) or emitting a task-local error message that uses the original path string.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — also Sin 2 territory. Addressed in the latest commit by sanitizing ex.Message before logging: any embedded absolutized path is replaced with OriginalValue. The replacement is a no-op when the input was already absolute, so no behavior change for existing callers.

…sages

- 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>
Copy link
Copy Markdown
Member

@JanProvaznik JanProvaznik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#13173 I had an attempt here, found it's not so simple and then context switched to do something else. the SecurityUtils library has nested problems with using process global state, which either need to be refactored or analyzed why they're not an issue.

┌────────────────────────────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐
│ Issue                                              │ Location                │ Risk                                          │
├────────────────────────────────────────────────────┼─────────────────────────┼───────────────────────────────────────────────┤
│ Directory.GetCurrentDirectory() fallback in        │ SecurityUtil.cs:875     │ Low — signtool rarely found via CWD           │
│ GetPathToTool                                      │                         │                                               │
├────────────────────────────────────────────────────┼─────────────────────────┼───────────────────────────────────────────────┤
│ SetDllDirectoryW unsynchronized process-global     │ SecurityUtil.cs:728,760 │ Real race — concurrent SignFile tasks could   │
│ mutation                                           │                         │ interfere                                     │
├────────────────────────────────────────────────────┼─────────────────────────┼───────────────────────────────────────────────┤
│ ProcessStartInfo without explicit WorkingDirectory │ SecurityUtil.cs:785     │ Medium — child process inherits wrong CWD in  │
│                                                    │                         │ MT mode                                       │
└────────────────────────────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┘

Comment thread src/Tasks/SignFile.cs
string SanitizeMessage(string msg) => msg?.Replace((string)signingTargetPath, signingTargetPath.OriginalValue) ?? msg;
try
{
SecurityUtilities.SignFile(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the insides of SecurityUtilities.SignFile depend on process global state

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enlighten SignFile task for multithreaded mode

3 participants