From 5fc1cc3ca1c39f2c3cdec37089491b1e5aa3564e Mon Sep 17 00:00:00 2001 From: Dhevenddra Date: Thu, 27 Aug 2026 16:33:10 +0530 Subject: [PATCH] test: skip the symlink tests when the platform won't allow symlinks Nine tests fail on an ordinary Windows machine, eight in backend/local and TestEnvironmentVariables in cmdtest, all with symlink file.txt \?\C:\Users\...\symlink.txt: A required privilege is not held by the client. Windows grants SeCreateSymbolicLinkPrivilege only to an elevated process or one running with Developer Mode enabled, and a default install gives an ordinary user neither. CI does not see this because the windows-latest runner is elevated, so the failures only show up on a contributor's own machine, where AGENTS.md asks for make quicktest to pass before opening a pull request. cmdtest already recognised the situation and attached a note to the failure saying the test could safely be ignored. If it is safe to ignore then the test knows it cannot run, so skip it and say why instead. backend/local gains a helper that tries a symlink in t.TempDir() and skips if it cannot make one, called from the six tests that need the privilege. Where a platform can create symlinks the probe succeeds and nothing is skipped, so other platforms are unchanged. TestMetadata is skipped whole because it creates its symlink before anything else and the object built from it is used throughout. TestSymlinkEscapeConcurrent is left alone: it goes through putLink and ignores the error, so it never needed the privilege. --- backend/local/local_internal_test.go | 18 ++++++++++++++++++ cmdtest/environment_test.go | 11 +++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/backend/local/local_internal_test.go b/backend/local/local_internal_test.go index 25adcdf80..b1d8d047c 100644 --- a/backend/local/local_internal_test.go +++ b/backend/local/local_internal_test.go @@ -96,7 +96,19 @@ func TestVerifyCopy(t *testing.T) { assert.Error(t, err) } +// skipIfNoSymlinks skips the test if this process can't create symlinks. +// +// Windows grants the privilege only to an elevated process or one running with +// Developer Mode enabled, so an ordinary user gets ERROR_PRIVILEGE_NOT_HELD. +func skipIfNoSymlinks(t *testing.T) { + t.Helper() + if err := os.Symlink("target", filepath.Join(t.TempDir(), "link")); err != nil { + t.Skipf("Skipping as symlinks are unavailable: %v", err) + } +} + func TestSymlink(t *testing.T) { + skipIfNoSymlinks(t) ctx := context.Background() r := fstest.NewRun(t) f := r.Flocal.(*Fs) @@ -238,6 +250,7 @@ func linksMode(f *Fs) { // faithful backup of the source) but must refuse to write through it, so // nothing lands outside the destination (CWE-59). func TestSymlinkEscapeWriteThroughBlocked(t *testing.T) { + skipIfNoSymlinks(t) ctx := context.Background() // A directory outside the destination the attacker wants to write into @@ -273,6 +286,7 @@ func TestSymlinkEscapeWriteThroughBlocked(t *testing.T) { // re-validated against the root, so the write-through is refused and nothing // escapes. func TestSymlinkEscapeNestedBlocked(t *testing.T) { + skipIfNoSymlinks(t) ctx := context.Background() evil := t.TempDir() @@ -324,6 +338,7 @@ func TestSymlinkEscapeConcurrent(t *testing.T) { // use: an in-tree symlink to a sibling directory can still be created and // written through, since that write stays inside the destination. func TestSymlinkInTreeWriteThroughWorks(t *testing.T) { + skipIfNoSymlinks(t) ctx := context.Background() r := fstest.NewRun(t) @@ -555,6 +570,7 @@ func TestHashOnDelete(t *testing.T) { } func TestMetadata(t *testing.T) { + skipIfNoSymlinks(t) ctx := context.Background() r := fstest.NewRun(t) const filePath = "metafile.txt" @@ -831,6 +847,7 @@ func TestFilter(t *testing.T) { } func testFilterSymlink(t *testing.T, copyLinks bool) { + skipIfNoSymlinks(t) ctx := context.Background() r := fstest.NewRun(t) defer r.Finalise() @@ -935,6 +952,7 @@ func TestFilterSymlinkLinks(t *testing.T) { } func TestCopySymlink(t *testing.T) { + skipIfNoSymlinks(t) ctx := context.Background() r := fstest.NewRun(t) defer r.Finalise() diff --git a/cmdtest/environment_test.go b/cmdtest/environment_test.go index 9e4f660d2..f86812f92 100644 --- a/cmdtest/environment_test.go +++ b/cmdtest/environment_test.go @@ -141,13 +141,12 @@ func TestEnvironmentVariables(t *testing.T) { // Reference: https://rclone.org/docs/#precedence // Create a symlink in test data err = os.Symlink(testdataPath+"/folderA", testdataPath+"/symlinkA") - if runtime.GOOS == "windows" { - errNote := "The policy settings on Windows often prohibit the creation of symlinks due to security issues.\n" - errNote += "You can safely ignore this test, if your change didn't affect environment variables." - require.NoError(t, err, errNote) - } else { - require.NoError(t, err) + if err != nil && runtime.GOOS == "windows" { + // Windows grants the privilege only to an elevated process or one + // running with Developer Mode enabled. + t.Skipf("Skipping as symlinks are unavailable: %v", err) } + require.NoError(t, err) // Create a local remote with explicit skip_links=false out, err = rclone("config", "create", "myLocal", "local", "skip_links", "false")