From e223a6609192f18b76a57b31a9a0338515a21789 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Fri, 8 Sep 2023 18:09:10 +0100 Subject: [PATCH 1/2] Bump github.com/cyphar/filepath-securejoin Signed-off-by: Paulo Gomes --- go.mod | 13 ++++++------- go.sum | 23 ++++++++++++++--------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index cef2802..fc0979a 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,19 @@ module github.com/go-git/go-billy/v5 // go-git supports the last 3 stable Go versions. go 1.19 -replace github.com/cyphar/filepath-securejoin => github.com/pjbgf/filepath-securejoin v0.0.0-20230821001828-0ca74e6d4bf8 - require ( - github.com/cyphar/filepath-securejoin v0.2.3 + github.com/cyphar/filepath-securejoin v0.2.4 github.com/onsi/gomega v1.27.10 - golang.org/x/sys v0.11.0 + golang.org/x/sys v0.12.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ) require ( github.com/google/go-cmp v0.5.9 // indirect - github.com/kr/pretty v0.2.1 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/text v0.11.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/text v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 418c52e..2c9b5b2 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,14 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -13,14 +16,16 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/pjbgf/filepath-securejoin v0.0.0-20230821001828-0ca74e6d4bf8 h1:nqjCeQ2TVnccihhBoVBd0p+70hCFT4yqJKhfc8l1D50= -github.com/pjbgf/filepath-securejoin v0.0.0-20230821001828-0ca74e6d4bf8/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 3994cd76bea8a403aad69281190b4b6368fe32aa Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Mon, 11 Sep 2023 17:05:18 +0100 Subject: [PATCH 2/2] osfs: Add WithDeduplicatePath In specific workflows within the BoundOS OS type, the returned path may have the base dir duplicated. By default such duplications are removed automatically, the new WithDeduplicatePath allows users to toggle that behaviour. Signed-off-by: Paulo Gomes --- osfs/os.go | 23 +++++++- osfs/os_bound.go | 24 ++++++-- osfs/os_bound_test.go | 129 +++++++++++++++++++++++------------------- 3 files changed, 110 insertions(+), 66 deletions(-) diff --git a/osfs/os.go b/osfs/os.go index cb7489a..a7fe79f 100644 --- a/osfs/os.go +++ b/osfs/os.go @@ -22,14 +22,18 @@ const ( var Default = &ChrootOS{} // New returns a new OS filesystem. +// By default paths are deduplicated, but still enforced +// under baseDir. For more info refer to WithDeduplicatePath. func New(baseDir string, opts ...Option) billy.Filesystem { - o := &options{} + o := &options{ + deduplicatePath: true, + } for _, opt := range opts { opt(o) } if o.Type == BoundOSFS { - return newBoundOS(baseDir) + return newBoundOS(baseDir, o.deduplicatePath) } return newChrootOS(baseDir) @@ -49,8 +53,23 @@ func WithChrootOS() Option { } } +// WithDeduplicatePath toggles the deduplication of the base dir in the path. +// This occurs when absolute links are being used. +// Assuming base dir /base/dir and an absolute symlink /base/dir/target: +// +// With DeduplicatePath (default): /base/dir/target +// Without DeduplicatePath: /base/dir/base/dir/target +// +// This option is only used by the BoundOS OS type. +func WithDeduplicatePath(enabled bool) Option { + return func(o *options) { + o.deduplicatePath = enabled + } +} + type options struct { Type + deduplicatePath bool } type Type int diff --git a/osfs/os_bound.go b/osfs/os_bound.go index 5e64ebd..b4b6dbc 100644 --- a/osfs/os_bound.go +++ b/osfs/os_bound.go @@ -41,11 +41,12 @@ import ( // 3. Readlink and Lstat ensures that the link file is located within the base // dir, evaluating any symlinks that file or base dir may contain. type BoundOS struct { - baseDir string + baseDir string + deduplicatePath bool } -func newBoundOS(d string) billy.Filesystem { - return &BoundOS{baseDir: d} +func newBoundOS(d string, deduplicatePath bool) billy.Filesystem { + return &BoundOS{baseDir: d, deduplicatePath: deduplicatePath} } func (fs *BoundOS) Create(filename string) (billy.File, error) { @@ -212,10 +213,21 @@ func (fs *BoundOS) createDir(fullpath string) error { func (fs *BoundOS) abs(filename string) (string, error) { if filename == fs.baseDir { filename = string(filepath.Separator) - } else if cw := fs.baseDir + string(filepath.Separator); strings.HasPrefix(filename, cw) { - filename = strings.TrimPrefix(filename, cw) } - return securejoin.SecureJoin(fs.baseDir, filename) + + path, err := securejoin.SecureJoin(fs.baseDir, filename) + if err != nil { + return "", nil + } + + if fs.deduplicatePath { + vol := filepath.VolumeName(fs.baseDir) + dup := filepath.Join(fs.baseDir, fs.baseDir[len(vol):]) + if strings.HasPrefix(path, dup+string(filepath.Separator)) { + return fs.abs(path[len(dup):]) + } + } + return path, nil } // insideBaseDir checks whether filename is located within diff --git a/osfs/os_bound_test.go b/osfs/os_bound_test.go index 8307028..bf98931 100644 --- a/osfs/os_bound_test.go +++ b/osfs/os_bound_test.go @@ -43,7 +43,7 @@ func TestOpen(t *testing.T) { name: "file: rel same dir", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "test-file", }, @@ -51,7 +51,7 @@ func TestOpen(t *testing.T) { name: "file: rel path to above cwd", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "rel-above-cwd"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "../../rel-above-cwd", }, @@ -60,7 +60,7 @@ func TestOpen(t *testing.T) { before: func(dir string) billy.Filesystem { os.Mkdir(filepath.Join(dir, "sub"), 0o700) os.WriteFile(filepath.Join(dir, "sub/rel-below-cwd"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "sub/rel-below-cwd", }, @@ -68,7 +68,7 @@ func TestOpen(t *testing.T) { name: "file: abs inside cwd", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "abs-test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "abs-test-file", makeAbs: true, @@ -76,7 +76,7 @@ func TestOpen(t *testing.T) { { name: "file: abs outside cwd", before: func(dir string) billy.Filesystem { - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "/some/path/outside/cwd", wantErr: notFoundError(), @@ -87,7 +87,7 @@ func TestOpen(t *testing.T) { target := filepath.Join(dir, "target-file") os.WriteFile(target, []byte("anything"), 0o600) os.Symlink(target, filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", }, @@ -95,7 +95,7 @@ func TestOpen(t *testing.T) { name: "symlink: rel outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("../../../../../../outside/cwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: true, @@ -105,7 +105,7 @@ func TestOpen(t *testing.T) { name: "symlink: abs outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("/some/path/outside/cwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: true, @@ -116,7 +116,7 @@ func TestOpen(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) if tt.before != nil { fs = tt.before(dir) @@ -185,7 +185,7 @@ func Test_Symlink(t *testing.T) { link: "new-dir/symlink", before: func(dir string) billy.Filesystem { os.Mkdir(filepath.Join(dir, "new-dir"), 0o701) - return newBoundOS(dir) + return newBoundOS(dir, true) }, target: filepath.FromSlash("../../../some/random/path"), }, @@ -194,7 +194,7 @@ func Test_Symlink(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) if tt.before != nil { fs = tt.before(dir) @@ -238,7 +238,7 @@ func Test_Symlink(t *testing.T) { func TestTempFile(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) f, err := fs.TempFile("", "prefix") g.Expect(err).ToNot(gomega.HaveOccurred()) @@ -266,7 +266,7 @@ func TestTempFile(t *testing.T) { func TestChroot(t *testing.T) { g := gomega.NewWithT(t) tmp := t.TempDir() - fs := newBoundOS(tmp) + fs := newBoundOS(tmp, true) f, err := fs.Chroot("test") g.Expect(err).ToNot(gomega.HaveOccurred()) @@ -277,7 +277,7 @@ func TestChroot(t *testing.T) { func TestRoot(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) root := fs.Root() g.Expect(root).To(gomega.Equal(dir)) @@ -297,7 +297,7 @@ func TestReadLink(t *testing.T) { name: "symlink: pointing to abs outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", expected: filepath.FromSlash("/etc/passwd"), @@ -311,7 +311,7 @@ func TestReadLink(t *testing.T) { name: "symlink: abs symlink pointing outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: true, @@ -329,7 +329,7 @@ func TestReadLink(t *testing.T) { os.Symlink(outside, filepath.Join(cwd, "symlink")) os.WriteFile(filepath.Join(outside, "file"), []byte("anything"), 0o600) - return newBoundOS(cwd) + return newBoundOS(cwd, true) }, filename: "current-dir/symlink/file", makeAbs: true, @@ -348,7 +348,7 @@ func TestReadLink(t *testing.T) { os.Symlink(cwdTarget, cwd) os.Symlink(cwdTarget, cwdAlt) os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(cwdAlt, "symlink-file")) - return newBoundOS(cwd) + return newBoundOS(cwd, true) }, filename: "symlink-file", expected: filepath.Join("cwd-target/file"), @@ -369,7 +369,7 @@ func TestReadLink(t *testing.T) { os.Symlink(cwdTarget, cwd) os.Symlink(outsideDir, outside) os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(outside, "symlink-file")) - return newBoundOS(cwd) + return newBoundOS(cwd, true) }, filename: "symlink-outside/symlink-file", wantErr: "path outside base dir", @@ -379,7 +379,7 @@ func TestReadLink(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) if tt.before != nil { fs = tt.before(dir) @@ -420,7 +420,7 @@ func TestLstat(t *testing.T) { name: "rel symlink: pointing to abs outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", }, @@ -428,7 +428,7 @@ func TestLstat(t *testing.T) { name: "rel symlink: pointing to rel path above cwd", before: func(dir string) billy.Filesystem { os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", }, @@ -436,7 +436,7 @@ func TestLstat(t *testing.T) { name: "abs symlink: pointing to abs outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: true, @@ -445,7 +445,7 @@ func TestLstat(t *testing.T) { name: "abs symlink: pointing to rel outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: false, @@ -463,7 +463,7 @@ func TestLstat(t *testing.T) { os.Symlink(cwdTarget, cwd) os.Symlink(cwdTarget, cwdAlt) os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(cwdAlt, "symlink-file")) - return newBoundOS(cwd) + return newBoundOS(cwd, true) }, filename: "symlink-file", makeAbs: false, @@ -483,7 +483,7 @@ func TestLstat(t *testing.T) { os.Symlink(cwdTarget, cwd) os.Symlink(outsideDir, outside) os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(outside, "symlink-file")) - return newBoundOS(cwd) + return newBoundOS(cwd, true) }, filename: "symlink-outside/symlink-file", makeAbs: false, @@ -503,7 +503,7 @@ func TestLstat(t *testing.T) { name: "file: rel", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "test-file", }, @@ -511,7 +511,7 @@ func TestLstat(t *testing.T) { name: "file: abs", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "test-file", makeAbs: true, @@ -521,7 +521,7 @@ func TestLstat(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) if tt.before != nil { fs = tt.before(dir) @@ -557,7 +557,7 @@ func TestStat(t *testing.T) { name: "rel symlink: pointing to abs outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", wantErr: notFoundError(), @@ -566,7 +566,7 @@ func TestStat(t *testing.T) { name: "rel symlink: pointing to rel path above cwd", before: func(dir string) billy.Filesystem { os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", wantErr: notFoundError(), @@ -576,7 +576,7 @@ func TestStat(t *testing.T) { name: "abs symlink: pointing to abs outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: true, @@ -586,7 +586,7 @@ func TestStat(t *testing.T) { name: "abs symlink: pointing to rel outside cwd", before: func(dir string) billy.Filesystem { os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: false, @@ -606,7 +606,7 @@ func TestStat(t *testing.T) { name: "rel file", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "test-file", }, @@ -614,7 +614,7 @@ func TestStat(t *testing.T) { name: "abs file", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "test-file", makeAbs: true, @@ -624,7 +624,7 @@ func TestStat(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) if tt.before != nil { fs = tt.before(dir) @@ -669,7 +669,7 @@ func TestRemove(t *testing.T) { { name: "inexistent dir", before: func(dir string) billy.Filesystem { - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "inexistent", wantErr: notFoundError(), @@ -678,7 +678,7 @@ func TestRemove(t *testing.T) { name: "same dir file", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "test-file", }, @@ -688,7 +688,7 @@ func TestRemove(t *testing.T) { target := filepath.Join(dir, "target-file") os.WriteFile(target, []byte("anything"), 0o600) os.Symlink(target, filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", }, @@ -696,7 +696,7 @@ func TestRemove(t *testing.T) { name: "rel path to file above cwd", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "rel-above-cwd"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "../../rel-above-cwd", }, @@ -704,7 +704,7 @@ func TestRemove(t *testing.T) { name: "abs file", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "abs-test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "abs-test-file", makeAbs: true, @@ -719,7 +719,7 @@ func TestRemove(t *testing.T) { os.MkdirAll(filepath.Dir(outsideFile), 0o700) os.WriteFile(outsideFile, []byte("anything"), 0o600) os.Symlink(outsideFile, filepath.Join(cwd, "remove-abs-symlink")) - return newBoundOS(cwd) + return newBoundOS(cwd, true) }, filename: "remove-abs-symlink", wantErr: notFoundError(), @@ -734,7 +734,7 @@ func TestRemove(t *testing.T) { os.MkdirAll(filepath.Dir(outsideFile), 0o700) os.WriteFile(outsideFile, []byte("anything"), 0o600) os.Symlink(filepath.Join("..", "outside-cwd", "file2"), filepath.Join(cwd, "remove-abs-symlink2")) - return newBoundOS(cwd) + return newBoundOS(cwd, true) }, filename: "remove-rel-symlink", wantErr: notFoundError(), @@ -744,7 +744,7 @@ func TestRemove(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) if tt.before != nil { fs = tt.before(dir) @@ -778,7 +778,7 @@ func TestRemoveAll(t *testing.T) { name: "parent with children", before: func(dir string) billy.Filesystem { os.MkdirAll(filepath.Join(dir, "parent/children"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "parent", }, @@ -790,7 +790,7 @@ func TestRemoveAll(t *testing.T) { name: "same dir file", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "test-file", }, @@ -800,7 +800,7 @@ func TestRemoveAll(t *testing.T) { target := filepath.Join(dir, "target-file") os.WriteFile(target, []byte("anything"), 0o600) os.Symlink(target, filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", }, @@ -808,7 +808,7 @@ func TestRemoveAll(t *testing.T) { name: "rel path to file above cwd", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "rel-above-cwd"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "../../rel-above-cwd", }, @@ -816,7 +816,7 @@ func TestRemoveAll(t *testing.T) { name: "abs file", before: func(dir string) billy.Filesystem { os.WriteFile(filepath.Join(dir, "abs-test-file"), []byte("anything"), 0o600) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "abs-test-file", makeAbs: true, @@ -825,7 +825,7 @@ func TestRemoveAll(t *testing.T) { name: "abs symlink", before: func(dir string) billy.Filesystem { os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return newBoundOS(dir) + return newBoundOS(dir, true) }, filename: "symlink", makeAbs: true, @@ -835,7 +835,7 @@ func TestRemoveAll(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir).(*BoundOS) + fs := newBoundOS(dir, true).(*BoundOS) if tt.before != nil { fs = tt.before(dir).(*BoundOS) @@ -886,7 +886,7 @@ func TestJoin(t *testing.T) { for _, tt := range tests { t.Run(tt.wanted, func(t *testing.T) { g := gomega.NewWithT(t) - fs := newBoundOS(t.TempDir()) + fs := newBoundOS(t.TempDir(), true) got := fs.Join(tt.elems...) g.Expect(got).To(gomega.Equal(tt.wanted)) @@ -903,6 +903,7 @@ func TestAbs(t *testing.T) { expected string makeExpectedAbs bool wantErr string + deduplicatePath bool before func(dir string) }{ { @@ -936,10 +937,17 @@ func TestAbs(t *testing.T) { expected: filepath.FromSlash("/working/dir/file"), }, { - name: "path: abs file within cwd", + name: "path: abs file within cwd", + cwd: filepath.FromSlash("/working/dir"), + filename: filepath.FromSlash("/working/dir/abs-file"), + expected: filepath.FromSlash("/working/dir/abs-file"), + deduplicatePath: true, + }, + { + name: "path: abs file within cwd wo deduplication", cwd: filepath.FromSlash("/working/dir"), filename: filepath.FromSlash("/working/dir/abs-file"), - expected: filepath.FromSlash("/working/dir/abs-file"), + expected: filepath.FromSlash("/working/dir/working/dir/abs-file"), }, { name: "path: abs file within cwd", @@ -956,6 +964,7 @@ func TestAbs(t *testing.T) { before: func(dir string) { os.Symlink(filepath.Join(dir, "within-cwd"), filepath.Join(dir, "ln-cwd-cwd")) }, + deduplicatePath: true, }, { name: "abs symlink: within cwd w rel descending target", @@ -966,6 +975,7 @@ func TestAbs(t *testing.T) { before: func(dir string) { os.Symlink("within-cwd", filepath.Join(dir, "ln-rel-cwd-cwd")) }, + deduplicatePath: true, }, { name: "abs symlink: within cwd w abs ascending target", @@ -976,6 +986,7 @@ func TestAbs(t *testing.T) { before: func(dir string) { os.Symlink("/some/outside/dir", filepath.Join(dir, "ln-cwd-up")) }, + deduplicatePath: true, }, { name: "abs symlink: within cwd w rel ascending target", @@ -986,6 +997,7 @@ func TestAbs(t *testing.T) { before: func(dir string) { os.Symlink("../../outside-cwd", filepath.Join(dir, "ln-rel-cwd-up")) }, + deduplicatePath: true, }, { name: "rel symlink: within cwd w abs descending target", @@ -995,6 +1007,7 @@ func TestAbs(t *testing.T) { before: func(dir string) { os.Symlink(filepath.Join(dir, "within-cwd"), filepath.Join(dir, "ln-cwd-cwd")) }, + deduplicatePath: true, }, { name: "rel symlink: within cwd w rel descending target", @@ -1032,7 +1045,7 @@ func TestAbs(t *testing.T) { cwd = t.TempDir() } - fs := newBoundOS(cwd).(*BoundOS) + fs := newBoundOS(cwd, tt.deduplicatePath).(*BoundOS) if tt.before != nil { tt.before(cwd) } @@ -1063,7 +1076,7 @@ func TestAbs(t *testing.T) { func TestReadDir(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) f, err := os.Create(filepath.Join(dir, "file1")) g.Expect(err).ToNot(gomega.HaveOccurred()) @@ -1098,7 +1111,7 @@ func TestMkdirAll(t *testing.T) { cwd := filepath.Join(root, "cwd") target := "abc" targetAbs := filepath.Join(cwd, target) - fs := newBoundOS(cwd) + fs := newBoundOS(cwd, true) // Even if CWD is changed outside of the fs instance, // the base dir must still be observed. @@ -1132,7 +1145,7 @@ func TestMkdirAll(t *testing.T) { func TestRename(t *testing.T) { g := gomega.NewWithT(t) dir := t.TempDir() - fs := newBoundOS(dir) + fs := newBoundOS(dir, true) oldFile := "old-file" newFile := filepath.Join("newdir", "newfile")