Skip to content

Commit f41db12

Browse files
committed
attempt to resolve symlink problems
1 parent e46ba21 commit f41db12

File tree

3 files changed

+104
-8
lines changed

3 files changed

+104
-8
lines changed

extractor.go

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import (
1111
)
1212

1313
type Extractor struct {
14-
Path string
15-
Progress func(int64) int64
14+
Path string
15+
Progress func(int64) int64
16+
sanitizePaths bool
17+
sanitizeLinks bool
18+
stubLinks bool
1619
}
1720

1821
func (te *Extractor) Extract(reader io.Reader) error {
@@ -61,13 +64,40 @@ func (te *Extractor) Extract(reader io.Reader) error {
6164
return nil
6265
}
6366

67+
// Sanitize toggles all output sanitation rules
68+
func (te *Extractor) Sanitize(toggle bool) {
69+
te.sanitizePaths = toggle
70+
te.sanitizeLinks = toggle
71+
}
72+
73+
// SanitizePaths toggles converting illegal paths to valid paths on Extract
74+
func (te *Extractor) SanitizePaths(toggle bool) {
75+
te.sanitizePaths = toggle
76+
77+
}
78+
79+
// SanitizeLinks toggles failure for links that are absolute or escape the output root on Extract
80+
func (te *Extractor) SanitizeLinks(toggle bool) {
81+
te.sanitizeLinks = toggle
82+
}
83+
84+
// StubLinks toggles link stubbing. When enabled, all links are created as empty files instead of links
85+
func (te *Extractor) StubLinks(toggle bool) {
86+
te.stubLinks = toggle
87+
}
88+
6489
// outputPath returns the path at which to place tarPath
6590
func (te *Extractor) outputPath(tarPath string) string {
66-
elems := strings.Split(tarPath, "/") // break into elems
67-
elems = elems[1:] // remove IPFS root
68-
safePath := platformSanitize(elems) // sanitize IPFS path to be platform legal
69-
safePath = fp.Join(te.Path, safePath) // rebase on to extraction target root
70-
return safePath
91+
var outPath string
92+
elems := strings.Split(tarPath, "/") // break into elems
93+
elems = elems[1:] // remove original root
94+
if te.sanitizePaths {
95+
outPath = platformSanitize(elems) // sanitize base path elements to be platform legal
96+
} else {
97+
outPath = fp.Join(elems...) // join elems
98+
}
99+
outPath = fp.Join(te.Path, outPath) // rebase on to extraction target root
100+
return outPath
71101
}
72102

73103
func (te *Extractor) extractDir(h *tar.Header, depth int) error {
@@ -82,6 +112,14 @@ func (te *Extractor) extractDir(h *tar.Header, depth int) error {
82112
}
83113

84114
func (te *Extractor) extractSymlink(h *tar.Header) error {
115+
if te.stubLinks {
116+
f, err := os.Create(te.outputPath(h.Name))
117+
f.Close()
118+
return err
119+
}
120+
if te.sanitizeLinks {
121+
return platformLink(te.Path, h.Linkname, te.outputPath(h.Name))
122+
}
85123
return os.Symlink(h.Linkname, te.outputPath(h.Name))
86124
}
87125

sanitize.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,29 @@
22

33
package tar
44

5-
import "path/filepath"
5+
import (
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
)
611

712
func platformSanitize(pathElements []string) string {
813
return filepath.Join(pathElements...)
914
}
15+
16+
//func platformLink(target, link string) error {
17+
//func platformLink(linkBase string, linkHeader *tar.Header) error {
18+
func platformLink(targetRoot, target, link string) error {
19+
//prevent symlinks from accessing outside of the output root
20+
resolvedTarget := filepath.Join(target, link)
21+
rel, err := filepath.Rel(targetRoot, resolvedTarget)
22+
if err != nil {
23+
return err
24+
}
25+
if strings.HasPrefix(rel, "..") {
26+
return fmt.Errorf("Symlink target %q escapes target root %q", target, targetRoot)
27+
}
28+
return os.Symlink(target, link)
29+
//return os.Symlink(h.Linkname, linkBase te.outputPath(h.Name))
30+
}

sanitize_windows.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tar
33
import (
44
"fmt"
55
"net/url"
6+
"os"
67
"path/filepath"
78
"regexp"
89
"strings"
@@ -54,3 +55,39 @@ func platformSanitize(pathElements []string) string {
5455

5556
return filepath.FromSlash(res)
5657
}
58+
59+
func platformLink(targetRoot, target, link string) error {
60+
if filepath.IsAbs(target) {
61+
return fmt.Errorf("Link target %q is an absolute path (forbidden)", target) //TODO: discuss
62+
}
63+
64+
if strings.HasPrefix(target, string(os.PathSeparator)) || strings.HasPrefix(target, "/") {
65+
return fmt.Errorf("Link target %q is relative to drive root (forbidden)", target) //TODO: discuss
66+
}
67+
68+
resolvedTarget := filepath.Join(link, target)
69+
rel, err := filepath.Rel(targetRoot, resolvedTarget)
70+
if err != nil {
71+
return err
72+
}
73+
74+
//disallow symlinks from climbing out of the target root
75+
if strings.HasPrefix(rel, "..") {
76+
return fmt.Errorf("Symlink target %q escapes target root %q", target, targetRoot)
77+
}
78+
//disallow pointing to self from parent ("../self" resolves to "self")
79+
if resolvedTarget == targetRoot {
80+
return fmt.Errorf("Symlink target %q points to itself %q", target, targetRoot)
81+
}
82+
83+
return os.Symlink(target, link)
84+
/* TODO: placeholder url; a modified version of this should be in client code, not this lib
85+
if err != nil {
86+
if lErr, ok := err.(*os.LinkError); ok && lErr.Err == windows.ERROR_PRIVILEGE_NOT_HELD {
87+
return fmt.Errorf("Symlink creation privileges not held, see: https://github.com/ipfs/go-ipfs/blob/master/docs/windows.md#troubleshooting")
88+
}
89+
return err
90+
}
91+
return nil
92+
*/
93+
}

0 commit comments

Comments
 (0)