mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Add more coverage for loader and strengthen type safety.
This commit is contained in:
58
pkg/fs/confirmeddir.go
Normal file
58
pkg/fs/confirmeddir.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfirmedDir is a clean, absolute, delinkified path
|
||||||
|
// that was confirmed to point to an existing directory.
|
||||||
|
type ConfirmedDir string
|
||||||
|
|
||||||
|
// HasPrefix returns true if the directory argument
|
||||||
|
// is a prefix of self (d) from the point of view of
|
||||||
|
// a file system.
|
||||||
|
//
|
||||||
|
// I.e., it's true if the argument equals or contains
|
||||||
|
// self (d) in a file path sense.
|
||||||
|
//
|
||||||
|
// HasPrefix emulates the semantics of strings.HasPrefix
|
||||||
|
// such that the following are true:
|
||||||
|
//
|
||||||
|
// strings.HasPrefix("foobar", "foobar")
|
||||||
|
// strings.HasPrefix("foobar", "foo")
|
||||||
|
// strings.HasPrefix("foobar", "")
|
||||||
|
//
|
||||||
|
// d := fSys.ConfirmDir("/foo/bar")
|
||||||
|
// d.HasPrefix("/foo/bar")
|
||||||
|
// d.HasPrefix("/foo")
|
||||||
|
// d.HasPrefix("/")
|
||||||
|
//
|
||||||
|
// Not contacting a file system here to check for
|
||||||
|
// actual path existence.
|
||||||
|
//
|
||||||
|
// This is tested on linux, but will have trouble
|
||||||
|
// on other operating systems. As soon as related
|
||||||
|
// work is completed in the core filepath package,
|
||||||
|
// this code should be refactored to use it.
|
||||||
|
// See:
|
||||||
|
// https://github.com/golang/go/issues/18355
|
||||||
|
// https://github.com/golang/dep/issues/296
|
||||||
|
// https://github.com/golang/dep/blob/master/internal/fs/fs.go#L33
|
||||||
|
// https://codereview.appspot.com/5712045
|
||||||
|
func (d ConfirmedDir) HasPrefix(path ConfirmedDir) bool {
|
||||||
|
if path.String() == string(filepath.Separator) || path == d {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return strings.HasPrefix(
|
||||||
|
string(d),
|
||||||
|
string(path)+string(filepath.Separator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d ConfirmedDir) Join(path string) string {
|
||||||
|
return filepath.Join(string(d), path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d ConfirmedDir) String() string {
|
||||||
|
return string(d)
|
||||||
|
}
|
||||||
87
pkg/fs/confirmeddir_test.go
Normal file
87
pkg/fs/confirmeddir_test.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJoin(t *testing.T) {
|
||||||
|
fSys := MakeFakeFS()
|
||||||
|
err := fSys.Mkdir("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
d, f, err := fSys.CleanedAbs("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
|
t.Fatalf("unexpected file: %v", f)
|
||||||
|
}
|
||||||
|
if d.Join("bar") != "/foo/bar" {
|
||||||
|
t.Fatalf("expected join %s", d.Join("bar"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix_Slash(t *testing.T) {
|
||||||
|
d, f, err := MakeFakeFS().CleanedAbs("/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
|
t.Fatalf("unexpected file: %v", f)
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/hey") {
|
||||||
|
t.Fatalf("should be false")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/") {
|
||||||
|
t.Fatalf("/ should have the prefix /")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix_SlashFoo(t *testing.T) {
|
||||||
|
fSys := MakeFakeFS()
|
||||||
|
err := fSys.Mkdir("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
d, _, err := fSys.CleanedAbs("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/fo") {
|
||||||
|
t.Fatalf("/foo does not have path prefix /fo")
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/fod") {
|
||||||
|
t.Fatalf("/foo does not have path prefix /fod")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/foo") {
|
||||||
|
t.Fatalf("/foo should have prefix /foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix_SlashFooBar(t *testing.T) {
|
||||||
|
fSys := MakeFakeFS()
|
||||||
|
err := fSys.MkdirAll("/foo/bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
d, _, err := fSys.CleanedAbs("/foo/bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/fo") {
|
||||||
|
t.Fatalf("/foo/bar does not have path prefix /fo")
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/foobar") {
|
||||||
|
t.Fatalf("/foo/bar does not have path prefix /foobar")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/foo/bar") {
|
||||||
|
t.Fatalf("/foo/bar should have prefix /foo/bar")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/foo") {
|
||||||
|
t.Fatalf("/foo/bar should have prefix /foo")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/") {
|
||||||
|
t.Fatalf("/foo/bar should have prefix /")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,16 +102,16 @@ func (fs *fakeFs) Open(name string) (File, error) {
|
|||||||
return fs.m[name], nil
|
return fs.m[name], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanedAbs does nothing and cannot fail.
|
// CleanedAbs cannot fail.
|
||||||
func (fs *fakeFs) CleanedAbs(path string) (string, string, error) {
|
func (fs *fakeFs) CleanedAbs(path string) (ConfirmedDir, string, error) {
|
||||||
if fs.IsDir(path) {
|
if fs.IsDir(path) {
|
||||||
return path, "", nil
|
return ConfirmedDir(path), "", nil
|
||||||
}
|
}
|
||||||
d := filepath.Dir(path)
|
d := filepath.Dir(path)
|
||||||
if d == path {
|
if d == path {
|
||||||
return d, "", nil
|
return ConfirmedDir(d), "", nil
|
||||||
}
|
}
|
||||||
return d, filepath.Base(path), nil
|
return ConfirmedDir(d), filepath.Base(path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exists returns true if file is known.
|
// Exists returns true if file is known.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type FileSystem interface {
|
|||||||
RemoveAll(name string) error
|
RemoveAll(name string) error
|
||||||
Open(name string) (File, error)
|
Open(name string) (File, error)
|
||||||
IsDir(name string) bool
|
IsDir(name string) bool
|
||||||
CleanedAbs(path string) (string, string, error)
|
CleanedAbs(path string) (ConfirmedDir, string, error)
|
||||||
Exists(name string) bool
|
Exists(name string) bool
|
||||||
Glob(pattern string) ([]string, error)
|
Glob(pattern string) ([]string, error)
|
||||||
ReadFile(name string) ([]byte, error)
|
ReadFile(name string) ([]byte, error)
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ func (realFS) Open(name string) (File, error) { return os.Open(name) }
|
|||||||
// and file components. If the entire path is
|
// and file components. If the entire path is
|
||||||
// a directory, the file component is an empty
|
// a directory, the file component is an empty
|
||||||
// string.
|
// string.
|
||||||
func (x realFS) CleanedAbs(path string) (string, string, error) {
|
func (x realFS) CleanedAbs(
|
||||||
|
path string) (ConfirmedDir, string, error) {
|
||||||
absRoot, err := filepath.Abs(path)
|
absRoot, err := filepath.Abs(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf(
|
return "", "", fmt.Errorf(
|
||||||
@@ -72,7 +73,7 @@ func (x realFS) CleanedAbs(path string) (string, string, error) {
|
|||||||
"evalsymlink failure on '%s' : %v", path, err)
|
"evalsymlink failure on '%s' : %v", path, err)
|
||||||
}
|
}
|
||||||
if x.IsDir(deLinked) {
|
if x.IsDir(deLinked) {
|
||||||
return deLinked, "", nil
|
return ConfirmedDir(deLinked), "", nil
|
||||||
}
|
}
|
||||||
d := filepath.Dir(deLinked)
|
d := filepath.Dir(deLinked)
|
||||||
if !x.IsDir(d) {
|
if !x.IsDir(d) {
|
||||||
@@ -89,7 +90,7 @@ func (x realFS) CleanedAbs(path string) (string, string, error) {
|
|||||||
log.Fatalf("these should be equal: '%s', '%s'",
|
log.Fatalf("these should be equal: '%s', '%s'",
|
||||||
filepath.Join(d, f), deLinked)
|
filepath.Join(d, f), deLinked)
|
||||||
}
|
}
|
||||||
return d, f, nil
|
return ConfirmedDir(d), f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exists returns true if os.Stat succeeds.
|
// Exists returns true if os.Stat succeeds.
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func TestCleanedAbs_1(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err=%v", err)
|
t.Fatalf("unexpected err=%v", err)
|
||||||
}
|
}
|
||||||
if d != wd {
|
if d.String() != wd {
|
||||||
t.Fatalf("unexpected d=%s", d)
|
t.Fatalf("unexpected d=%s", d)
|
||||||
}
|
}
|
||||||
if f != "" {
|
if f != "" {
|
||||||
@@ -90,7 +90,7 @@ func TestCleanedAbs_3(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err=%v", err)
|
t.Fatalf("unexpected err=%v", err)
|
||||||
}
|
}
|
||||||
if d != testDir {
|
if d.String() != testDir {
|
||||||
t.Fatalf("unexpected d=%s", d)
|
t.Fatalf("unexpected d=%s", d)
|
||||||
}
|
}
|
||||||
if f != "foo" {
|
if f != "foo" {
|
||||||
@@ -119,7 +119,7 @@ func TestCleanedAbs_4(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err=%v", err)
|
t.Fatalf("unexpected err=%v", err)
|
||||||
}
|
}
|
||||||
if d != filepath.Join(testDir, "d1", "d2") {
|
if d.String() != filepath.Join(testDir, "d1", "d2") {
|
||||||
t.Fatalf("unexpected d=%s", d)
|
t.Fatalf("unexpected d=%s", d)
|
||||||
}
|
}
|
||||||
if f != "" {
|
if f != "" {
|
||||||
@@ -131,7 +131,7 @@ func TestCleanedAbs_4(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err=%v", err)
|
t.Fatalf("unexpected err=%v", err)
|
||||||
}
|
}
|
||||||
if d != filepath.Join(testDir, "d1", "d2") {
|
if d.String() != filepath.Join(testDir, "d1", "d2") {
|
||||||
t.Fatalf("unexpected d=%s", d)
|
t.Fatalf("unexpected d=%s", d)
|
||||||
}
|
}
|
||||||
if f != "bar" {
|
if f != "bar" {
|
||||||
|
|||||||
@@ -78,10 +78,16 @@ import (
|
|||||||
// from /etc/passwd will fail.
|
// from /etc/passwd will fail.
|
||||||
//
|
//
|
||||||
type fileLoader struct {
|
type fileLoader struct {
|
||||||
// Previously visited absolute directory paths.
|
// Loader that spawned this loader.
|
||||||
// Tracked to avoid cycles.
|
// Used to avoid cycles.
|
||||||
// The last entry is the current root.
|
referrer *fileLoader
|
||||||
roots []string
|
// An absolute, cleaned path to a directory.
|
||||||
|
// The Load function reads from this directory,
|
||||||
|
// or directories below it.
|
||||||
|
root fs.ConfirmedDir
|
||||||
|
// URI, if any, used for a download into root.
|
||||||
|
// TODO(monopole): use non-string type.
|
||||||
|
uri string
|
||||||
// File system utilities.
|
// File system utilities.
|
||||||
fSys fs.FileSystem
|
fSys fs.FileSystem
|
||||||
// Used to clone repositories.
|
// Used to clone repositories.
|
||||||
@@ -103,12 +109,12 @@ func NewFileLoaderAtRoot(fSys fs.FileSystem) *fileLoader {
|
|||||||
// Root returns the absolute path that is prepended to any
|
// Root returns the absolute path that is prepended to any
|
||||||
// relative paths used in Load.
|
// relative paths used in Load.
|
||||||
func (l *fileLoader) Root() string {
|
func (l *fileLoader) Root() string {
|
||||||
return l.roots[len(l.roots)-1]
|
return l.root.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLoaderOrDie(fSys fs.FileSystem, path string) *fileLoader {
|
func newLoaderOrDie(fSys fs.FileSystem, path string) *fileLoader {
|
||||||
l, err := newFileLoaderAt(
|
l, err := newFileLoaderAt(
|
||||||
path, fSys, []string{}, simpleGitCloner)
|
path, fSys, nil, simpleGitCloner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to make loader at '%s'; %v", path, err)
|
log.Fatalf("unable to make loader at '%s'; %v", path, err)
|
||||||
}
|
}
|
||||||
@@ -117,31 +123,30 @@ func newLoaderOrDie(fSys fs.FileSystem, path string) *fileLoader {
|
|||||||
|
|
||||||
// newFileLoaderAt returns a new fileLoader with given root.
|
// newFileLoaderAt returns a new fileLoader with given root.
|
||||||
func newFileLoaderAt(
|
func newFileLoaderAt(
|
||||||
root string, fSys fs.FileSystem,
|
possibleRoot string, fSys fs.FileSystem,
|
||||||
roots []string, cloner gitCloner) (*fileLoader, error) {
|
referrer *fileLoader, cloner gitCloner) (*fileLoader, error) {
|
||||||
if root == "" {
|
if possibleRoot == "" {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"loader root cannot be empty")
|
"loader root cannot be empty")
|
||||||
}
|
}
|
||||||
absRoot, f, err := fSys.CleanedAbs(root)
|
root, f, err := fSys.CleanedAbs(possibleRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"absolute path error in '%s' : %v", root, err)
|
"absolute path error in '%s' : %v", possibleRoot, err)
|
||||||
}
|
}
|
||||||
if f != "" {
|
if f != "" {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"got file '%s', but '%s' must be a directory to be a root",
|
"got file '%s', but '%s' must be a directory to be a root",
|
||||||
f, root)
|
f, possibleRoot)
|
||||||
}
|
}
|
||||||
if err := isPathEqualToOrAbove(absRoot, roots); err != nil {
|
if referrer != nil {
|
||||||
|
if err := referrer.errIfArgEqualOrHigher(root); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !fSys.IsDir(absRoot) {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"'%s' does not exist or is not a directory", absRoot)
|
|
||||||
}
|
}
|
||||||
return &fileLoader{
|
return &fileLoader{
|
||||||
roots: append(roots, absRoot),
|
root: root,
|
||||||
|
referrer: referrer,
|
||||||
fSys: fSys,
|
fSys: fSys,
|
||||||
cloner: cloner,
|
cloner: cloner,
|
||||||
cleaner: func() error { return nil },
|
cleaner: func() error { return nil },
|
||||||
@@ -155,68 +160,82 @@ func (l *fileLoader) New(path string) (ifc.Loader, error) {
|
|||||||
return nil, fmt.Errorf("new root cannot be empty")
|
return nil, fmt.Errorf("new root cannot be empty")
|
||||||
}
|
}
|
||||||
if isRepoUrl(path) {
|
if isRepoUrl(path) {
|
||||||
// This works well enough for purpose at hand to detect
|
// Avoid cycles.
|
||||||
// previously visited URLs and thus avoid cycles.
|
if err := l.errIfPreviouslySeenUri(path); err != nil {
|
||||||
if err := isPathEqualToOrAbove(path, l.roots); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newGitLoader(path, l.fSys, l.roots, l.cloner)
|
return newGitLoader(path, l.fSys, l.referrer, l.cloner)
|
||||||
}
|
}
|
||||||
if filepath.IsAbs(path) {
|
if filepath.IsAbs(path) {
|
||||||
return nil, fmt.Errorf("new root '%s' cannot be absolute", path)
|
return nil, fmt.Errorf("new root '%s' cannot be absolute", path)
|
||||||
}
|
}
|
||||||
return newFileLoaderAt(
|
return newFileLoaderAt(
|
||||||
filepath.Join(l.Root(), path), l.fSys, l.roots, l.cloner)
|
l.root.Join(path), l.fSys, l, l.cloner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newGitLoader returns a new Loader pinned to a temporary
|
// newGitLoader returns a new Loader pinned to a temporary
|
||||||
// directory holding a cloned git repo.
|
// directory holding a cloned git repo.
|
||||||
func newGitLoader(
|
func newGitLoader(
|
||||||
root string, fSys fs.FileSystem,
|
uri string, fSys fs.FileSystem,
|
||||||
roots []string, cloner gitCloner) (ifc.Loader, error) {
|
referrer *fileLoader, cloner gitCloner) (ifc.Loader, error) {
|
||||||
tmpDirForRepo, pathInRepo, err := cloner(root)
|
tmpDirForRepo, pathInRepo, err := cloner(uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
trueRoot := filepath.Join(tmpDirForRepo, pathInRepo)
|
root, f, err := fSys.CleanedAbs(
|
||||||
if !fSys.IsDir(trueRoot) {
|
filepath.Join(tmpDirForRepo, pathInRepo))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"something wrong cloning '%s'; unable to find '%s'",
|
"'%s' refers to file '%s'; expecting directory", pathInRepo, f)
|
||||||
root, trueRoot)
|
|
||||||
}
|
}
|
||||||
return &fileLoader{
|
return &fileLoader{
|
||||||
roots: append(roots, root, trueRoot),
|
root: root,
|
||||||
|
referrer: referrer,
|
||||||
|
uri: uri,
|
||||||
fSys: fSys,
|
fSys: fSys,
|
||||||
cloner: cloner,
|
cloner: cloner,
|
||||||
cleaner: func() error { return fSys.RemoveAll(tmpDirForRepo) },
|
cleaner: func() error { return fSys.RemoveAll(tmpDirForRepo) },
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPathEqualToOrAbove tests whether the 1st argument,
|
// errIfArgEqualOrHigher tests whether the argument,
|
||||||
// viewed as a path to a directory, is equal to or above
|
// is equal to or above the root of any ancestor.
|
||||||
// any of the paths in the 2nd argument. It's assumed
|
func (l *fileLoader) errIfArgEqualOrHigher(
|
||||||
// that all paths are cleaned, delinkified and absolute.
|
candidateRoot fs.ConfirmedDir) error {
|
||||||
func isPathEqualToOrAbove(path string, roots []string) error {
|
if l.root.HasPrefix(candidateRoot) {
|
||||||
terminated := path + string(filepath.Separator)
|
|
||||||
for _, r := range roots {
|
|
||||||
if r == path || strings.HasPrefix(r, terminated) {
|
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"cycle detected: new root '%s' contains previous root '%s'",
|
"cycle detected: candidate root '%s' contains visited root '%s'",
|
||||||
path, r)
|
candidateRoot, l.root)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if l.referrer == nil {
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
return l.referrer.errIfArgEqualOrHigher(candidateRoot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *fileLoader) errIfPreviouslySeenUri(uri string) error {
|
||||||
|
if strings.HasPrefix(l.uri, uri) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"cycle detected: URI '%s' referenced by previous URI '%s'",
|
||||||
|
uri, l.uri)
|
||||||
|
}
|
||||||
|
if l.referrer == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.referrer.errIfPreviouslySeenUri(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load returns content of file at the given relative path,
|
// Load returns content of file at the given relative path,
|
||||||
// else an error. The path must refer to a file in or
|
// else an error. The path must refer to a file in or
|
||||||
// below the current Root().
|
// below the current root.
|
||||||
func (l *fileLoader) Load(path string) ([]byte, error) {
|
func (l *fileLoader) Load(path string) ([]byte, error) {
|
||||||
if filepath.IsAbs(path) {
|
if filepath.IsAbs(path) {
|
||||||
return nil, l.loadOutOfBounds(path)
|
return nil, l.loadOutOfBounds(path)
|
||||||
}
|
}
|
||||||
d, f, err := l.fSys.CleanedAbs(
|
d, f, err := l.fSys.CleanedAbs(l.root.Join(path))
|
||||||
filepath.Join(l.Root(), path))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -224,41 +243,16 @@ func (l *fileLoader) Load(path string) ([]byte, error) {
|
|||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"'%s' must be a file (got d='%s')", path, d)
|
"'%s' must be a file (got d='%s')", path, d)
|
||||||
}
|
}
|
||||||
path = filepath.Join(d, f)
|
if !d.HasPrefix(l.root) {
|
||||||
if !l.isInOrBelowRoot(path) {
|
|
||||||
return nil, l.loadOutOfBounds(path)
|
return nil, l.loadOutOfBounds(path)
|
||||||
}
|
}
|
||||||
return l.fSys.ReadFile(path)
|
return l.fSys.ReadFile(d.Join(f))
|
||||||
}
|
|
||||||
|
|
||||||
// isInOrBelowRoot is true if the argument is in or
|
|
||||||
// below Root() from purely a path perspective (no
|
|
||||||
// check for actual file existence). For this to work,
|
|
||||||
// both the given argument "path" and l.Root() must
|
|
||||||
// be cleaned, absolute paths, and l.Root() must be
|
|
||||||
// a directory. Both conditions enforced elsewhere.
|
|
||||||
//
|
|
||||||
// This is tested on linux, but will have trouble
|
|
||||||
// on other operating systems. As soon as related
|
|
||||||
// work is completed in the core filepath package,
|
|
||||||
// this code should be refactored to use it.
|
|
||||||
// See:
|
|
||||||
// https://github.com/golang/go/issues/18355
|
|
||||||
// https://github.com/golang/dep/issues/296
|
|
||||||
// https://github.com/golang/dep/blob/master/internal/fs/fs.go#L33
|
|
||||||
// https://codereview.appspot.com/5712045
|
|
||||||
func (l *fileLoader) isInOrBelowRoot(path string) bool {
|
|
||||||
if l.Root() == string(filepath.Separator) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return strings.HasPrefix(
|
|
||||||
path, l.Root()+string(filepath.Separator))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *fileLoader) loadOutOfBounds(path string) error {
|
func (l *fileLoader) loadOutOfBounds(path string) error {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"security; file '%s' is not in or below '%s'",
|
"security; file '%s' is not in or below '%s'",
|
||||||
path, l.Root())
|
path, l.root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup runs the cleaner.
|
// Cleanup runs the cleaner.
|
||||||
|
|||||||
@@ -63,15 +63,15 @@ func MakeFakeFs(td []testData) fs.FileSystem {
|
|||||||
|
|
||||||
func TestNewFileLoaderAt_DemandsDirectory(t *testing.T) {
|
func TestNewFileLoaderAt_DemandsDirectory(t *testing.T) {
|
||||||
fSys := MakeFakeFs(testCases)
|
fSys := MakeFakeFs(testCases)
|
||||||
_, err := newFileLoaderAt("/foo", fSys, []string{}, nil)
|
_, err := newFileLoaderAt("/foo", fSys, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error - a directory should work.")
|
t.Fatalf("Unexpected error - a directory should work.")
|
||||||
}
|
}
|
||||||
_, err = newFileLoaderAt("/foo/project", fSys, []string{}, nil)
|
_, err = newFileLoaderAt("/foo/project", fSys, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error - a directory should work.")
|
t.Fatalf("Unexpected error - a directory should work.")
|
||||||
}
|
}
|
||||||
_, err = newFileLoaderAt("/foo/project/fileA.yaml", fSys, []string{}, nil)
|
_, err = newFileLoaderAt("/foo/project/fileA.yaml", fSys, nil, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected error - a file should not work.")
|
t.Fatalf("Expected error - a file should not work.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func TestGitLoader(t *testing.T) {
|
|||||||
whatever
|
whatever
|
||||||
`))
|
`))
|
||||||
l, err := newGitLoader(
|
l, err := newGitLoader(
|
||||||
url, fSys, []string{},
|
url, fSys, nil,
|
||||||
makeFakeGitCloner(t, fSys, coRoot))
|
makeFakeGitCloner(t, fSys, coRoot))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v\n", err)
|
t.Fatalf("unexpected err: %v\n", err)
|
||||||
@@ -177,10 +177,10 @@ whatever
|
|||||||
coRoot+"/"+pathInRepo, l.Root())
|
coRoot+"/"+pathInRepo, l.Root())
|
||||||
}
|
}
|
||||||
if _, err = l.New(url); err == nil {
|
if _, err = l.New(url); err == nil {
|
||||||
t.Fatalf("expected cycle error")
|
t.Fatalf("expected cycle error 1")
|
||||||
}
|
}
|
||||||
if _, err = l.New(rootUrl + "/" + "foo"); err == nil {
|
if _, err = l.New(rootUrl + "/" + "foo"); err == nil {
|
||||||
t.Fatalf("expected cycle error")
|
t.Fatalf("expected cycle error 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
pathInRepo = "foo/overlay"
|
pathInRepo = "foo/overlay"
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import (
|
|||||||
func NewLoader(root string, fSys fs.FileSystem) (ifc.Loader, error) {
|
func NewLoader(root string, fSys fs.FileSystem) (ifc.Loader, error) {
|
||||||
if isRepoUrl(root) {
|
if isRepoUrl(root) {
|
||||||
return newGitLoader(
|
return newGitLoader(
|
||||||
root, fSys, []string{}, simpleGitCloner)
|
root, fSys, nil, simpleGitCloner)
|
||||||
}
|
}
|
||||||
return newFileLoaderAt(
|
return newFileLoaderAt(
|
||||||
root, fSys, []string{}, simpleGitCloner)
|
root, fSys, nil, simpleGitCloner)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user