From 942ed8a67da35805b7dd1cdc782dc14d3b27586e Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Tue, 28 Apr 2026 16:30:47 +0200 Subject: [PATCH 1/5] Restrict artifact temp directory permissions on Unix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ArtifactProcessingManager.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs index 769ac39b15..619daf5b9c 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -104,6 +105,9 @@ public void CollectArtifacts(TestRunCompleteEventArgs testRunCompleteEventArgs, EqtTrace.Verbose($"ArtifactProcessingManager.CollectArtifacts: Saving data collectors artifacts for post process into {_processArtifactFolder}"); Stopwatch watch = Stopwatch.StartNew(); TPDebug.Assert(_testSessionProcessArtifactFolder is not null, "_testSessionProcessArtifactFolder is null"); + TPDebug.Assert(_processArtifactFolder is not null, "_processArtifactFolder is null"); + + CreateDirectoryWithUserOnlyAccess(_processArtifactFolder); _fileHelper.CreateDirectory(_testSessionProcessArtifactFolder); EqtTrace.Verbose($"ArtifactProcessingManager.CollectArtifacts: Persist runsettings \n{runSettingsXml}"); _fileHelper.WriteAllTextToFile(Path.Combine(_testSessionProcessArtifactFolder, RunsettingsFileName), runSettingsXml); @@ -246,4 +250,42 @@ private TestArtifacts[] LoadTestArtifacts() } private static bool IsTelemetryOptedIn() => Environment.GetEnvironmentVariable("VSTEST_TELEMETRY_OPTEDIN")?.Equals("1", StringComparison.Ordinal) == true; + + /// + /// Creates a directory with permissions restricted to the current user on Unix. + /// + internal /* for testing */ static void CreateDirectoryWithUserOnlyAccess(string path) + { +#if NETFRAMEWORK + Directory.CreateDirectory(path); +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Directory.CreateDirectory(path); + } + else + { + Directory.CreateDirectory(path); + SetUnixDirectoryPermissions(path); + } +#endif + } + +#if !NETFRAMEWORK + private static void SetUnixDirectoryPermissions(string path) + { + // 0700 octal = owner read/write/execute only + const int ownerFullAccess = 0x1C0; + + int result = NativeChmod(path, ownerFullAccess); + if (result != 0) + { + int error = Marshal.GetLastWin32Error(); + EqtTrace.Warning($"ArtifactProcessingManager: Failed to set permissions on '{path}', errno: {error}"); + } + } + + [DllImport("libc", EntryPoint = "chmod", SetLastError = true)] + private static extern int NativeChmod(string pathname, int mode); +#endif } From 18f46c7a62f5749737571741222c6962935686fa Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Wed, 29 Apr 2026 09:41:15 +0200 Subject: [PATCH 2/5] Throw on chmod failure instead of warning Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../PostProcessing/ArtifactProcessingManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs index 619daf5b9c..af26f4ca2b 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs @@ -281,7 +281,7 @@ private static void SetUnixDirectoryPermissions(string path) if (result != 0) { int error = Marshal.GetLastWin32Error(); - EqtTrace.Warning($"ArtifactProcessingManager: Failed to set permissions on '{path}', errno: {error}"); + throw new InvalidOperationException($"Failed to set permissions on '{path}', errno: {error}"); } } From 54745fd39c14b3825efb5b1e0e271a68ebdaf476 Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Wed, 29 Apr 2026 11:50:00 +0200 Subject: [PATCH 3/5] Use _fileHelper.CreateDirectory instead of direct Directory.CreateDirectory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../PostProcessing/ArtifactProcessingManager.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs index af26f4ca2b..1450ef42ae 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs @@ -254,18 +254,12 @@ private TestArtifacts[] LoadTestArtifacts() /// /// Creates a directory with permissions restricted to the current user on Unix. /// - internal /* for testing */ static void CreateDirectoryWithUserOnlyAccess(string path) + internal /* for testing */ void CreateDirectoryWithUserOnlyAccess(string path) { -#if NETFRAMEWORK - Directory.CreateDirectory(path); -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Directory.CreateDirectory(path); - } - else + _fileHelper.CreateDirectory(path); +#if !NETFRAMEWORK + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Directory.CreateDirectory(path); SetUnixDirectoryPermissions(path); } #endif From 69abd683d9d694a80bf7ca60ffc5293291acb0ec Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Wed, 29 Apr 2026 15:45:54 +0200 Subject: [PATCH 4/5] Fix test: CreateDirectory now called twice (parent + session dir) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../PostProcessing/ArtifactProcessingTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs index b594dd6719..32f6cb43a6 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs @@ -97,7 +97,7 @@ public void CollectArtifacts_ShouldSerializeToDisk() _artifactProcessingManager.CollectArtifacts(testRunCompleteEventArgs, string.Empty); // assert - _fileHelperMock.Verify(x => x.CreateDirectory(It.IsAny()), Times.Once); + _fileHelperMock.Verify(x => x.CreateDirectory(It.IsAny()), Times.Exactly(2)); _fileHelperMock.Verify(x => x.WriteAllTextToFile(It.IsAny(), It.IsAny()), Times.Exactly(2)); _dataSerializer.Verify(x => x.SerializePayload(It.IsAny(), It.IsAny()), Times.Once); } From f56a81f606097313bbe369b22498a16817038305 Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Mon, 4 May 2026 13:37:04 +0200 Subject: [PATCH 5/5] Skip chmod when directory does not exist (mocked in tests) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../PostProcessing/ArtifactProcessingManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs index 1450ef42ae..940c94f277 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs @@ -258,7 +258,7 @@ private TestArtifacts[] LoadTestArtifacts() { _fileHelper.CreateDirectory(path); #if !NETFRAMEWORK - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Directory.Exists(path)) { SetUnixDirectoryPermissions(path); }