diff --git a/src/OpenClaw.Connection/WindowsAuthenticodeVerifier.cs b/src/OpenClaw.Connection/WindowsAuthenticodeVerifier.cs
index f2e190d3d..c6223964e 100644
--- a/src/OpenClaw.Connection/WindowsAuthenticodeVerifier.cs
+++ b/src/OpenClaw.Connection/WindowsAuthenticodeVerifier.cs
@@ -1,5 +1,12 @@
using System;
+using System.Diagnostics;
using System.IO;
+using System.Linq;
+using System.Runtime.Versioning;
+using System.Security.AccessControl;
+using System.Security.Principal;
+using System.Text.Json;
+using System.Threading;
using Microsoft.Security.Extensions;
namespace OpenClaw.Connection;
@@ -11,9 +18,83 @@ internal readonly record struct AuthenticodeTrustResult(bool IsTrusted, string?
public static AuthenticodeTrustResult Rejected(string detail) => new(false, detail);
}
+///
+/// Minimal projection of a Windows AppX/MSIX package as reported by
+/// Get-AppxPackage, used to corroborate genuine Microsoft WSL binaries that
+/// ship without a per-file Authenticode/catalog signature (MSIX packages are
+/// trusted at the package level, not per-file).
+///
+internal readonly record struct AppxPackageInfo(
+ string? PackageFamilyName,
+ string? Publisher,
+ string? SignatureKind,
+ string? InstallLocation,
+ string? Version);
+
+internal readonly record struct WslRelayPathInspection(
+ bool IsSecure,
+ string? FileVersion,
+ string? Detail)
+{
+ public static WslRelayPathInspection Secure(string? fileVersion) =>
+ new(true, fileVersion, null);
+
+ public static WslRelayPathInspection Rejected(string detail) =>
+ new(false, null, detail);
+}
+
internal static class WindowsAuthenticodeVerifier
{
- public static AuthenticodeTrustResult VerifyMicrosoftSignedFile(string path)
+ // Modern WSL ships as an MSIX package. The trailing suffix is a hash derived
+ // from the publisher's signing certificate/public key, so an impostor package
+ // re-signed with a different certificate would get a different family name.
+ private const string WslPackageFamilyName =
+ "MicrosoftCorporationII.WindowsSubsystemForLinux_8wekyb3d8bbwe";
+ private const string TrustedInstallerSid =
+ "S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464";
+
+ public static AuthenticodeTrustResult VerifyMicrosoftSignedFile(string path) =>
+ VerifyMicrosoftSignedFile(
+ path,
+ VerifyAuthenticodeSignature,
+ LookupWslAppxPackageViaPowerShell,
+ InspectWslRelayPath);
+
+ ///
+ /// Test seam for the primary signature, WSL package, and protected-path
+ /// evidence used by the package fallback.
+ ///
+ internal static AuthenticodeTrustResult VerifyMicrosoftSignedFile(
+ string path,
+ Func verifyAuthenticode,
+ Func lookupWslPackage,
+ Func inspectRelayPath)
+ {
+ AuthenticodeTrustResult primary;
+ try
+ {
+ primary = verifyAuthenticode(path);
+ }
+ catch
+ {
+ return AuthenticodeTrustResult.Rejected(
+ "WSL relay Authenticode verification could not complete.");
+ }
+
+ if (primary.IsTrusted)
+ return primary;
+
+ // MSIX packages (modern WSL) don't carry a per-file Authenticode/catalog
+ // signature, so a failed classic check isn't conclusive for wslrelay.exe.
+ // Only consult the package-level fallback for the WSL binaries this
+ // codebase actually cares about; never widen trust for arbitrary files.
+ if (!string.Equals(Path.GetFileName(path), "wslrelay.exe", StringComparison.OrdinalIgnoreCase))
+ return primary;
+
+ return VerifyWslPackageFallback(path, primary, lookupWslPackage, inspectRelayPath);
+ }
+
+ private static AuthenticodeTrustResult VerifyAuthenticodeSignature(string path)
{
try
{
@@ -45,6 +126,141 @@ public static AuthenticodeTrustResult VerifyMicrosoftSignedFile(string path)
}
}
+ private static AuthenticodeTrustResult VerifyWslPackageFallback(
+ string path,
+ AuthenticodeTrustResult primaryFailure,
+ Func lookupWslPackage,
+ Func inspectRelayPath)
+ {
+ AppxPackageInfo? package;
+ try
+ {
+ package = lookupWslPackage();
+ }
+ catch
+ {
+ package = null;
+ }
+
+ if (package is not { } info)
+ {
+ // No genuine WSL AppX package installed to corroborate; surface the
+ // original Authenticode diagnostic rather than a new, less useful one.
+ return primaryFailure;
+ }
+
+ if (!string.Equals(info.PackageFamilyName, WslPackageFamilyName, StringComparison.Ordinal))
+ {
+ // Doesn't match the well-known family name (a different/impostor
+ // package can't corroborate this binary); surface the original detail.
+ return primaryFailure;
+ }
+
+ if (string.IsNullOrEmpty(info.SignatureKind) ||
+ string.Equals(info.SignatureKind, "None", StringComparison.OrdinalIgnoreCase))
+ {
+ return AuthenticodeTrustResult.Rejected(
+ "WSL package signature verification failed: the installed WSL package is unsigned.");
+ }
+
+ if (info.Publisher is null || !HasMicrosoftPublisherIdentity(info.Publisher))
+ {
+ return AuthenticodeTrustResult.Rejected(
+ "WSL package signature verification failed: the installed WSL package's publisher is not Microsoft Corporation.");
+ }
+
+ if (string.IsNullOrWhiteSpace(info.InstallLocation) ||
+ !Path.IsPathFullyQualified(info.InstallLocation))
+ {
+ return AuthenticodeTrustResult.Rejected(
+ "WSL package provenance verification failed: package location data is unavailable.");
+ }
+
+ string fullPath;
+ string installLocation;
+ try
+ {
+ fullPath = Path.GetFullPath(path);
+ installLocation = Path.GetFullPath(info.InstallLocation);
+ }
+ catch
+ {
+ return AuthenticodeTrustResult.Rejected(
+ "WSL package provenance verification failed: package location data is invalid.");
+ }
+
+ var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
+ var windowsAppsRoot = Path.Combine(programFiles, "WindowsApps");
+ if (IsPathWithin(installLocation, windowsAppsRoot) &&
+ IsPathWithin(fullPath, installLocation))
+ {
+ return ToTrustResult(
+ InspectProtectedRelayPath(fullPath, programFiles, inspectRelayPath));
+ }
+
+ var externalRelayPath = Path.Combine(programFiles, "WSL", "wslrelay.exe");
+ if (!string.Equals(fullPath, externalRelayPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return AuthenticodeTrustResult.Rejected(
+ "WSL package provenance verification failed: the relay is not owned by the installed WSL package.");
+ }
+
+ var inspection = InspectProtectedRelayPath(
+ fullPath,
+ programFiles,
+ inspectRelayPath);
+ if (!inspection.IsSecure)
+ return ToTrustResult(inspection);
+
+ // The external WSL layout stamps wslrelay.exe with the package version.
+ // Exact equality binds the protected external bytes to the installed package.
+ if (!VersionsMatch(info.Version, inspection.FileVersion))
+ {
+ return AuthenticodeTrustResult.Rejected(
+ "WSL package provenance verification failed: the external relay version does not match the installed WSL package.");
+ }
+
+ return AuthenticodeTrustResult.Trusted();
+ }
+
+ private static WslRelayPathInspection InspectProtectedRelayPath(
+ string fullPath,
+ string trustedRoot,
+ Func inspectRelayPath)
+ {
+ try
+ {
+ return inspectRelayPath(fullPath, trustedRoot);
+ }
+ catch
+ {
+ return WslRelayPathInspection.Rejected(
+ "WSL package provenance verification failed: the relay path could not be inspected.");
+ }
+ }
+
+ private static AuthenticodeTrustResult ToTrustResult(WslRelayPathInspection inspection) =>
+ inspection.IsSecure
+ ? AuthenticodeTrustResult.Trusted()
+ : AuthenticodeTrustResult.Rejected(
+ inspection.Detail ??
+ "WSL package provenance verification failed: the relay path is not protected.");
+
+ private static bool VersionsMatch(string? packageVersion, string? fileVersion) =>
+ Version.TryParse(packageVersion, out var parsedPackageVersion) &&
+ Version.TryParse(fileVersion, out var parsedFileVersion) &&
+ parsedPackageVersion.Equals(parsedFileVersion);
+
+ private static bool IsPathWithin(string path, string root)
+ {
+ var relative = Path.GetRelativePath(root, path);
+ return !Path.IsPathRooted(relative) &&
+ !string.Equals(relative, "..", StringComparison.Ordinal) &&
+ !relative.StartsWith(
+ $"..{Path.DirectorySeparatorChar}",
+ StringComparison.Ordinal);
+ }
+
internal static bool HasMicrosoftPublisherIdentity(string subject) =>
subject.Split(',')
.Select(part => part.Trim())
@@ -53,4 +269,249 @@ internal static bool HasMicrosoftPublisherIdentity(string subject) =>
part,
"O=Microsoft Corporation",
StringComparison.OrdinalIgnoreCase));
+
+ private static AppxPackageInfo? LookupWslAppxPackageViaPowerShell()
+ {
+ Process? process = null;
+ try
+ {
+ var psi = new ProcessStartInfo
+ {
+ FileName = ResolvePowerShellPath(),
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ };
+ psi.ArgumentList.Add("-NoProfile");
+ psi.ArgumentList.Add("-NonInteractive");
+ psi.ArgumentList.Add("-Command");
+ psi.ArgumentList.Add(
+ "Get-AppxPackage -Name 'MicrosoftCorporationII.WindowsSubsystemForLinux' | " +
+ "Sort-Object Version -Descending | " +
+ "Select-Object -First 1 PackageFamilyName, Publisher, InstallLocation, " +
+ "@{Name='SignatureKind'; Expression={$_.SignatureKind.ToString()}}, " +
+ "@{Name='Version'; Expression={$_.Version.ToString()}} | " +
+ "ConvertTo-Json -Compress");
+
+ process = Process.Start(psi);
+ if (process is null)
+ return null;
+
+ using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(8));
+ var stdoutTask = process.StandardOutput.ReadToEndAsync(timeout.Token);
+ var stderrTask = process.StandardError.ReadToEndAsync(timeout.Token);
+
+ if (!process.WaitForExit(8_000))
+ {
+ try { process.Kill(entireProcessTree: true); } catch { }
+ return null;
+ }
+
+ string output;
+ try
+ {
+ output = stdoutTask.GetAwaiter().GetResult();
+ _ = stderrTask.GetAwaiter().GetResult();
+ }
+ catch
+ {
+ return null;
+ }
+
+ if (process.ExitCode != 0 || string.IsNullOrWhiteSpace(output))
+ return null;
+
+ return ParseAppxPackageJson(output);
+ }
+ catch
+ {
+ return null;
+ }
+ finally
+ {
+ process?.Dispose();
+ }
+ }
+
+ private static string ResolvePowerShellPath()
+ {
+ var systemRoot = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
+ if (string.IsNullOrWhiteSpace(systemRoot))
+ systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
+ if (string.IsNullOrWhiteSpace(systemRoot))
+ systemRoot = @"C:\Windows";
+ return Path.Combine(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
+ }
+
+ private static AppxPackageInfo? ParseAppxPackageJson(string json)
+ {
+ try
+ {
+ using var document = JsonDocument.Parse(json);
+ var root = document.RootElement;
+ if (root.ValueKind == JsonValueKind.Array)
+ {
+ if (root.GetArrayLength() == 0)
+ return null;
+ root = root[0];
+ }
+ if (root.ValueKind != JsonValueKind.Object)
+ return null;
+
+ var familyName = GetStringProperty(root, "PackageFamilyName");
+ if (familyName is null)
+ return null;
+
+ return new AppxPackageInfo(
+ familyName,
+ GetStringProperty(root, "Publisher"),
+ GetStringProperty(root, "SignatureKind"),
+ GetStringProperty(root, "InstallLocation"),
+ GetStringProperty(root, "Version"));
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static string? GetStringProperty(JsonElement element, string propertyName) =>
+ element.TryGetProperty(propertyName, out var value) && value.ValueKind == JsonValueKind.String
+ ? value.GetString()
+ : null;
+
+ private static WslRelayPathInspection InspectWslRelayPath(
+ string path,
+ string trustedRoot)
+ {
+ if (!OperatingSystem.IsWindows())
+ {
+ return WslRelayPathInspection.Rejected(
+ "WSL package provenance verification is only available on Windows.");
+ }
+
+ try
+ {
+ var fullPath = Path.GetFullPath(path);
+ var fullRoot = Path.GetFullPath(trustedRoot);
+ if (!File.Exists(fullPath) || !IsPathWithin(fullPath, fullRoot))
+ {
+ return WslRelayPathInspection.Rejected(
+ "WSL package provenance verification failed: the relay path is unavailable or outside its trusted root.");
+ }
+
+ for (var current = fullPath; ; current = Path.GetDirectoryName(current)!)
+ {
+ if ((File.GetAttributes(current) & FileAttributes.ReparsePoint) != 0)
+ {
+ return WslRelayPathInspection.Rejected(
+ "WSL package provenance verification failed: the relay path contains a reparse point.");
+ }
+
+ FileSystemSecurity security =
+ string.Equals(current, fullPath, StringComparison.OrdinalIgnoreCase)
+ ? new FileInfo(current).GetAccessControl(
+ AccessControlSections.Owner | AccessControlSections.Access)
+ : new DirectoryInfo(current).GetAccessControl(
+ AccessControlSections.Owner | AccessControlSections.Access);
+ if (!HasProtectedOwnershipAndWriteAcl(security))
+ {
+ return WslRelayPathInspection.Rejected(
+ "WSL package provenance verification failed: the relay path permits untrusted modification.");
+ }
+
+ if (string.Equals(current, fullRoot, StringComparison.OrdinalIgnoreCase))
+ break;
+
+ if (string.IsNullOrWhiteSpace(Path.GetDirectoryName(current)))
+ {
+ return WslRelayPathInspection.Rejected(
+ "WSL package provenance verification failed: the relay path escaped its trusted root.");
+ }
+ }
+
+ return WslRelayPathInspection.Secure(
+ FileVersionInfo.GetVersionInfo(fullPath).FileVersion);
+ }
+ catch
+ {
+ return WslRelayPathInspection.Rejected(
+ "WSL package provenance verification failed: the relay path could not be inspected.");
+ }
+ }
+
+ [SupportedOSPlatform("windows")]
+ private static bool HasProtectedOwnershipAndWriteAcl(FileSystemSecurity security)
+ {
+ var owner = security.GetOwner(typeof(SecurityIdentifier)) as SecurityIdentifier;
+ var rules = security.GetAccessRules(
+ includeExplicit: true,
+ includeInherited: true,
+ targetType: typeof(SecurityIdentifier))
+ .OfType();
+ var descriptor = new RawSecurityDescriptor(
+ security.GetSecurityDescriptorBinaryForm(),
+ offset: 0);
+ var hasDiscretionaryAcl =
+ (descriptor.ControlFlags & ControlFlags.DiscretionaryAclPresent) != 0 &&
+ descriptor.DiscretionaryAcl is not null;
+ return HasProtectedOwnershipAndWriteAcl(
+ owner,
+ rules,
+ hasDiscretionaryAcl);
+ }
+
+ [SupportedOSPlatform("windows")]
+ internal static bool HasProtectedOwnershipAndWriteAcl(
+ SecurityIdentifier? owner,
+ IEnumerable rules,
+ bool hasDiscretionaryAcl)
+ {
+ if (!hasDiscretionaryAcl)
+ return false;
+
+ var system = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
+ var administrators = new SecurityIdentifier(
+ WellKnownSidType.BuiltinAdministratorsSid,
+ null);
+ var trustedInstaller = new SecurityIdentifier(TrustedInstallerSid);
+ if (owner is null ||
+ (!owner.Equals(system) &&
+ !owner.Equals(administrators) &&
+ !owner.Equals(trustedInstaller)))
+ {
+ return false;
+ }
+
+ const FileSystemRights writeRights =
+ FileSystemRights.WriteData |
+ FileSystemRights.AppendData |
+ FileSystemRights.WriteExtendedAttributes |
+ FileSystemRights.WriteAttributes |
+ FileSystemRights.DeleteSubdirectoriesAndFiles |
+ FileSystemRights.Delete |
+ FileSystemRights.ChangePermissions |
+ FileSystemRights.TakeOwnership;
+
+ foreach (var rule in rules)
+ {
+ if ((rule.PropagationFlags & PropagationFlags.InheritOnly) != 0 ||
+ rule.AccessControlType != AccessControlType.Allow ||
+ (rule.FileSystemRights & writeRights) == 0)
+ {
+ continue;
+ }
+
+ if (rule.IdentityReference is not SecurityIdentifier sid ||
+ (!sid.Equals(system) &&
+ !sid.Equals(administrators) &&
+ !sid.Equals(trustedInstaller)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/tests/OpenClaw.Connection.Tests/ManagedLocalGatewayPortProvenanceServiceTests.cs b/tests/OpenClaw.Connection.Tests/ManagedLocalGatewayPortProvenanceServiceTests.cs
index b5856a970..da0b5e7b2 100644
--- a/tests/OpenClaw.Connection.Tests/ManagedLocalGatewayPortProvenanceServiceTests.cs
+++ b/tests/OpenClaw.Connection.Tests/ManagedLocalGatewayPortProvenanceServiceTests.cs
@@ -1,4 +1,7 @@
using System.Net;
+using System.Runtime.Versioning;
+using System.Security.AccessControl;
+using System.Security.Principal;
using OpenClaw.Shared;
using Xunit;
@@ -53,6 +56,269 @@ public void VerifyMicrosoftSignedFile_RejectsUnsignedAssembly()
Assert.Contains("Authenticode verification failed", result.Detail);
}
+ private const string WslPackageFamilyName =
+ "MicrosoftCorporationII.WindowsSubsystemForLinux_8wekyb3d8bbwe";
+ private const string WslPackageInstallLocation =
+ @"C:\Program Files\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForLinux_2.9.4.0_x64__8wekyb3d8bbwe";
+ private const string ExternalWslRelayPath = @"C:\Program Files\WSL\wslrelay.exe";
+ private const string PackageVersion = "2.9.4.0";
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_AuthenticodeFailsButProtectedExternalRelayMatchesPackage_IsTrusted()
+ {
+ var result = VerifyPackageFallback(ExternalWslRelayPath, ValidWslPackage());
+
+ Assert.True(result.IsTrusted, result.Detail);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_AuthenticodeFailsButPackageOwnsWindowsAppsRelay_IsTrusted()
+ {
+ var packageRelayPath = Path.Combine(WslPackageInstallLocation, "wslrelay.exe");
+
+ var result = VerifyPackageFallback(
+ packageRelayPath,
+ ValidWslPackage(),
+ WslRelayPathInspection.Secure("different-file-version"));
+
+ Assert.True(result.IsTrusted, result.Detail);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_SiblingWindowsAppsRelay_IsRejectedWithoutPathInspection()
+ {
+ var inspected = false;
+ var siblingRelayPath = Path.Combine(
+ @"C:\Program Files\WindowsApps\SomeOther.Package_1.0.0.0_x64__deadbeefcafe",
+ "wslrelay.exe");
+
+ var result = WindowsAuthenticodeVerifier.VerifyMicrosoftSignedFile(
+ siblingRelayPath,
+ _ => AuthenticodeTrustResult.Rejected("Injected primary failure."),
+ () => ValidWslPackage(),
+ (_, _) =>
+ {
+ inspected = true;
+ return WslRelayPathInspection.Secure(PackageVersion);
+ });
+
+ Assert.False(result.IsTrusted);
+ Assert.False(inspected);
+ Assert.Contains("not owned", result.Detail, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_ExternalRelayVersionMismatch_IsRejected()
+ {
+ var result = VerifyPackageFallback(
+ ExternalWslRelayPath,
+ ValidWslPackage(),
+ WslRelayPathInspection.Secure("2.9.3.0"));
+
+ Assert.False(result.IsTrusted);
+ Assert.Contains("version does not match", result.Detail, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_ExternalRelayWithUnprotectedPath_IsRejected()
+ {
+ var result = VerifyPackageFallback(
+ ExternalWslRelayPath,
+ ValidWslPackage(),
+ WslRelayPathInspection.Rejected("Injected unprotected path."));
+
+ Assert.False(result.IsTrusted);
+ Assert.Contains("unprotected", result.Detail, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_RelativePackageLocation_IsRejectedWithoutPathInspection()
+ {
+ var inspected = false;
+ var package = ValidWslPackage() with { InstallLocation = @"relative\package" };
+
+ var result = WindowsAuthenticodeVerifier.VerifyMicrosoftSignedFile(
+ ExternalWslRelayPath,
+ _ => AuthenticodeTrustResult.Rejected("Injected primary failure."),
+ () => package,
+ (_, _) =>
+ {
+ inspected = true;
+ return WslRelayPathInspection.Secure(PackageVersion);
+ });
+
+ Assert.False(result.IsTrusted);
+ Assert.False(inspected);
+ Assert.Contains("location data is unavailable", result.Detail, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_AuthenticodeFailsAndNoWslPackageFound_IsRejectedWithOriginalDetail()
+ {
+ var result = VerifyPackageFallback(ExternalWslRelayPath, null);
+
+ Assert.False(result.IsTrusted);
+ Assert.Contains("Injected primary failure", result.Detail, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_AuthenticodeFailsAndWslPackageFamilyNameMismatches_IsRejected()
+ {
+ var result = VerifyPackageFallback(
+ ExternalWslRelayPath,
+ new AppxPackageInfo(
+ "SomeImpostor.WindowsSubsystemForLinux_deadbeefcafe",
+ "CN=Microsoft Windows, O=Microsoft Corporation, C=US",
+ "Developer",
+ WslPackageInstallLocation,
+ PackageVersion));
+
+ Assert.False(result.IsTrusted);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_AuthenticodeFailsAndWslPackageUnsigned_IsRejected()
+ {
+ var result = VerifyPackageFallback(
+ ExternalWslRelayPath,
+ new AppxPackageInfo(
+ WslPackageFamilyName,
+ "CN=Microsoft Windows, O=Microsoft Corporation, C=US",
+ "None",
+ WslPackageInstallLocation,
+ PackageVersion));
+
+ Assert.False(result.IsTrusted);
+ Assert.Contains("WSL package", result.Detail, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_AuthenticodeFailsAndWslPackagePublisherNotMicrosoft_IsRejected()
+ {
+ var result = VerifyPackageFallback(
+ ExternalWslRelayPath,
+ new AppxPackageInfo(
+ WslPackageFamilyName,
+ "CN=Evil Corp, O=Evil Corp, C=US",
+ "Developer",
+ WslPackageInstallLocation,
+ PackageVersion));
+
+ Assert.False(result.IsTrusted);
+ Assert.Contains("WSL package", result.Detail, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_AuthenticodeSucceeds_NeverConsultsWslPackageFallback()
+ {
+ var fallbackInvoked = false;
+
+ var result = WindowsAuthenticodeVerifier.VerifyMicrosoftSignedFile(
+ ExternalWslRelayPath,
+ _ => AuthenticodeTrustResult.Trusted(),
+ () =>
+ {
+ fallbackInvoked = true;
+ throw new InvalidOperationException("Fallback should not be consulted.");
+ },
+ (_, _) => throw new InvalidOperationException("Path should not be inspected."));
+
+ Assert.True(result.IsTrusted, result.Detail);
+ Assert.False(fallbackInvoked);
+ }
+
+ [Fact]
+ public void VerifyMicrosoftSignedFile_NonRelayFailure_NeverConsultsWslPackageFallback()
+ {
+ var fallbackInvoked = false;
+
+ var result = WindowsAuthenticodeVerifier.VerifyMicrosoftSignedFile(
+ @"C:\Program Files\WSL\wsl.exe",
+ _ => AuthenticodeTrustResult.Rejected("Injected primary failure."),
+ () =>
+ {
+ fallbackInvoked = true;
+ return ValidWslPackage();
+ },
+ (_, _) => throw new InvalidOperationException("Path should not be inspected."));
+
+ Assert.False(result.IsTrusted);
+ Assert.Contains("Injected primary failure", result.Detail, StringComparison.Ordinal);
+ Assert.False(fallbackInvoked);
+ }
+
+ private static AppxPackageInfo ValidWslPackage() => new(
+ WslPackageFamilyName,
+ "CN=Microsoft Windows, O=Microsoft Corporation, C=US",
+ "Developer",
+ WslPackageInstallLocation,
+ PackageVersion);
+
+ private static AuthenticodeTrustResult VerifyPackageFallback(
+ string path,
+ AppxPackageInfo? package,
+ WslRelayPathInspection? pathInspection = null) =>
+ WindowsAuthenticodeVerifier.VerifyMicrosoftSignedFile(
+ path,
+ _ => AuthenticodeTrustResult.Rejected("Injected primary failure."),
+ () => package,
+ (_, _) => pathInspection ?? WslRelayPathInspection.Secure(PackageVersion));
+
+ [Fact]
+ [SupportedOSPlatform("windows")]
+ public void HasProtectedOwnershipAndWriteAcl_AllowsReadOnlyUsersButRejectsUserWrite()
+ {
+ var system = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
+ var administrators = new SecurityIdentifier(
+ WellKnownSidType.BuiltinAdministratorsSid,
+ null);
+ var users = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
+ FileSystemAccessRule[] protectedRules =
+ [
+ new(system, FileSystemRights.FullControl, AccessControlType.Allow),
+ new(administrators, FileSystemRights.Modify, AccessControlType.Allow),
+ new(users, FileSystemRights.ReadAndExecute, AccessControlType.Allow),
+ new(
+ new SecurityIdentifier(WellKnownSidType.CreatorOwnerSid, null),
+ FileSystemRights.FullControl,
+ InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
+ PropagationFlags.InheritOnly,
+ AccessControlType.Allow),
+ ];
+
+ Assert.True(
+ WindowsAuthenticodeVerifier.HasProtectedOwnershipAndWriteAcl(
+ system,
+ protectedRules,
+ hasDiscretionaryAcl: true));
+
+ var writableByUsers = protectedRules.Append(
+ new FileSystemAccessRule(
+ users,
+ FileSystemRights.WriteData,
+ AccessControlType.Allow));
+ Assert.False(
+ WindowsAuthenticodeVerifier.HasProtectedOwnershipAndWriteAcl(
+ system,
+ writableByUsers,
+ hasDiscretionaryAcl: true));
+ Assert.False(
+ WindowsAuthenticodeVerifier.HasProtectedOwnershipAndWriteAcl(
+ users,
+ protectedRules,
+ hasDiscretionaryAcl: true));
+ Assert.False(
+ WindowsAuthenticodeVerifier.HasProtectedOwnershipAndWriteAcl(
+ system,
+ [],
+ hasDiscretionaryAcl: false));
+ Assert.True(
+ WindowsAuthenticodeVerifier.HasProtectedOwnershipAndWriteAcl(
+ system,
+ [],
+ hasDiscretionaryAcl: true));
+ }
+
[Theory]
[InlineData("CN=Microsoft Windows, O=Microsoft Corporation, C=US", true)]
[InlineData("CN=Microsoft Corporation Test Certificate, O=Example Corp, C=US", false)]