From 25f54ee80e509826d2dea4d702ec46f623099929 Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Fri, 8 Apr 2022 00:17:28 +0200 Subject: [PATCH] sympath: provide abs path after eval symlink This can be used to detect traversion outside of a certain path scope while walking. Signed-off-by: Hidde Beydals --- internal/helm/chart/loader/sympath/walk.go | 32 +++++++----- .../helm/chart/loader/sympath/walk_test.go | 49 +++++++++++-------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/internal/helm/chart/loader/sympath/walk.go b/internal/helm/chart/loader/sympath/walk.go index 752526fe..af0e1a15 100644 --- a/internal/helm/chart/loader/sympath/walk.go +++ b/internal/helm/chart/loader/sympath/walk.go @@ -5,6 +5,7 @@ provided under the BSD license. https://github.com/golang/go/blob/master/LICENSE Copyright The Helm Authors. +Copyright 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 @@ -21,7 +22,7 @@ limitations under the License. package sympath import ( - "log" + "io/fs" "os" "path/filepath" "sort" @@ -29,17 +30,21 @@ import ( "github.com/pkg/errors" ) +// AbsWalkFunc functions like filepath.WalkFunc but provides the absolute path +// of fs.FileInfo when path is a symlink. +type AbsWalkFunc func(path, absPath string, info fs.FileInfo, err error) error + // Walk walks the file tree rooted at root, calling walkFn for each file or directory // in the tree, including root. All errors that arise visiting files and directories // are filtered by walkFn. The files are walked in lexical order, which makes the // output deterministic but means that for very large directories Walk can be // inefficient. Walk follows symbolic links. -func Walk(root string, walkFn filepath.WalkFunc) error { +func Walk(root string, walkFn AbsWalkFunc) error { info, err := os.Lstat(root) if err != nil { - err = walkFn(root, nil, err) + err = walkFn(root, root, nil, err) } else { - err = symwalk(root, info, walkFn) + err = symwalk(root, root, info, walkFn) } if err == filepath.SkipDir { return nil @@ -63,25 +68,25 @@ func readDirNames(dirname string) ([]string, error) { return names, nil } -// symwalk recursively descends path, calling walkFn. -func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { +// symwalk recursively descends path, calling AbsWalkFunc. +func symwalk(path, absPath string, info os.FileInfo, walkFn AbsWalkFunc) error { // Recursively walk symlinked directories. if IsSymlink(info) { resolved, err := filepath.EvalSymlinks(path) if err != nil { return errors.Wrapf(err, "error evaluating symlink %s", path) } - log.Printf("found symbolic link in path: %s resolves to %s", path, resolved) if info, err = os.Lstat(resolved); err != nil { return err } - if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir { + // NB: pass-on resolved as absolute path + if err := symwalk(path, resolved, info, walkFn); err != nil && err != filepath.SkipDir { return err } return nil } - if err := walkFn(path, info, nil); err != nil { + if err := walkFn(path, absPath, info, nil); err != nil { return err } @@ -91,19 +96,20 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { names, err := readDirNames(path) if err != nil { - return walkFn(path, info, err) + return walkFn(path, absPath, info, err) } for _, name := range names { filename := filepath.Join(path, name) + // NB: possibly absPath != path separately + absFilename := filepath.Join(absPath, name) fileInfo, err := os.Lstat(filename) if err != nil { - if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { + if err := walkFn(filename, absFilename, fileInfo, err); err != nil && err != filepath.SkipDir { return err } } else { - err = symwalk(filename, fileInfo, walkFn) - if err != nil { + if err = symwalk(filename, absFilename, fileInfo, walkFn); err != nil { if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir { return err } diff --git a/internal/helm/chart/loader/sympath/walk_test.go b/internal/helm/chart/loader/sympath/walk_test.go index 25f73713..50740f34 100644 --- a/internal/helm/chart/loader/sympath/walk_test.go +++ b/internal/helm/chart/loader/sympath/walk_test.go @@ -27,45 +27,47 @@ import ( ) type Node struct { - name string - entries []*Node // nil if the entry is a file - marks int - expectedMarks int - symLinkedTo string + name string + entries []*Node // nil if the entry is a file + marks int + expectedMarks int + symLinkedTo string + absPath string + expectedAbsPath string } var tree = &Node{ "testdata", []*Node{ - {"a", nil, 0, 1, ""}, - {"b", []*Node{}, 0, 1, ""}, - {"c", nil, 0, 2, ""}, - {"d", nil, 0, 0, "c"}, + {"a", nil, 0, 1, "", "", "testdata/a"}, + {"b", []*Node{}, 0, 1, "", "", "testdata/b"}, + {"c", nil, 0, 2, "", "", "testdata/c"}, + {"d", nil, 0, 0, "c", "", "testdata/c"}, { "e", []*Node{ - {"x", nil, 0, 1, ""}, - {"y", []*Node{}, 0, 1, ""}, + {"x", nil, 0, 1, "", "", "testdata/e/x"}, + {"y", []*Node{}, 0, 1, "", "", "testdata/e/y"}, { "z", []*Node{ - {"u", nil, 0, 1, ""}, - {"v", nil, 0, 1, ""}, - {"w", nil, 0, 1, ""}, + {"u", nil, 0, 1, "", "", "testdata/e/z/u"}, + {"v", nil, 0, 1, "", "", "testdata/e/z/v"}, + {"w", nil, 0, 1, "", "", "testdata/e/z/w"}, }, 0, 1, - "", + "", "", "testdata/e/z", }, }, 0, 1, - "", + "", "", "testdata/e", }, }, 0, 1, - "", + "", "", "testdata", } func walkTree(n *Node, path string, f func(path string, n *Node)) { @@ -103,6 +105,9 @@ func checkMarks(t *testing.T, report bool) { if n.marks != n.expectedMarks && report { t.Errorf("node %s mark = %d; expected %d", path, n.marks, n.expectedMarks) } + if n.absPath != n.expectedAbsPath && report { + t.Errorf("node %s absPath = %s; expected %s", path, n.absPath, n.expectedAbsPath) + } n.marks = 0 }) } @@ -110,7 +115,7 @@ func checkMarks(t *testing.T, report bool) { // Assumes that each node name is unique. Good enough for a test. // If clear is true, any incoming error is cleared before return. The errors // are always accumulated, though. -func mark(info os.FileInfo, err error, errors *[]error, clear bool) error { +func mark(absPath string, info os.FileInfo, err error, errors *[]error, clear bool) error { if err != nil { *errors = append(*errors, err) if clear { @@ -120,8 +125,12 @@ func mark(info os.FileInfo, err error, errors *[]error, clear bool) error { } name := info.Name() walkTree(tree, tree.name, func(path string, n *Node) { + if n.symLinkedTo == name { + n.absPath = absPath + } if n.name == name { n.marks++ + n.absPath = absPath } }) return nil @@ -131,8 +140,8 @@ func TestWalk(t *testing.T) { makeTree(t) errors := make([]error, 0, 10) clear := true - markFn := func(path string, info os.FileInfo, err error) error { - return mark(info, err, &errors, clear) + markFn := func(path, absPath string, info os.FileInfo, err error) error { + return mark(absPath, info, err, &errors, clear) } // Expect no errors. err := Walk(tree.name, markFn)