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.
This commit is contained in:
committed by
Nick Craig-Wood
parent
1583cce1e2
commit
5fc1cc3ca1
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user