diff --git a/Launcher.Next.Core/NextPilot.cs b/Launcher.Next.Core/NextPilot.cs index 5d230e4..c216368 100644 --- a/Launcher.Next.Core/NextPilot.cs +++ b/Launcher.Next.Core/NextPilot.cs @@ -56,7 +56,7 @@ public sealed class PilotRootPolicy(string trustedRoot, string? testRoot = null) { if (string.IsNullOrWhiteSpace(candidate) || !Path.IsPathFullyQualified(candidate) || candidate.StartsWith("\\\\", StringComparison.Ordinal) || candidate.Contains(':', StringComparison.Ordinal) && candidate.IndexOf(':') != 1) return new(false, null, ["PILOT_ROOT_UNTRUSTED"]); var full = Path.GetFullPath(candidate); var permitted = new[] { trustedRoot, testRoot }.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => Path.GetFullPath(x!)); - if (!permitted.Any(x => full.StartsWith(x + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) || full.Contains("Program Files", StringComparison.OrdinalIgnoreCase) || full.Contains("Applications", StringComparison.OrdinalIgnoreCase)) return new(false, null, ["PILOT_ROOT_UNTRUSTED"]); + if (!permitted.Any(x => full.Equals(x, StringComparison.OrdinalIgnoreCase) || full.StartsWith(x + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) || full.Contains("Program Files", StringComparison.OrdinalIgnoreCase) || full.Contains("Applications", StringComparison.OrdinalIgnoreCase)) return new(false, null, ["PILOT_ROOT_UNTRUSTED"]); return new(true, full, []); } } diff --git a/tests/Launcher.Next.Tests/PathAndLaunchTests.cs b/tests/Launcher.Next.Tests/PathAndLaunchTests.cs index 0ffa2fe..28a3221 100644 --- a/tests/Launcher.Next.Tests/PathAndLaunchTests.cs +++ b/tests/Launcher.Next.Tests/PathAndLaunchTests.cs @@ -89,6 +89,7 @@ public sealed class PathAndLaunchTests } [Fact] public void ProductionLaunch_DoesNotExposeSentinels() { var source = File.ReadAllText(Path.Combine(FindRoot(), "Launcher.Next.Autodesk", "AutodeskAdapters.cs")); Assert.DoesNotContain("PHP_REC_AUTOMATION_BEFORE_PXP_1", source[..source.IndexOf("BuildAutomationScript", StringComparison.Ordinal)]); } [Fact] public void CorporateDfsRoot_RemainsConfigurationOnly() => Assert.DoesNotContain("phimproject.com", File.ReadAllText(Path.Combine(FindRoot(), "Launcher.Next.Core", "LauncherPaths.cs")), StringComparison.OrdinalIgnoreCase); + [Fact] public void PilotRootPolicy_AcceptsItsTrustedRoot() => Assert.True(new PilotRootPolicy("C:\\Pilot").Validate("C:\\Pilot").IsValid); [Fact] public void ProductionSource_HasNoForbiddenPathLiterals() {