diff --git a/src/Runner.Sdk/Util/WhichUtil.cs b/src/Runner.Sdk/Util/WhichUtil.cs index 1a12a889a..d1bf5dba7 100644 --- a/src/Runner.Sdk/Util/WhichUtil.cs +++ b/src/Runner.Sdk/Util/WhichUtil.cs @@ -60,7 +60,7 @@ namespace GitHub.Runner.Sdk trace?.Verbose(ex.ToString()); } - if (matches != null && matches.Length > 0) + if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace)) { trace?.Info($"Location: '{matches.First()}'"); return matches.First(); @@ -86,7 +86,7 @@ namespace GitHub.Runner.Sdk for (int i = 0; i < pathExtSegments.Length; i++) { string fullPath = Path.Combine(pathSegment, $"{command}{pathExtSegments[i]}"); - if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase))) + if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase)) && IsPathValid(fullPath, trace)) { trace?.Info($"Location: '{fullPath}'"); return fullPath; @@ -105,7 +105,7 @@ namespace GitHub.Runner.Sdk trace?.Verbose(ex.ToString()); } - if (matches != null && matches.Length > 0) + if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace)) { trace?.Info($"Location: '{matches.First()}'"); return matches.First(); @@ -128,5 +128,15 @@ namespace GitHub.Runner.Sdk return null; } + + // checks if the file is a symlink and if the symlink`s target exists. + private static bool IsPathValid(string path, ITraceWriter trace = null) + { + var fileInfo = new FileInfo(path); + var linkTargetFullPath = fileInfo.Directory?.FullName + Path.DirectorySeparatorChar + fileInfo.LinkTarget; + if(fileInfo.LinkTarget == null || File.Exists(linkTargetFullPath) || File.Exists(fileInfo.LinkTarget)) return true; + trace?.Info($"the target '{fileInfo.LinkTarget}' of the symbolic link '{path}', does not exist"); + return false; + } } } diff --git a/src/Test/L0/Util/WhichUtilL0.cs b/src/Test/L0/Util/WhichUtilL0.cs index c6a057144..180ff6ab0 100644 --- a/src/Test/L0/Util/WhichUtilL0.cs +++ b/src/Test/L0/Util/WhichUtilL0.cs @@ -1,4 +1,4 @@ -using GitHub.Runner.Common.Util; +using GitHub.Runner.Common.Util; using GitHub.Runner.Sdk; using System; using System.IO; @@ -89,5 +89,128 @@ namespace GitHub.Runner.Common.Tests.Util Assert.Equal(gitPath, gitPath2); } } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Common")] + public void WhichHandlesSymlinkToTargetFullPath() + { + // Arrange + using TestHostContext hc = new TestHostContext(this); + Tracing trace = hc.GetTrace(); + string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable); +#if OS_WINDOWS + string newValue = oldValue + @$";{Path.GetTempPath()}"; + string symlinkName = $"symlink-{Guid.NewGuid()}"; + string symlink = Path.GetTempPath() + $"{symlinkName}.exe"; + string target = Path.GetTempPath() + $"target-{Guid.NewGuid()}.exe"; +#else + string newValue = oldValue + @$":{Path.GetTempPath()}"; + string symlinkName = $"symlink-{Guid.NewGuid()}"; + string symlink = Path.GetTempPath() + $"{symlinkName}"; + string target = Path.GetTempPath() + $"target-{Guid.NewGuid()}"; +#endif + + Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue); + + + using (File.Create(target)) + { + File.CreateSymbolicLink(symlink, target); + + // Act. + var result = WhichUtil.Which(symlinkName, require: true, trace: trace); + + // Assert + Assert.True(!string.IsNullOrEmpty(result) && File.Exists(result), $"Unable to find symlink through: {nameof(WhichUtil.Which)}"); + + } + + + // Cleanup + File.Delete(symlink); + File.Delete(target); + Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue); + + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Common")] + public void WhichHandlesSymlinkToTargetRelativePath() + { + // Arrange + using TestHostContext hc = new TestHostContext(this); + Tracing trace = hc.GetTrace(); + string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable); +#if OS_WINDOWS + string newValue = oldValue + @$";{Path.GetTempPath()}"; + string symlinkName = $"symlink-{Guid.NewGuid()}"; + string symlink = Path.GetTempPath() + $"{symlinkName}.exe"; + string targetName = $"target-{Guid.NewGuid()}.exe"; + string target = Path.GetTempPath() + targetName; +#else + string newValue = oldValue + @$":{Path.GetTempPath()}"; + string symlinkName = $"symlink-{Guid.NewGuid()}"; + string symlink = Path.GetTempPath() + $"{symlinkName}"; + string targetName = $"target-{Guid.NewGuid()}"; + string target = Path.GetTempPath() + targetName; +#endif + Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue); + + + using (File.Create(target)) + { + File.CreateSymbolicLink(symlink, targetName); + + // Act. + var result = WhichUtil.Which(symlinkName, require: true, trace: trace); + + // Assert + Assert.True(!string.IsNullOrEmpty(result) && File.Exists(result), $"Unable to find {symlinkName} through: {nameof(WhichUtil.Which)}"); + } + + // Cleanup + File.Delete(symlink); + File.Delete(target); + Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue); + + } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Common")] + public void WhichThrowsWhenSymlinkBroken() + { + // Arrange + using TestHostContext hc = new TestHostContext(this); + Tracing trace = hc.GetTrace(); + string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable); + +#if OS_WINDOWS + string newValue = oldValue + @$";{Path.GetTempPath()}"; + string brokenSymlinkName = $"broken-symlink-{Guid.NewGuid()}"; + string brokenSymlink = Path.GetTempPath() + $"{brokenSymlinkName}.exe"; +#else + string newValue = oldValue + @$":{Path.GetTempPath()}"; + string brokenSymlinkName = $"broken-symlink-{Guid.NewGuid()}"; + string brokenSymlink = Path.GetTempPath() + $"{brokenSymlinkName}"; +#endif + + + string target = "no-such-file-cf7e351f"; + Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue); + + File.CreateSymbolicLink(brokenSymlink, target); + + // Act. + var exception = Assert.Throws(()=>WhichUtil.Which(brokenSymlinkName, require: true, trace: trace)); + + // Assert + Assert.Equal(brokenSymlinkName, exception.FileName); + + // Cleanup + File.Delete(brokenSymlink); + Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue); + } } }