diff --git a/git/gogit/client.go b/git/gogit/client.go index a96084d07..966f229e0 100644 --- a/git/gogit/client.go +++ b/git/gogit/client.go @@ -27,6 +27,7 @@ import ( "github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-billy/v5/osfs" extgogit "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" @@ -39,7 +40,6 @@ import ( "github.com/go-git/go-git/v5/storage/memory" "github.com/fluxcd/pkg/git" - "github.com/fluxcd/pkg/git/gogit/fs" "github.com/fluxcd/pkg/git/repository" ) @@ -159,8 +159,8 @@ func WithSingleBranch(singleBranch bool) ClientOption { // Git related objects on disk. func WithDiskStorage() ClientOption { return func(c *Client) error { - wt := fs.New(c.path) - dot := fs.New(filepath.Join(c.path, extgogit.GitDirName)) + wt := osfs.New(c.path, osfs.WithBoundOS()) + dot := osfs.New(filepath.Join(c.path, extgogit.GitDirName), osfs.WithBoundOS()) c.storer = filesystem.NewStorage(dot, cache.NewObjectLRUDefault()) c.worktreeFS = wt diff --git a/git/gogit/clone_test.go b/git/gogit/clone_test.go index bf6d71f23..a5094430a 100644 --- a/git/gogit/clone_test.go +++ b/git/gogit/clone_test.go @@ -34,6 +34,7 @@ import ( "github.com/elazarl/goproxy" "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-billy/v5/osfs" extgogit "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" @@ -46,7 +47,6 @@ import ( "github.com/fluxcd/gitkit" "github.com/fluxcd/pkg/git" - "github.com/fluxcd/pkg/git/gogit/fs" "github.com/fluxcd/pkg/git/repository" "github.com/fluxcd/pkg/gittestserver" "github.com/fluxcd/pkg/ssh" @@ -1556,7 +1556,7 @@ func Fuzz_GoGitError(f *testing.F) { } func initRepo(tmpDir string) (*extgogit.Repository, string, error) { - sto := filesystem.NewStorage(fs.New(tmpDir), cache.NewObjectLRUDefault()) + sto := filesystem.NewStorage(osfs.New(tmpDir, osfs.WithBoundOS()), cache.NewObjectLRUDefault()) repo, err := extgogit.Init(sto, memfs.New()) if err != nil { return nil, "", err diff --git a/git/gogit/fs/join.go b/git/gogit/fs/join.go deleted file mode 100644 index 4590fe0e1..000000000 --- a/git/gogit/fs/join.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. -// Copyright (C) 2017 SUSE LLC. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Copyright 2022 The Flux authors. All rights reserved. -// Adapted from: github.com/go-git/go-billy/v5/osfs - -// Package securejoin is an implementation of the hopefully-soon-to-be-included -// SecureJoin helper that is meant to be part of the "path/filepath" package. -// The purpose of this project is to provide a PoC implementation to make the -// SecureJoin proposal (https://github.com/golang/go/issues/20126) more -// tangible. -package fs - -import ( - "bytes" - "errors" - "os" - "path/filepath" - "strings" - "syscall" -) - -// IsNotExist tells you if err is an error that implies that either the path -// accessed does not exist (or path components don't exist). This is -// effectively a more broad version of os.IsNotExist. -func IsNotExist(err error) bool { - // Check that it's not actually an ENOTDIR, which in some cases is a more - // convoluted case of ENOENT (usually involving weird paths). - return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT) -} - -// SecureJoinVFS joins the two given path components (similar to Join) except -// that the returned path is guaranteed to be scoped inside the provided root -// path (when evaluated). Any symbolic links in the path are evaluated with the -// given root treated as the root of the filesystem, similar to a chroot. The -// filesystem state is evaluated through the given VFS interface (if nil, the -// standard os.* family of functions are used). -// -// Note that the guarantees provided by this function only apply if the path -// components in the returned string are not modified (in other words are not -// replaced with symlinks on the filesystem) after this function has returned. -// Such a symlink race is necessarily out-of-scope of SecureJoin. -func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) { - // Use the os.* VFS implementation if none was specified. - if vfs == nil { - vfs = osVFS{} - } - - var path bytes.Buffer - n := 0 - for unsafePath != "" { - if n > 255 { - return "", &os.PathError{Op: "SecureJoin", Path: root + "/" + unsafePath, Err: syscall.ELOOP} - } - - // Next path component, p. - i := strings.IndexRune(unsafePath, filepath.Separator) - var p string - if i == -1 { - p, unsafePath = unsafePath, "" - } else { - p, unsafePath = unsafePath[:i], unsafePath[i+1:] - } - - // Create a cleaned path, using the lexical semantics of /../a, to - // create a "scoped" path component which can safely be joined to fullP - // for evaluation. At this point, path.String() doesn't contain any - // symlink components. - cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p) - if cleanP == string(filepath.Separator) { - path.Reset() - continue - } - fullP := filepath.Clean(root + cleanP) - - // Figure out whether the path is a symlink. - fi, err := vfs.Lstat(fullP) - if err != nil && !IsNotExist(err) { - return "", err - } - // Treat non-existent path components the same as non-symlinks (we - // can't do any better here). - if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 { - path.WriteString(p) - path.WriteRune(filepath.Separator) - continue - } - - // Only increment when we actually dereference a link. - n++ - - // It's a symlink, expand it by prepending it to the yet-unparsed path. - dest, err := vfs.Readlink(fullP) - if err != nil { - return "", err - } - // Absolute symlinks reset any work we've already done. - if filepath.IsAbs(dest) { - if !fi.IsDir() && strings.HasPrefix(dest, root+string(filepath.Separator)) { - return filepath.Clean(dest), nil - } - path.Reset() - } - unsafePath = dest + string(filepath.Separator) + unsafePath - } - - // We have to clean path.String() here because it may contain '..' - // components that are entirely lexical, but would be misleading otherwise. - // And finally do a final clean to ensure that root is also lexically - // clean. - fullP := filepath.Clean(string(filepath.Separator) + path.String()) - return filepath.Clean(root + fullP), nil -} - -// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library -// of functions as the VFS. If in doubt, use this function over SecureJoinVFS. -func SecureJoin(root, unsafePath string) (string, error) { - return SecureJoinVFS(root, unsafePath, nil) -} - -// In future this should be moved into a separate package, because now there -// are several projects (umoci and go-mtree) that are using this sort of -// interface. - -// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is -// equivalent to using the standard os.* family of functions. This is mainly -// used for the purposes of mock testing, but also can be used to otherwise use -// SecureJoin with VFS-like system. -type VFS interface { - // Lstat returns a FileInfo describing the named file. If the file is a - // symbolic link, the returned FileInfo describes the symbolic link. Lstat - // makes no attempt to follow the link. These semantics are identical to - // os.Lstat. - Lstat(name string) (os.FileInfo, error) - - // Readlink returns the destination of the named symbolic link. These - // semantics are identical to os.Readlink. - Readlink(name string) (string, error) -} - -// osVFS is the "nil" VFS, in that it just passes everything through to the os -// module. -type osVFS struct{} - -// Lstat returns a FileInfo describing the named file. If the file is a -// symbolic link, the returned FileInfo describes the symbolic link. Lstat -// makes no attempt to follow the link. These semantics are identical to -// os.Lstat. -func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } - -// Readlink returns the destination of the named symbolic link. These -// semantics are identical to os.Readlink. -func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) } diff --git a/git/gogit/fs/join_test.go b/git/gogit/fs/join_test.go deleted file mode 100644 index 4d441dedb..000000000 --- a/git/gogit/fs/join_test.go +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright (C) 2017 SUSE LLC. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Copyright 2022 The Flux authors. All rights reserved. -// Adapted from: github.com/go-git/go-billy/v5/osfs - -package fs - -import ( - "bytes" - "errors" - "io/ioutil" - "os" - "path/filepath" - "syscall" - "testing" -) - -// TODO: These tests won't work on plan9 because it doesn't have symlinks, and -// also we use '/' here explicitly which probably won't work on Windows. - -func symlink(t *testing.T, oldname, newname string) { - if err := os.Symlink(oldname, newname); err != nil { - t.Fatal(err) - } -} - -func TestSymlinkFile(t *testing.T) { - dir := t.TempDir() - wanted := []byte("file content") - - target := filepath.Join(dir, "target-file") - os.WriteFile(target, wanted, 0o644) - os.Symlink(target, filepath.Join(dir, "symlink")) - - fn, err := SecureJoin(dir, "symlink") - if err != nil { - t.Errorf("fail to securejoin: %v", err) - } - - got, err := os.ReadFile(fn) - if err != nil { - t.Errorf("fail to securejoin: %v", err) - } - if !bytes.Equal(got, wanted) { - t.Errorf("wanted %q got %q", wanted, got) - } -} - -// Test basic handling of symlink expansion. -func TestSymlink(t *testing.T) { - dir, err := ioutil.TempDir("", "TestSymlink") - if err != nil { - t.Fatal(err) - } - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - symlink(t, "somepath", filepath.Join(dir, "etc")) - symlink(t, "../../../../../../../../../../../../../etc", filepath.Join(dir, "etclink")) - symlink(t, "/../../../../../../../../../../../../../etc/passwd", filepath.Join(dir, "passwd")) - - for _, test := range []struct { - root, unsafe string - expected string - }{ - // Make sure that expansion with a root of '/' proceeds in the expected fashion. - {"/", filepath.Join(dir, "passwd"), "/etc/passwd"}, - {"/", filepath.Join(dir, "etclink"), "/etc"}, - {"/", filepath.Join(dir, "etc"), filepath.Join(dir, "somepath")}, - // Now test scoped expansion. - {dir, "passwd", filepath.Join(dir, "somepath", "passwd")}, - {dir, "etclink", filepath.Join(dir, "somepath")}, - {dir, "etc", filepath.Join(dir, "somepath")}, - {dir, "etc/test", filepath.Join(dir, "somepath", "test")}, - {dir, "etc/test/..", filepath.Join(dir, "somepath")}, - } { - got, err := SecureJoin(test.root, test.unsafe) - if err != nil { - t.Errorf("securejoin(%q, %q): unexpected error: %v", test.root, test.unsafe, err) - continue - } - // This is only for OS X, where /etc is a symlink to /private/etc. In - // principle, SecureJoin(/, pth) is the same as EvalSymlinks(pth) in - // the case where the path exists. - if test.root == "/" { - if expected, err := filepath.EvalSymlinks(test.expected); err == nil { - test.expected = expected - } - } - if got != test.expected { - t.Errorf("securejoin(%q, %q): expected %q, got %q", test.root, test.unsafe, test.expected, got) - continue - } - } -} - -// In a path without symlinks, SecureJoin is equivalent to Clean+Join. -func TestNoSymlink(t *testing.T) { - dir, err := ioutil.TempDir("", "TestNoSymlink") - if err != nil { - t.Fatal(err) - } - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - for _, test := range []struct { - root, unsafe string - }{ - // TODO: Do we need to have some conditional FromSlash handling here? - {dir, "somepath"}, - {dir, "even/more/path"}, - {dir, "/this/is/a/path"}, - {dir, "also/a/../path/././/with/some/./.././junk"}, - {dir, "yetanother/../path/././/with/some/./.././junk../../../../../../../../../../../../etc/passwd"}, - {dir, "/../../../../../../../../../../../../../../../../etc/passwd"}, - {dir, "../../../../../../../../../../../../../../../../somedir"}, - {dir, "../../../../../../../../../../../../../../../../"}, - {dir, "./../../.././././../../../../../../../../../../../../../../../../etc passwd"}, - } { - expected := filepath.Join(test.root, filepath.Clean(string(filepath.Separator)+test.unsafe)) - got, err := SecureJoin(test.root, test.unsafe) - if err != nil { - t.Errorf("securejoin(%q, %q): unexpected error: %v", test.root, test.unsafe, err) - continue - } - if got != expected { - t.Errorf("securejoin(%q, %q): expected %q, got %q", test.root, test.unsafe, expected, got) - continue - } - } -} - -// Make sure that .. is **not** expanded lexically. -func TestNonLexical(t *testing.T) { - dir, err := ioutil.TempDir("", "TestNonLexical") - if err != nil { - t.Fatal(err) - } - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - os.MkdirAll(filepath.Join(dir, "subdir"), 0755) - os.MkdirAll(filepath.Join(dir, "cousinparent", "cousin"), 0755) - symlink(t, "../cousinparent/cousin", filepath.Join(dir, "subdir", "link")) - symlink(t, "/../cousinparent/cousin", filepath.Join(dir, "subdir", "link2")) - symlink(t, "/../../../../../../../../../../../../../../../../cousinparent/cousin", filepath.Join(dir, "subdir", "link3")) - - for _, test := range []struct { - root, unsafe string - expected string - }{ - {dir, "subdir", filepath.Join(dir, "subdir")}, - {dir, "subdir/link/test", filepath.Join(dir, "cousinparent", "cousin", "test")}, - {dir, "subdir/link2/test", filepath.Join(dir, "cousinparent", "cousin", "test")}, - {dir, "subdir/link3/test", filepath.Join(dir, "cousinparent", "cousin", "test")}, - {dir, "subdir/../test", filepath.Join(dir, "test")}, - // This is the divergence from a simple filepath.Clean implementation. - {dir, "subdir/link/../test", filepath.Join(dir, "cousinparent", "test")}, - {dir, "subdir/link2/../test", filepath.Join(dir, "cousinparent", "test")}, - {dir, "subdir/link3/../test", filepath.Join(dir, "cousinparent", "test")}, - } { - got, err := SecureJoin(test.root, test.unsafe) - if err != nil { - t.Errorf("securejoin(%q, %q): unexpected error: %v", test.root, test.unsafe, err) - continue - } - if got != test.expected { - t.Errorf("securejoin(%q, %q): expected %q, got %q", test.root, test.unsafe, test.expected, got) - continue - } - } -} - -// Make sure that symlink loops result in errors. -func TestSymlinkLoop(t *testing.T) { - dir, err := ioutil.TempDir("", "TestSymlinkLoop") - if err != nil { - t.Fatal(err) - } - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - os.MkdirAll(filepath.Join(dir, "subdir"), 0755) - symlink(t, "../../../../../../../../../../../../../../../../path", filepath.Join(dir, "subdir", "link")) - symlink(t, "/subdir/link", filepath.Join(dir, "path")) - symlink(t, "/../../../../../../../../../../../../../../../../self", filepath.Join(dir, "self")) - - for _, test := range []struct { - root, unsafe string - }{ - {dir, "subdir/link"}, - {dir, "path"}, - {dir, "../../path"}, - {dir, "subdir/link/../.."}, - {dir, "../../../../../../../../../../../../../../../../subdir/link/../../../../../../../../../../../../../../../.."}, - {dir, "self"}, - {dir, "self/.."}, - {dir, "/../../../../../../../../../../../../../../../../self/.."}, - {dir, "/self/././.."}, - } { - got, err := SecureJoin(test.root, test.unsafe) - if !errors.Is(err, syscall.ELOOP) { - t.Errorf("securejoin(%q, %q): expected ELOOP, got %v & %q", test.root, test.unsafe, err, got) - continue - } - } -} - -// Make sure that ENOTDIR is correctly handled. -func TestEnotdir(t *testing.T) { - dir, err := ioutil.TempDir("", "TestEnotdir") - if err != nil { - t.Fatal(err) - } - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - os.MkdirAll(filepath.Join(dir, "subdir"), 0755) - ioutil.WriteFile(filepath.Join(dir, "notdir"), []byte("I am not a directory!"), 0755) - symlink(t, "/../../../notdir/somechild", filepath.Join(dir, "subdir", "link")) - - for _, test := range []struct { - root, unsafe string - }{ - {dir, "subdir/link"}, - {dir, "notdir"}, - {dir, "notdir/child"}, - } { - _, err := SecureJoin(test.root, test.unsafe) - if err != nil { - t.Errorf("securejoin(%q, %q): unexpected error: %v", test.root, test.unsafe, err) - continue - } - } -} - -// Some silly tests to make sure that all error types are correctly handled. -func TestIsNotExist(t *testing.T) { - for _, test := range []struct { - err error - expected bool - }{ - {&os.PathError{Op: "test1", Err: syscall.ENOENT}, true}, - {&os.LinkError{Op: "test1", Err: syscall.ENOENT}, true}, - {&os.SyscallError{Syscall: "test1", Err: syscall.ENOENT}, true}, - {&os.PathError{Op: "test2", Err: syscall.ENOTDIR}, true}, - {&os.LinkError{Op: "test2", Err: syscall.ENOTDIR}, true}, - {&os.SyscallError{Syscall: "test2", Err: syscall.ENOTDIR}, true}, - {&os.PathError{Op: "test3", Err: syscall.EACCES}, false}, - {&os.LinkError{Op: "test3", Err: syscall.EACCES}, false}, - {&os.SyscallError{Syscall: "test3", Err: syscall.EACCES}, false}, - {errors.New("not a proper error"), false}, - } { - got := IsNotExist(test.err) - if got != test.expected { - t.Errorf("IsNotExist(%#v): expected %v, got %v", test.err, test.expected, got) - } - } -} - -type mockVFS struct { - lstat func(path string) (os.FileInfo, error) - readlink func(path string) (string, error) -} - -func (m mockVFS) Lstat(path string) (os.FileInfo, error) { return m.lstat(path) } -func (m mockVFS) Readlink(path string) (string, error) { return m.readlink(path) } - -// Make sure that SecureJoinVFS actually does use the given VFS interface. -func TestSecureJoinVFS(t *testing.T) { - dir, err := ioutil.TempDir("", "TestNonLexical") - if err != nil { - t.Fatal(err) - } - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - os.MkdirAll(filepath.Join(dir, "subdir"), 0755) - os.MkdirAll(filepath.Join(dir, "cousinparent", "cousin"), 0755) - symlink(t, "../cousinparent/cousin", filepath.Join(dir, "subdir", "link")) - symlink(t, "/../cousinparent/cousin", filepath.Join(dir, "subdir", "link2")) - symlink(t, "/../../../../../../../../../../../../../../../../cousinparent/cousin", filepath.Join(dir, "subdir", "link3")) - - for _, test := range []struct { - root, unsafe string - expected string - }{ - {dir, "subdir", filepath.Join(dir, "subdir")}, - {dir, "subdir/link/test", filepath.Join(dir, "cousinparent", "cousin", "test")}, - {dir, "subdir/link2/test", filepath.Join(dir, "cousinparent", "cousin", "test")}, - {dir, "subdir/link3/test", filepath.Join(dir, "cousinparent", "cousin", "test")}, - {dir, "subdir/../test", filepath.Join(dir, "test")}, - // This is the divergence from a simple filepath.Clean implementation. - {dir, "subdir/link/../test", filepath.Join(dir, "cousinparent", "test")}, - {dir, "subdir/link2/../test", filepath.Join(dir, "cousinparent", "test")}, - {dir, "subdir/link3/../test", filepath.Join(dir, "cousinparent", "test")}, - } { - var nLstat, nReadlink int - mock := mockVFS{ - lstat: func(path string) (os.FileInfo, error) { nLstat++; return os.Lstat(path) }, - readlink: func(path string) (string, error) { nReadlink++; return os.Readlink(path) }, - } - - got, err := SecureJoinVFS(test.root, test.unsafe, mock) - if err != nil { - t.Errorf("securejoin(%q, %q): unexpected error: %v", test.root, test.unsafe, err) - continue - } - if got != test.expected { - t.Errorf("securejoin(%q, %q): expected %q, got %q", test.root, test.unsafe, test.expected, got) - continue - } - if nLstat == 0 && nReadlink == 0 { - t.Errorf("securejoin(%q, %q): expected to use either lstat or readlink, neither were used", test.root, test.unsafe) - } - } -} - -// Make sure that SecureJoinVFS actually does use the given VFS interface, and -// that errors are correctly propagated. -func TestSecureJoinVFSErrors(t *testing.T) { - var ( - lstatErr = errors.New("lstat error") - readlinkErr = errors.New("readlink err") - ) - - // Set up directory. - dir, err := ioutil.TempDir("", "TestSecureJoinVFSErrors") - if err != nil { - t.Fatal(err) - } - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - // Make a link. - symlink(t, "../../../../../../../../../../../../../../../../path", filepath.Join(dir, "link")) - - // Define some fake mock functions. - lstatFailFn := func(path string) (os.FileInfo, error) { return nil, lstatErr } - readlinkFailFn := func(path string) (string, error) { return "", readlinkErr } - - // Make sure that the set of {lstat, readlink} failures do propagate. - for idx, test := range []struct { - vfs VFS - expected []error - }{ - { - expected: []error{nil}, - vfs: mockVFS{ - lstat: os.Lstat, - readlink: os.Readlink, - }, - }, - { - expected: []error{lstatErr}, - vfs: mockVFS{ - lstat: lstatFailFn, - readlink: os.Readlink, - }, - }, - { - expected: []error{readlinkErr}, - vfs: mockVFS{ - lstat: os.Lstat, - readlink: readlinkFailFn, - }, - }, - { - expected: []error{lstatErr, readlinkErr}, - vfs: mockVFS{ - lstat: lstatFailFn, - readlink: readlinkFailFn, - }, - }, - } { - _, err := SecureJoinVFS(dir, "link", test.vfs) - - success := false - for _, exp := range test.expected { - if err == exp { - success = true - } - } - if !success { - t.Errorf("SecureJoinVFS.mock%d: expected to get lstatError, got %v", idx, err) - } - } -} diff --git a/git/gogit/fs/osfs_linux.go b/git/gogit/fs/osfs_linux.go deleted file mode 100644 index dd350456b..000000000 --- a/git/gogit/fs/osfs_linux.go +++ /dev/null @@ -1,41 +0,0 @@ -//go:build linux -// +build linux - -/* -Copyright 2017 Go-Git authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Copyright 2022 The Flux authors. All rights reserved. -// Adapted from: github.com/go-git/go-billy/v5/osfs - -package fs - -import ( - "golang.org/x/sys/unix" -) - -func (f *file) Lock() error { - f.m.Lock() - defer f.m.Unlock() - - return unix.Flock(int(f.File.Fd()), unix.LOCK_EX) -} - -func (f *file) Unlock() error { - f.m.Lock() - defer f.m.Unlock() - - return unix.Flock(int(f.File.Fd()), unix.LOCK_UN) -} diff --git a/git/gogit/fs/osfs_os.go b/git/gogit/fs/osfs_os.go deleted file mode 100644 index 64662b946..000000000 --- a/git/gogit/fs/osfs_os.go +++ /dev/null @@ -1,293 +0,0 @@ -/* -Copyright 2017 Go-Git authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Copyright 2022 The Flux authors. All rights reserved. -// Adapted from: github.com/go-git/go-billy/v5/osfs - -package fs - -import ( - "fmt" - stdfs "io/fs" - "os" - "path/filepath" - "strings" - "sync" - - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/go-git/go-billy/v5" -) - -const ( - defaultDirectoryMode = 0o755 - defaultCreateMode = 0o666 -) - -// OS is a fs implementation based on the OS filesystem which has some -// changes in behaviour when compared to the upstream go-git/go-billy/v5/osfs: -// -// - Chroot doesn't return a chrooted filesystem but returns a new OS filesystem. -// - Relative paths are forced to descend from the working dir. -// - Symlinks don't have its targets modified, and therefore can point to locations -// outside the working dir or to non-existent paths. -// - OpenFile honours the FileMode passed as argument. -// - ReadLink and Lstat does not follow symlinks as most other funcs do. -// However, it ensures that: -// -// a) The filename is located within the current dir. -// b) The dir in which filename is based, is located within the current dir. -type OS struct { - workingDir string -} - -// New returns a new OS filesystem using the workingDir as prefix for relative paths. -// It also ensures that operations are kept within that working dir. -func New(workingDir string) billy.Filesystem { - return &OS{ - workingDir: workingDir, - } -} - -func (fs *OS) Create(filename string) (billy.File, error) { - return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode) -} - -func (fs *OS) OpenFile(filename string, flag int, perm os.FileMode) (billy.File, error) { - fn, err := fs.abs(filename) - if err != nil { - return nil, err - } - if flag&os.O_CREATE != 0 { - if err := fs.createDir(fn); err != nil { - return nil, err - } - } - - f, err := os.OpenFile(fn, flag, perm) - if err != nil { - return nil, err - } - return &file{File: f}, err -} - -func (fs *OS) ReadDir(path string) ([]os.FileInfo, error) { - dir, err := fs.abs(path) - if err != nil { - return nil, err - } - - entries, err := os.ReadDir(dir) - if err != nil { - return nil, err - } - infos := make([]stdfs.FileInfo, 0, len(entries)) - for _, entry := range entries { - info, err := entry.Info() - if err != nil { - return nil, err - } - infos = append(infos, info) - } - return infos, nil -} - -func (fs *OS) Rename(from, to string) error { - f, err := fs.abs(from) - if err != nil { - return err - } - t, err := fs.abs(to) - if err != nil { - return err - } - - // MkdirAll for target name. - if err := fs.createDir(t); err != nil { - return err - } - - return os.Rename(f, t) -} - -func (fs *OS) MkdirAll(path string, perm os.FileMode) error { - dir, err := fs.abs(path) - if err != nil { - return err - } - return os.MkdirAll(dir, perm) -} - -func (fs *OS) Open(filename string) (billy.File, error) { - return fs.OpenFile(filename, os.O_RDONLY, 0) -} - -func (fs *OS) Stat(filename string) (os.FileInfo, error) { - filename, err := fs.abs(filename) - if err != nil { - return nil, err - } - return os.Stat(filename) -} - -func (fs *OS) Remove(filename string) error { - fn, err := fs.abs(filename) - if err != nil { - return err - } - return os.Remove(fn) -} - -// TempFile creates a temporary file. If dir is empty, the file -// will be created within the OS Temporary dir. If dir is provided -// it must descend from the current working dir. -func (fs *OS) TempFile(dir, prefix string) (billy.File, error) { - if dir != "" { - var err error - dir, err = fs.abs(dir) - if err != nil { - return nil, err - } - } - - f, err := os.CreateTemp(dir, prefix) - if err != nil { - return nil, err - } - return &file{File: f}, nil -} - -func (fs *OS) Join(elem ...string) string { - return filepath.Join(elem...) -} - -func (fs *OS) RemoveAll(path string) error { - dir, err := fs.abs(path) - if err != nil { - return err - } - return os.RemoveAll(dir) -} - -func (fs *OS) Symlink(target, link string) error { - ln, err := fs.abs(link) - if err != nil { - return err - } - // MkdirAll for containing dir. - if err := fs.createDir(ln); err != nil { - return err - } - return os.Symlink(target, ln) -} - -func (fs *OS) Lstat(filename string) (os.FileInfo, error) { - filename = filepath.Clean(filename) - if !filepath.IsAbs(filename) { - filename = filepath.Join(fs.workingDir, filename) - } - if ok, err := fs.insideWorkingDirEval(filename); !ok { - return nil, err - } - return os.Lstat(filename) -} - -func (fs *OS) Readlink(link string) (string, error) { - if !filepath.IsAbs(link) { - link = filepath.Clean(filepath.Join(fs.workingDir, link)) - } - if ok, err := fs.insideWorkingDirEval(link); !ok { - return "", err - } - return os.Readlink(link) -} - -// Chroot returns a new OS filesystem, with working directory set to path. -func (fs *OS) Chroot(path string) (billy.Filesystem, error) { - joined, err := securejoin.SecureJoin(fs.workingDir, path) - if err != nil { - return nil, err - } - return New(joined), nil -} - -// Root returns the current working dir of the billy.Filesystem. -// This is required in order for this implementation to be a drop-in -// replacement for other upstream implementations (e.g. memory and osfs). -func (fs *OS) Root() string { - return fs.workingDir -} - -// file is a wrapper for an os.File which adds support for file locking. -type file struct { - *os.File - m sync.Mutex -} - -func (fs *OS) createDir(fullpath string) error { - dir := filepath.Dir(fullpath) - if dir != "." { - if err := os.MkdirAll(dir, defaultDirectoryMode); err != nil { - return err - } - } - - return nil -} - -// abs transforms filename to an absolute path, taking into account the working dir. -// Relative paths won't be allowed to ascend the working dir, so `../file` will become -// `/working-dir/file`. -// -// Note that if filename is a symlink, the returned address will be the target of the -// symlink. -func (fs *OS) abs(filename string) (string, error) { - if filename == fs.workingDir { - filename = "/" - } else if strings.HasPrefix(filename, fs.workingDir+string(filepath.Separator)) { - filename = strings.TrimPrefix(filename, fs.workingDir+string(filepath.Separator)) - } - return SecureJoin(fs.workingDir, filename) -} - -// insideWorkingDir checks whether filename is located within -// the fs.workingDir. -func (fs *OS) insideWorkingDir(filename string) (bool, error) { - if filename == fs.workingDir { - return true, nil - } - if !strings.HasPrefix(filename, fs.workingDir+string(filepath.Separator)) { - return false, fmt.Errorf("path outside working dir") - } - return true, nil -} - -// insideWorkingDirEval checks whether filename is contained within -// a dir that is within the fs.workingDir, by evaluating any symlinks -// that either filename or fs.workingDir may contain. -func (fs *OS) insideWorkingDirEval(filename string) (bool, error) { - dir, err := filepath.EvalSymlinks(filepath.Dir(filename)) - if dir == "" || os.IsNotExist(err) { - dir = filepath.Dir(filename) - } - wd, err := filepath.EvalSymlinks(fs.workingDir) - if wd == "" || os.IsNotExist(err) { - wd = fs.workingDir - } - if filename != wd && dir != wd && !strings.HasPrefix(dir, wd+string(filepath.Separator)) { - return false, fmt.Errorf("path outside working dir") - } - return true, nil -} diff --git a/git/gogit/fs/osfs_test.go b/git/gogit/fs/osfs_test.go deleted file mode 100644 index d465a50ee..000000000 --- a/git/gogit/fs/osfs_test.go +++ /dev/null @@ -1,1141 +0,0 @@ -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package fs - -import ( - "fmt" - "os" - "path/filepath" - "runtime" - "syscall" - "testing" - - "github.com/go-git/go-billy/v5" - . "github.com/onsi/gomega" -) - -func TestOpen(t *testing.T) { - tests := []struct { - name string - filename string - makeAbs bool - before func(dir string) billy.Filesystem - wantErr string - }{ - { - name: "file: rel same dir", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "test-file", - }, - { - 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 New(dir) - }, - filename: "../../rel-above-cwd", - }, - { - name: "file: rel path to below cwd", - 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 New(dir) - }, - filename: "sub/rel-below-cwd", - }, - { - name: "file: abs inside cwd", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "abs-test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "abs-test-file", - makeAbs: true, - }, - { - name: "file: abs outside cwd", - before: func(dir string) billy.Filesystem { - return New(dir) - }, - filename: "/some/path/outside/cwd", - wantErr: "/some/path/outside/cwd: no such file or directory", - }, - { - name: "symlink: same dir", - before: func(dir string) billy.Filesystem { - target := filepath.Join(dir, "target-file") - os.WriteFile(target, []byte("anything"), 0o600) - os.Symlink(target, filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - }, - { - name: "symlink: rel outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("../../../../../../outside/cwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: true, - wantErr: "/outside/cwd: no such file or directory", - }, - { - name: "symlink: abs outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("/some/path/outside/cwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: true, - wantErr: "/some/path/outside/cwd: no such file or directory", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - if tt.before != nil { - fs = tt.before(dir) - } - - filename := tt.filename - if tt.makeAbs { - filename = filepath.Join(dir, filename) - } - - fi, err := fs.Open(filename) - if tt.wantErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantErr)) - g.Expect(fi).To(BeNil()) - } else { - g.Expect(err).To(BeNil()) - g.Expect(fi).ToNot(BeNil()) - } - }) - } -} - -func Test_Symlink(t *testing.T) { - if runtime.GOOS == "linux" { - // The umask value set at OS level can impact this test, so - // it is set to 0 during the duration of this test and then - // reverted back to the original value. - defer syscall.Umask(syscall.Umask(0)) - } - - tests := []struct { - name string - link string - target string - before func(dir string) billy.Filesystem - wantStatErr string - }{ - { - name: "link to abs valid target", - link: "symlink", - target: "/etc/passwd", - }, - { - name: "link to abs inexistent target", - link: "symlink", - target: "/some/random/path", - }, - { - name: "link to rel valid target", - link: "symlink", - target: "../../../../../../../../../etc/passwd", - }, - { - name: "link to rel inexistent target", - link: "symlink", - target: "../../../some/random/path", - }, - { - name: "auto create dir", - link: "new-dir/symlink", - target: "../../../some/random/path", - }, - { - name: "keep dir filemode if exists", - link: "new-dir/symlink", - before: func(dir string) billy.Filesystem { - os.Mkdir(filepath.Join(dir, "new-dir"), 0o701) - return New(dir) - }, - target: "../../../some/random/path", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - if tt.before != nil { - fs = tt.before(dir) - } - - // Even if CWD is changed outside of fs the instance, - // the current working dir must still be observed. - err := os.Chdir(os.TempDir()) - g.Expect(err).ToNot(HaveOccurred()) - - link := filepath.Join(dir, tt.link) - - diBefore, _ := os.Lstat(filepath.Dir(link)) - - err = fs.Symlink(tt.target, tt.link) - g.Expect(err).ToNot(HaveOccurred()) - - fi, err := os.Lstat(link) - if tt.wantStatErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantStatErr)) - } else { - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(fi).ToNot(BeNil()) - } - - got, err := os.Readlink(link) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(got).To(Equal(tt.target)) - - diAfter, err := os.Lstat(filepath.Dir(link)) - g.Expect(err).ToNot(HaveOccurred()) - - if diBefore != nil { - g.Expect(diAfter.Mode()).To(Equal(diBefore.Mode())) - } - }) - } -} - -func TestTempFile(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - f, err := fs.TempFile("", "prefix") - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(f).ToNot(BeNil()) - g.Expect(f.Name()).To(ContainSubstring(os.TempDir())) - - f, err = fs.TempFile("/above/cwd", "prefix") - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(fmt.Sprint(dir, "/above/cwd/prefix"))) - g.Expect(f).To(BeNil()) - - f, err = fs.TempFile(os.TempDir(), "prefix") - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(filepath.Join(dir, os.TempDir(), "prefix"))) - g.Expect(f).To(BeNil()) -} - -func TestChroot(t *testing.T) { - g := NewWithT(t) - tmp := t.TempDir() - fs := New(tmp) - - f, err := fs.Chroot("test") - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(f).ToNot(BeNil()) - g.Expect(f.Root()).To(Equal(filepath.Join(tmp, "test"))) -} - -func TestRoot(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - root := fs.Root() - g.Expect(root).To(Equal(dir)) -} - -func TestReadLink(t *testing.T) { - tests := []struct { - name string - filename string - makeAbs bool - expected string - makeExpectedAbs bool - before func(dir string) billy.Filesystem - wantErr string - }{ - { - name: "symlink: pointing to abs outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - expected: "/etc/passwd", - }, - { - name: "file: rel pointing to abs above cwd", - filename: "../../file", - wantErr: "path outside working dir", - }, - { - name: "symlink: abs symlink pointing outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: true, - expected: "/etc/passwd", - }, - { - name: "symlink: dir pointing outside cwd", - before: func(dir string) billy.Filesystem { - cwd := filepath.Join(dir, "current-dir") - outside := filepath.Join(dir, "outside-cwd") - - os.Mkdir(cwd, 0o700) - os.Mkdir(outside, 0o700) - - os.Symlink(outside, filepath.Join(cwd, "symlink")) - os.WriteFile(filepath.Join(outside, "file"), []byte("anything"), 0o600) - - return New(cwd) - }, - filename: "current-dir/symlink/file", - makeAbs: true, - wantErr: "path outside working dir", - }, - { - name: "symlink: within cwd + workingDir symlink", - before: func(dir string) billy.Filesystem { - cwd := filepath.Join(dir, "symlink-dir") - cwdAlt := filepath.Join(dir, "symlink-altdir") - cwdTarget := filepath.Join(dir, "cwd-target") - - os.MkdirAll(cwdTarget, 0o700) - - os.WriteFile(filepath.Join(cwdTarget, "file"), []byte{}, 0o600) - os.Symlink(cwdTarget, cwd) - os.Symlink(cwdTarget, cwdAlt) - os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(cwdAlt, "symlink-file")) - return New(cwd) - }, - filename: "symlink-file", - expected: "cwd-target/file", - makeExpectedAbs: true, - }, - { - name: "symlink: outside cwd + workingDir symlink", - before: func(dir string) billy.Filesystem { - cwd := filepath.Join(dir, "symlink-dir") - outside := filepath.Join(cwd, "symlink-outside") - cwdTarget := filepath.Join(dir, "cwd-target") - outsideDir := filepath.Join(dir, "outside") - - os.Mkdir(cwdTarget, 0o700) - os.Mkdir(outsideDir, 0o700) - - os.WriteFile(filepath.Join(cwdTarget, "file"), []byte{}, 0o600) - os.Symlink(cwdTarget, cwd) - os.Symlink(outsideDir, outside) - os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(outside, "symlink-file")) - return New(cwd) - }, - filename: "symlink-outside/symlink-file", - wantErr: "path outside working dir", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - if tt.before != nil { - fs = tt.before(dir) - } - - filename := tt.filename - if tt.makeAbs { - filename = filepath.Join(dir, filename) - } - - expected := tt.expected - if tt.makeExpectedAbs { - expected = filepath.Join(dir, expected) - } - - got, err := fs.Readlink(filename) - if tt.wantErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantErr)) - g.Expect(got).To(BeEmpty()) - } else { - g.Expect(err).To(BeNil()) - g.Expect(got).To(Equal(expected)) - } - }) - } -} - -func TestLstat(t *testing.T) { - tests := []struct { - name string - filename string - makeAbs bool - before func(dir string) billy.Filesystem - wantErr string - }{ - { - name: "rel symlink: pointing to abs outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - }, - { - name: "rel symlink: pointing to rel path above cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - }, - { - name: "abs symlink: pointing to abs outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: true, - }, - { - name: "abs symlink: pointing to rel outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: false, - }, - { - name: "symlink: within cwd + workingDir symlink", - before: func(dir string) billy.Filesystem { - cwd := filepath.Join(dir, "symlink-dir") - cwdAlt := filepath.Join(dir, "symlink-altdir") - cwdTarget := filepath.Join(dir, "cwd-target") - - os.MkdirAll(cwdTarget, 0o700) - - os.WriteFile(filepath.Join(cwdTarget, "file"), []byte{}, 0o600) - os.Symlink(cwdTarget, cwd) - os.Symlink(cwdTarget, cwdAlt) - os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(cwdAlt, "symlink-file")) - return New(cwd) - }, - filename: "symlink-file", - makeAbs: false, - }, - { - name: "symlink: outside cwd + workingDir symlink", - before: func(dir string) billy.Filesystem { - cwd := filepath.Join(dir, "symlink-dir") - outside := filepath.Join(cwd, "symlink-outside") - cwdTarget := filepath.Join(dir, "cwd-target") - outsideDir := filepath.Join(dir, "outside") - - os.Mkdir(cwdTarget, 0o700) - os.Mkdir(outsideDir, 0o700) - - os.WriteFile(filepath.Join(cwdTarget, "file"), []byte{}, 0o600) - os.Symlink(cwdTarget, cwd) - os.Symlink(outsideDir, outside) - os.Symlink(filepath.Join(cwdTarget, "file"), filepath.Join(outside, "symlink-file")) - return New(cwd) - }, - filename: "symlink-outside/symlink-file", - makeAbs: false, - wantErr: "path outside working dir", - }, - { - name: "path: rel pointing to abs above cwd", - filename: "../../file", - wantErr: "path outside working dir", - }, - { - name: "path: abs pointing outside cwd", - filename: "/etc/passwd", - wantErr: "path outside working dir", - }, - { - name: "file: rel", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "test-file", - }, - { - name: "file: abs", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "test-file", - makeAbs: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - if tt.before != nil { - fs = tt.before(dir) - } - - filename := tt.filename - if tt.makeAbs { - filename = filepath.Join(dir, filename) - } - fi, err := fs.Lstat(filename) - if tt.wantErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantErr)) - g.Expect(fi).To(BeNil()) - } else { - g.Expect(err).To(BeNil()) - g.Expect(fi).ToNot(BeNil()) - g.Expect(fi.Name()).To(Equal(filepath.Base(tt.filename))) - } - }) - } -} - -func TestStat(t *testing.T) { - tests := []struct { - name string - filename string - makeAbs bool - before func(dir string) billy.Filesystem - wantErr string - }{ - { - name: "rel symlink: pointing to abs outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - wantErr: "/001/etc/passwd: no such file or directory", - }, - { - name: "rel symlink: pointing to rel path above cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - wantErr: "/001/etc/passwd: no such file or directory", - }, - - { - name: "abs symlink: pointing to abs outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: true, - wantErr: "/001/etc/passwd: no such file or directory", - }, - { - name: "abs symlink: pointing to rel outside cwd", - before: func(dir string) billy.Filesystem { - os.Symlink("../../../../../../../../etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: false, - wantErr: "/001/etc/passwd: no such file or directory", - }, - { - name: "path: rel pointing to abs above cwd", - filename: "../../file", - wantErr: "/001/file: no such file or directory", - }, - { - name: "path: abs pointing outside cwd", - filename: "/etc/passwd", - wantErr: "/001/etc/passwd: no such file or directory", - }, - { - name: "rel file", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "test-file", - }, - { - name: "abs file", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "test-file", - makeAbs: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - if tt.before != nil { - fs = tt.before(dir) - } - - filename := tt.filename - if tt.makeAbs { - filename = filepath.Join(dir, filename) - } - - fi, err := fs.Stat(filename) - if tt.wantErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantErr)) - g.Expect(fi).To(BeNil()) - } else { - g.Expect(err).To(BeNil()) - g.Expect(fi).ToNot(BeNil()) - } - }) - } -} - -func TestRemove(t *testing.T) { - tests := []struct { - name string - filename string - makeAbs bool - before func(dir string) billy.Filesystem - wantErr string - }{ - { - name: "path: rel pointing outside cwd w forward slash", - filename: "/some/path/outside/cwd", - wantErr: "/001/some/path/outside/cwd: no such file or directory", - }, - { - name: "path: rel pointing outside cwd", - filename: "../../../../path/outside/cwd", - wantErr: "/001/path/outside/cwd: no such file or directory", - }, - { - name: "parent with children", - before: func(dir string) billy.Filesystem { - os.MkdirAll(filepath.Join(dir, "parent/children"), 0o600) - return New(dir) - }, - filename: "parent", - }, - { - name: "inexistent dir", - before: func(dir string) billy.Filesystem { - return New(dir) - }, - filename: "inexistent", - wantErr: "inexistent: no such file or directory", - }, - { - name: "same dir file", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "test-file", - }, - { - name: "symlink: same dir", - before: func(dir string) billy.Filesystem { - target := filepath.Join(dir, "target-file") - os.WriteFile(target, []byte("anything"), 0o600) - os.Symlink(target, filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - }, - { - 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 New(dir) - }, - filename: "../../rel-above-cwd", - }, - { - name: "abs file", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "abs-test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "abs-test-file", - makeAbs: true, - }, - { - name: "abs symlink: pointing outside is forced to descend", - before: func(dir string) billy.Filesystem { - cwd := filepath.Join(dir, "current-dir") - outsideFile := filepath.Join(cwd, dir, "outside-cwd/file") - - os.Mkdir(cwd, 0o700) - os.MkdirAll(filepath.Dir(outsideFile), 0o700) - os.WriteFile(outsideFile, []byte("anything"), 0o600) - os.Symlink(outsideFile, filepath.Join(cwd, "remove-abs-symlink")) - return New(cwd) - }, - filename: "current-dir/remove-abs-symlink", - wantErr: "/001/current-dir/current-dir/remove-abs-symlink: no such file or directory", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - if tt.before != nil { - fs = tt.before(dir) - } - - filename := tt.filename - if tt.makeAbs { - filename = filepath.Join(dir, filename) - } - - err := fs.Remove(filename) - if tt.wantErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantErr)) - } else { - g.Expect(err).To(BeNil()) - } - }) - } -} - -func TestRemoveAll(t *testing.T) { - tests := []struct { - name string - filename string - makeAbs bool - before func(dir string) billy.Filesystem - wantErr string - }{ - { - name: "parent with children", - before: func(dir string) billy.Filesystem { - os.MkdirAll(filepath.Join(dir, "parent/children"), 0o600) - return New(dir) - }, - filename: "parent", - }, - { - name: "inexistent dir", - filename: "inexistent", - }, - { - name: "same dir file", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "test-file", - }, - { - name: "same dir symlink", - before: func(dir string) billy.Filesystem { - target := filepath.Join(dir, "target-file") - os.WriteFile(target, []byte("anything"), 0o600) - os.Symlink(target, filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - }, - { - 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 New(dir) - }, - filename: "../../rel-above-cwd", - }, - { - name: "abs file", - before: func(dir string) billy.Filesystem { - os.WriteFile(filepath.Join(dir, "abs-test-file"), []byte("anything"), 0o600) - return New(dir) - }, - filename: "abs-test-file", - makeAbs: true, - }, - { - name: "abs symlink", - before: func(dir string) billy.Filesystem { - os.Symlink("/etc/passwd", filepath.Join(dir, "symlink")) - return New(dir) - }, - filename: "symlink", - makeAbs: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir).(*OS) - - if tt.before != nil { - fs = tt.before(dir).(*OS) - } - - filename := tt.filename - if tt.makeAbs { - filename = filepath.Join(dir, filename) - } - - err := fs.RemoveAll(filename) - if tt.wantErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantErr)) - } else { - g.Expect(err).To(BeNil()) - } - }) - } -} - -func TestJoin(t *testing.T) { - tests := []struct { - elems []string - wanted string - }{ - { - elems: []string{}, - wanted: "", - }, - { - elems: []string{"/a", "b", "c"}, - wanted: "/a/b/c", - }, - { - elems: []string{"/a", "b/c"}, - wanted: "/a/b/c", - }, - { - elems: []string{"/a", ""}, - wanted: "/a", - }, - { - elems: []string{"/a", "/", "b"}, - wanted: "/a/b", - }, - } - for _, tt := range tests { - t.Run(tt.wanted, func(t *testing.T) { - g := NewWithT(t) - fs := New(t.TempDir()) - - got := fs.Join(tt.elems...) - g.Expect(got).To(Equal(tt.wanted)) - }) - } -} - -func TestAbs(t *testing.T) { - tests := []struct { - name string - cwd string - filename string - makeAbs bool - expected string - makeExpectedAbs bool - wantErr string - before func(dir string) - }{ - { - name: "path: same dir rel file", - cwd: "/working/dir", - filename: "./file", - expected: "/working/dir/file", - }, - { - name: "path: descending rel file", - cwd: "/working/dir", - filename: "file", - expected: "/working/dir/file", - }, - { - name: "path: ascending rel file 1", - cwd: "/working/dir", - filename: "../file", - expected: "/working/dir/file", - }, - { - name: "path: ascending rel file 2", - cwd: "/working/dir", - filename: "../../file", - expected: "/working/dir/file", - }, - { - name: "path: ascending rel file 3", - cwd: "/working/dir", - filename: "/../../file", - expected: "/working/dir/file", - }, - { - name: "path: abs file within cwd", - cwd: "/working/dir", - filename: "/working/dir/abs-file", - expected: "/working/dir/abs-file", - }, - { - name: "path: abs file within cwd", - cwd: "/working/dir", - filename: "/outside/dir/abs-file", - expected: "/working/dir/outside/dir/abs-file", - }, - { - name: "abs symlink: within cwd w abs descending target", - filename: "ln-cwd-cwd", - makeAbs: true, - expected: "within-cwd", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink(filepath.Join(dir, "within-cwd"), filepath.Join(dir, "ln-cwd-cwd")) - }, - }, - { - name: "abs symlink: within cwd w rel descending target", - filename: "ln-rel-cwd-cwd", - makeAbs: true, - expected: "within-cwd", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink("within-cwd", filepath.Join(dir, "ln-rel-cwd-cwd")) - }, - }, - { - name: "abs symlink: within cwd w abs ascending target", - filename: "ln-cwd-up", - makeAbs: true, - expected: "/some/outside/dir", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink("/some/outside/dir", filepath.Join(dir, "ln-cwd-up")) - }, - }, - { - name: "abs symlink within cwd w rel ascending target", - filename: "ln-rel-cwd-up", - makeAbs: true, - expected: "outside-cwd", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink("../../outside-cwd", filepath.Join(dir, "ln-rel-cwd-up")) - }, - }, - { - name: "rel symlink: within cwd w abs descending target", - filename: "ln-cwd-cwd", - expected: "within-cwd", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink(filepath.Join(dir, "within-cwd"), filepath.Join(dir, "ln-cwd-cwd")) - }, - }, - { - name: "rel symlink: within cwd w rel descending target", - filename: "ln-rel-cwd-cwd2", - expected: "within-cwd", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink("within-cwd", filepath.Join(dir, "ln-rel-cwd-cwd2")) - }, - }, - { - name: "rel symlink: within cwd w abs ascending target", - filename: "ln-cwd-up2", - expected: "/outside/path/up", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink("/outside/path/up", filepath.Join(dir, "ln-cwd-up2")) - }, - }, - { - name: "rel symlink: within cwd w rel ascending target", - filename: "ln-rel-cwd-up2", - expected: "outside", - makeExpectedAbs: true, - before: func(dir string) { - os.Symlink("../../../../outside", filepath.Join(dir, "ln-rel-cwd-up2")) - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - cwd := tt.cwd - if cwd == "" { - cwd = t.TempDir() - } - - fs := New(cwd).(*OS) - if tt.before != nil { - tt.before(cwd) - } - - filename := tt.filename - if tt.makeAbs { - filename = filepath.Join(cwd, filename) - } - - expected := tt.expected - if tt.makeExpectedAbs { - expected = filepath.Join(cwd, expected) - } - - got, err := fs.abs(filename) - if tt.wantErr != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.wantErr)) - } else { - g.Expect(err).ToNot(HaveOccurred()) - } - - g.Expect(got).To(Equal(expected)) - }) - } -} - -func TestReadDir(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - f, err := os.Create(filepath.Join(dir, "file1")) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(f).ToNot(BeNil()) - - f, err = os.Create(filepath.Join(dir, "file2")) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(f).ToNot(BeNil()) - - dirs, err := fs.ReadDir(dir) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dirs).ToNot(BeNil()) - g.Expect(dirs).To(HaveLen(2)) - - dirs, err = fs.ReadDir(".") - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dirs).ToNot(BeNil()) - g.Expect(dirs).To(HaveLen(2)) - - os.Symlink("/some/path/outside/cwd", filepath.Join(dir, "symlink")) - dirs, err = fs.ReadDir("symlink") - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(dir + "/some/path/outside/cwd: no such file or directory")) - g.Expect(dirs).To(BeNil()) -} - -func TestMkdirAll(t *testing.T) { - g := NewWithT(t) - root := t.TempDir() - cwd := filepath.Join(root, "cwd") - target := "abc" - targetAbs := filepath.Join(cwd, target) - fs := New(cwd) - - // Even if CWD is changed outside of fs the instance, - // the current working dir must still be observed. - err := os.Chdir(os.TempDir()) - g.Expect(err).ToNot(HaveOccurred()) - - err = fs.MkdirAll(target, 0o700) - g.Expect(err).ToNot(HaveOccurred()) - - fi, err := os.Stat(targetAbs) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(fi).ToNot(BeNil()) - - os.Mkdir(filepath.Join(root, "outside"), 0o700) - os.Symlink(filepath.Join(root, "outside"), filepath.Join(cwd, "symlink")) - err = fs.MkdirAll(filepath.Join(cwd, "symlink/new-dir"), 0o700) - g.Expect(err).ToNot(HaveOccurred()) - - mustExist(filepath.Join(cwd, filepath.Join(cwd, "../outside/new-dir"))) -} - -func TestRename(t *testing.T) { - g := NewWithT(t) - dir := t.TempDir() - fs := New(dir) - - oldFile := "old-file" - newFile := "newdir/newfile" - - // Even if CWD is changed outside of fs the instance, - // the current working dir must still be observed. - err := os.Chdir(os.TempDir()) - g.Expect(err).ToNot(HaveOccurred()) - - _, err = fs.Create(oldFile) - g.Expect(err).ToNot(HaveOccurred()) - - err = fs.Rename(oldFile, newFile) - g.Expect(err).ToNot(HaveOccurred()) - - fi, err := os.Stat(filepath.Join(dir, newFile)) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(fi).ToNot(BeNil()) - - err = fs.Rename("/tmp/outside/cwd/file1", newFile) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("newdir/newfile: no such file or directory")) - - err = fs.Rename(oldFile, "/tmp/outside/cwd/file2") - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("outside/cwd/file2: no such file or directory")) -} - -func mustExist(filename string) { - fi, err := os.Stat(filename) - if err != nil || fi == nil { - panic(fmt.Sprintf("file %s should exist", filename)) - } -} diff --git a/git/gogit/fs/osfs_unsupported.go b/git/gogit/fs/osfs_unsupported.go deleted file mode 100644 index 36f9ce92a..000000000 --- a/git/gogit/fs/osfs_unsupported.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build !linux -// +build !linux - -/* -Copyright 2017 Go-Git authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Copyright 2022 The Flux authors. All rights reserved. -// Adapted from: github.com/go-git/go-billy/v5/osfs - -package fs - -func (f *file) Lock() error { - return nil -} - -func (f *file) Unlock() error { - return nil -} diff --git a/git/gogit/go.mod b/git/gogit/go.mod index 8e88c3e3a..6365227d9 100644 --- a/git/gogit/go.mod +++ b/git/gogit/go.mod @@ -12,29 +12,28 @@ replace ( require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 - github.com/cyphar/filepath-securejoin v0.2.3 - github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 + github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a github.com/fluxcd/gitkit v0.6.0 github.com/fluxcd/pkg/git v0.13.0 github.com/fluxcd/pkg/gittestserver v0.8.6 github.com/fluxcd/pkg/ssh v0.8.2 github.com/fluxcd/pkg/version v0.2.2 - github.com/go-git/go-billy/v5 v5.4.1 - github.com/go-git/go-git/v5 v5.8.1 + github.com/go-git/go-billy/v5 v5.5.0 + github.com/go-git/go-git/v5 v5.9.0 github.com/onsi/gomega v1.27.10 - golang.org/x/crypto v0.12.0 - golang.org/x/sys v0.11.0 + golang.org/x/crypto v0.13.0 ) require ( dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/cloudflare/circl v1.3.3 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/gofrs/uuid v4.2.0+incompatible // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -43,10 +42,11 @@ require ( github.com/sergi/go-diff v1.3.1 // indirect github.com/skeema/knownhosts v1.2.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/text v0.12.0 // indirect - golang.org/x/tools v0.9.3 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.13.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/git/gogit/go.sum b/git/gogit/go.sum index acc6987e5..5c6f7e200 100644 --- a/git/gogit/go.sum +++ b/git/gogit/go.sum @@ -5,8 +5,8 @@ github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYr github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= @@ -15,14 +15,13 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 h1:1L0aalTpPz7YlMxETKpmQoWMBkeiuorElZIXoNmgiPE= -github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -32,15 +31,15 @@ github.com/fluxcd/gitkit v0.6.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihn github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= -github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= -github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= +github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= +github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= 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/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -51,15 +50,12 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 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= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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= @@ -70,7 +66,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -88,12 +84,12 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -101,12 +97,12 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -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/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -120,14 +116,14 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -135,19 +131,18 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +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.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=