Skip to content

Commit 25f54ee

Browse files
committed
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 <[email protected]>
1 parent ad597b3 commit 25f54ee

File tree

2 files changed

+48
-33
lines changed

2 files changed

+48
-33
lines changed

internal/helm/chart/loader/sympath/walk.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ provided under the BSD license.
55
https://github.com/golang/go/blob/master/LICENSE
66
77
Copyright The Helm Authors.
8+
Copyright The Flux authors
89
Licensed under the Apache License, Version 2.0 (the "License");
910
you may not use this file except in compliance with the License.
1011
You may obtain a copy of the License at
@@ -21,25 +22,29 @@ limitations under the License.
2122
package sympath
2223

2324
import (
24-
"log"
25+
"io/fs"
2526
"os"
2627
"path/filepath"
2728
"sort"
2829

2930
"github.com/pkg/errors"
3031
)
3132

33+
// AbsWalkFunc functions like filepath.WalkFunc but provides the absolute path
34+
// of fs.FileInfo when path is a symlink.
35+
type AbsWalkFunc func(path, absPath string, info fs.FileInfo, err error) error
36+
3237
// Walk walks the file tree rooted at root, calling walkFn for each file or directory
3338
// in the tree, including root. All errors that arise visiting files and directories
3439
// are filtered by walkFn. The files are walked in lexical order, which makes the
3540
// output deterministic but means that for very large directories Walk can be
3641
// inefficient. Walk follows symbolic links.
37-
func Walk(root string, walkFn filepath.WalkFunc) error {
42+
func Walk(root string, walkFn AbsWalkFunc) error {
3843
info, err := os.Lstat(root)
3944
if err != nil {
40-
err = walkFn(root, nil, err)
45+
err = walkFn(root, root, nil, err)
4146
} else {
42-
err = symwalk(root, info, walkFn)
47+
err = symwalk(root, root, info, walkFn)
4348
}
4449
if err == filepath.SkipDir {
4550
return nil
@@ -63,25 +68,25 @@ func readDirNames(dirname string) ([]string, error) {
6368
return names, nil
6469
}
6570

66-
// symwalk recursively descends path, calling walkFn.
67-
func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
71+
// symwalk recursively descends path, calling AbsWalkFunc.
72+
func symwalk(path, absPath string, info os.FileInfo, walkFn AbsWalkFunc) error {
6873
// Recursively walk symlinked directories.
6974
if IsSymlink(info) {
7075
resolved, err := filepath.EvalSymlinks(path)
7176
if err != nil {
7277
return errors.Wrapf(err, "error evaluating symlink %s", path)
7378
}
74-
log.Printf("found symbolic link in path: %s resolves to %s", path, resolved)
7579
if info, err = os.Lstat(resolved); err != nil {
7680
return err
7781
}
78-
if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir {
82+
// NB: pass-on resolved as absolute path
83+
if err := symwalk(path, resolved, info, walkFn); err != nil && err != filepath.SkipDir {
7984
return err
8085
}
8186
return nil
8287
}
8388

84-
if err := walkFn(path, info, nil); err != nil {
89+
if err := walkFn(path, absPath, info, nil); err != nil {
8590
return err
8691
}
8792

@@ -91,19 +96,20 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
9196

9297
names, err := readDirNames(path)
9398
if err != nil {
94-
return walkFn(path, info, err)
99+
return walkFn(path, absPath, info, err)
95100
}
96101

97102
for _, name := range names {
98103
filename := filepath.Join(path, name)
104+
// NB: possibly absPath != path separately
105+
absFilename := filepath.Join(absPath, name)
99106
fileInfo, err := os.Lstat(filename)
100107
if err != nil {
101-
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
108+
if err := walkFn(filename, absFilename, fileInfo, err); err != nil && err != filepath.SkipDir {
102109
return err
103110
}
104111
} else {
105-
err = symwalk(filename, fileInfo, walkFn)
106-
if err != nil {
112+
if err = symwalk(filename, absFilename, fileInfo, walkFn); err != nil {
107113
if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir {
108114
return err
109115
}

internal/helm/chart/loader/sympath/walk_test.go

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,45 +27,47 @@ import (
2727
)
2828

2929
type Node struct {
30-
name string
31-
entries []*Node // nil if the entry is a file
32-
marks int
33-
expectedMarks int
34-
symLinkedTo string
30+
name string
31+
entries []*Node // nil if the entry is a file
32+
marks int
33+
expectedMarks int
34+
symLinkedTo string
35+
absPath string
36+
expectedAbsPath string
3537
}
3638

3739
var tree = &Node{
3840
"testdata",
3941
[]*Node{
40-
{"a", nil, 0, 1, ""},
41-
{"b", []*Node{}, 0, 1, ""},
42-
{"c", nil, 0, 2, ""},
43-
{"d", nil, 0, 0, "c"},
42+
{"a", nil, 0, 1, "", "", "testdata/a"},
43+
{"b", []*Node{}, 0, 1, "", "", "testdata/b"},
44+
{"c", nil, 0, 2, "", "", "testdata/c"},
45+
{"d", nil, 0, 0, "c", "", "testdata/c"},
4446
{
4547
"e",
4648
[]*Node{
47-
{"x", nil, 0, 1, ""},
48-
{"y", []*Node{}, 0, 1, ""},
49+
{"x", nil, 0, 1, "", "", "testdata/e/x"},
50+
{"y", []*Node{}, 0, 1, "", "", "testdata/e/y"},
4951
{
5052
"z",
5153
[]*Node{
52-
{"u", nil, 0, 1, ""},
53-
{"v", nil, 0, 1, ""},
54-
{"w", nil, 0, 1, ""},
54+
{"u", nil, 0, 1, "", "", "testdata/e/z/u"},
55+
{"v", nil, 0, 1, "", "", "testdata/e/z/v"},
56+
{"w", nil, 0, 1, "", "", "testdata/e/z/w"},
5557
},
5658
0,
5759
1,
58-
"",
60+
"", "", "testdata/e/z",
5961
},
6062
},
6163
0,
6264
1,
63-
"",
65+
"", "", "testdata/e",
6466
},
6567
},
6668
0,
6769
1,
68-
"",
70+
"", "", "testdata",
6971
}
7072

7173
func walkTree(n *Node, path string, f func(path string, n *Node)) {
@@ -103,14 +105,17 @@ func checkMarks(t *testing.T, report bool) {
103105
if n.marks != n.expectedMarks && report {
104106
t.Errorf("node %s mark = %d; expected %d", path, n.marks, n.expectedMarks)
105107
}
108+
if n.absPath != n.expectedAbsPath && report {
109+
t.Errorf("node %s absPath = %s; expected %s", path, n.absPath, n.expectedAbsPath)
110+
}
106111
n.marks = 0
107112
})
108113
}
109114

110115
// Assumes that each node name is unique. Good enough for a test.
111116
// If clear is true, any incoming error is cleared before return. The errors
112117
// are always accumulated, though.
113-
func mark(info os.FileInfo, err error, errors *[]error, clear bool) error {
118+
func mark(absPath string, info os.FileInfo, err error, errors *[]error, clear bool) error {
114119
if err != nil {
115120
*errors = append(*errors, err)
116121
if clear {
@@ -120,8 +125,12 @@ func mark(info os.FileInfo, err error, errors *[]error, clear bool) error {
120125
}
121126
name := info.Name()
122127
walkTree(tree, tree.name, func(path string, n *Node) {
128+
if n.symLinkedTo == name {
129+
n.absPath = absPath
130+
}
123131
if n.name == name {
124132
n.marks++
133+
n.absPath = absPath
125134
}
126135
})
127136
return nil
@@ -131,8 +140,8 @@ func TestWalk(t *testing.T) {
131140
makeTree(t)
132141
errors := make([]error, 0, 10)
133142
clear := true
134-
markFn := func(path string, info os.FileInfo, err error) error {
135-
return mark(info, err, &errors, clear)
143+
markFn := func(path, absPath string, info os.FileInfo, err error) error {
144+
return mark(absPath, info, err, &errors, clear)
136145
}
137146
// Expect no errors.
138147
err := Walk(tree.name, markFn)

0 commit comments

Comments
 (0)