moby/internal/safepath/join_test.go
Paweł Gronowski 5bbcc41c20
volumes/subpath: Plumb context
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-01-19 17:32:21 +01:00

145 lines
4.5 KiB
Go

package safepath
import (
"context"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestJoinEscapingSymlink(t *testing.T) {
type testCase struct {
name string
target string
}
var cases []testCase
if runtime.GOOS == "windows" {
cases = []testCase{
{name: "root", target: `C:\`},
{name: "absolute file", target: `C:\Windows\System32\cmd.exe`},
}
} else {
cases = []testCase{
{name: "root", target: "/"},
{name: "absolute file", target: "/etc/passwd"},
}
}
cases = append(cases, testCase{name: "relative", target: "../../"})
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
tempDir := t.TempDir()
dir, err := filepath.EvalSymlinks(tempDir)
assert.NilError(t, err, "filepath.EvalSymlinks failed for temporary directory %s", tempDir)
err = os.Symlink(tc.target, filepath.Join(dir, "link"))
assert.NilError(t, err, "failed to create symlink to %s", tc.target)
safe, err := Join(context.Background(), dir, "link")
if err == nil {
safe.Close(context.Background())
}
assert.ErrorType(t, err, &ErrEscapesBase{})
})
}
}
func TestJoinGoodSymlink(t *testing.T) {
tempDir := t.TempDir()
dir, err := filepath.EvalSymlinks(tempDir)
assert.NilError(t, err, "filepath.EvalSymlinks failed for temporary directory %s", tempDir)
assert.Assert(t, os.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0o744), "failed to create file 'foo'")
assert.Assert(t, os.Mkdir(filepath.Join(dir, "subdir"), 0o744), "failed to create directory 'subdir'")
assert.Assert(t, os.WriteFile(filepath.Join(dir, "subdir/hello.txt"), []byte("world"), 0o744), "failed to create file 'subdir/hello.txt'")
assert.Assert(t, os.Symlink(filepath.Join(dir, "subdir"), filepath.Join(dir, "subdir_link_absolute")), "failed to create absolute symlink to directory 'subdir'")
assert.Assert(t, os.Symlink("subdir", filepath.Join(dir, "subdir_link_relative")), "failed to create relative symlink to directory 'subdir'")
assert.Assert(t, os.Symlink(filepath.Join(dir, "foo"), filepath.Join(dir, "foo_link_absolute")), "failed to create absolute symlink to file 'foo'")
assert.Assert(t, os.Symlink("foo", filepath.Join(dir, "foo_link_relative")), "failed to create relative symlink to file 'foo'")
for _, target := range []string{
"foo", "subdir",
"subdir_link_absolute", "foo_link_absolute",
"subdir_link_relative", "foo_link_relative",
} {
t.Run(target, func(t *testing.T) {
safe, err := Join(context.Background(), dir, target)
assert.NilError(t, err)
defer safe.Close(context.Background())
if strings.HasPrefix(target, "subdir") {
data, err := os.ReadFile(filepath.Join(safe.Path(), "hello.txt"))
assert.NilError(t, err)
assert.Assert(t, is.Equal(string(data), "world"))
}
})
}
}
func TestJoinWithSymlinkReplace(t *testing.T) {
tempDir := t.TempDir()
dir, err := filepath.EvalSymlinks(tempDir)
assert.NilError(t, err, "filepath.EvalSymlinks failed for temporary directory %s", tempDir)
link := filepath.Join(dir, "link")
target := filepath.Join(dir, "foo")
err = os.WriteFile(target, []byte("bar"), 0o744)
assert.NilError(t, err, "failed to create test file")
err = os.Symlink(target, link)
assert.Check(t, err, "failed to create symlink to foo")
safe, err := Join(context.Background(), dir, "link")
assert.NilError(t, err)
defer safe.Close(context.Background())
// Delete the link target.
err = os.Remove(target)
if runtime.GOOS == "windows" {
// On Windows it shouldn't be possible.
assert.Assert(t, is.ErrorType(err, &os.PathError{}), "link shouldn't be deletable before cleanup")
} else {
// On Linux we can delete it just fine.
assert.NilError(t, err, "failed to remove symlink")
// Replace target with a symlink to /etc/paswd
err = os.Symlink("/etc/passwd", target)
assert.NilError(t, err, "failed to create symlink")
}
// The returned safe path should still point to the old file.
data, err := os.ReadFile(safe.Path())
assert.NilError(t, err, "failed to read file")
assert.Check(t, is.Equal(string(data), "bar"))
}
func TestJoinCloseInvalidates(t *testing.T) {
tempDir := t.TempDir()
dir, err := filepath.EvalSymlinks(tempDir)
assert.NilError(t, err)
foo := filepath.Join(dir, "foo")
err = os.WriteFile(foo, []byte("bar"), 0o744)
assert.NilError(t, err, "failed to create test file")
safe, err := Join(context.Background(), dir, "foo")
assert.NilError(t, err)
assert.Check(t, safe.IsValid())
assert.NilError(t, safe.Close(context.Background()))
assert.Check(t, !safe.IsValid())
}