handle symlinks

This commit is contained in:
AJ ONeal 2019-07-12 01:47:12 -06:00
parent 1e733b6d6d
commit 7bb71f07ca
1 changed files with 45 additions and 15 deletions

View File

@ -133,7 +133,8 @@ func main() {
if nil != err { if nil != err {
panic(err) panic(err)
} }
err = unzip(z, s.Size(), outdir, npath) strip := 1
err = unzip(z, s.Size(), outdir, strip)
if nil != err { if nil != err {
panic(err) panic(err)
} }
@ -142,7 +143,8 @@ func main() {
if nil != err { if nil != err {
panic(err) panic(err)
} }
err = untar(tgz, outdir, npath) strip := 1
err = untar(tgz, outdir, strip)
if nil != err { if nil != err {
panic(err) panic(err)
} }
@ -163,16 +165,16 @@ func main() {
if nil != err { if nil != err {
panic(err) panic(err)
} }
if err := unzip(z, s.Size(), outdir, "telebit.js"); nil != err { strip := 1
if err := unzip(z, s.Size(), outdir, strip); nil != err {
panic(err) panic(err)
} }
return
} }
fmt.Printf("Done.\n") fmt.Printf("Done.\n")
} }
func untar(tgz io.Reader, outdir string, strip string) error { func untar(tgz io.Reader, outdir string, strip int) error {
t, err := gzip.NewReader(tgz) t, err := gzip.NewReader(tgz)
if nil != err { if nil != err {
return err return err
@ -188,15 +190,20 @@ func untar(tgz io.Reader, outdir string, strip string) error {
return err return err
} }
fpath := header.Name fpath := stripPrefix(header.Name, strip)
fpath = filepath.Join(outdir, strings.Trim(strings.Trim(strings.TrimPrefix(fpath, strip), `/`), `\`)) fpath = filepath.Join(outdir, fpath)
switch header.Typeflag { switch header.Typeflag {
case tar.TypeLink: case tar.TypeLink:
// ignore hard links // ignore hard links
case tar.TypeSymlink: case tar.TypeSymlink:
// we don't really expect these (or support them) // Note: the link itself is always a file, even when it represents a directory
fmt.Printf("[debug] symlink? %s\n", fpath) lpath := filepath.Join(filepath.Dir(fpath), header.Linkname)
if !strings.HasPrefix(lpath+string(os.PathSeparator), outdir+string(os.PathSeparator)) {
return fmt.Errorf("Malicious link path: %s", header.Linkname)
}
if err := os.Symlink(header.Linkname, fpath); nil != err {
return err
}
case tar.TypeDir: case tar.TypeDir:
// gonna use the same perms as were set previously here // gonna use the same perms as were set previously here
// should be fine (i.e. we want 755 for execs on *nix) // should be fine (i.e. we want 755 for execs on *nix)
@ -227,7 +234,7 @@ func untar(tgz io.Reader, outdir string, strip string) error {
return nil return nil
} }
func unzip(z io.ReaderAt, size int64, outdir string, strip string) error { func unzip(z io.ReaderAt, size int64, outdir string, strip int) error {
zr, err := zip.NewReader(z, size) zr, err := zip.NewReader(z, size)
if nil != err { if nil != err {
return err return err
@ -235,8 +242,9 @@ func unzip(z io.ReaderAt, size int64, outdir string, strip string) error {
for i := range zr.File { for i := range zr.File {
f := zr.File[i] f := zr.File[i]
fpath := filepath.Join(outdir, strings.Trim(strings.Trim(strings.TrimPrefix(f.Name, strip), `/`), `\`))
fpath := stripPrefix(f.Name, strip)
fpath = filepath.Join(outdir, fpath)
out, err := safeOpen(f.FileInfo(), f.Mode(), fpath, outdir) out, err := safeOpen(f.FileInfo(), f.Mode(), fpath, outdir)
if nil != err { if nil != err {
return err return err
@ -271,15 +279,37 @@ func unzip(z io.ReaderAt, size int64, outdir string, strip string) error {
return nil return nil
} }
func stripPrefix(fpath string, strip int) string {
// /foo/bar/baz/ => foo/bar/baz
// strip 1 => bar/baz
fpath = strings.Trim(filepath.ToSlash(fpath), "/")
parts := []string{}
if "" != fpath {
parts = strings.Split(fpath, "/")
}
if strip > 0 {
n := len(parts)
if strip > n {
strip = n
}
if 0 != len(parts) {
parts = parts[strip:]
}
}
return strings.Join(parts, "/")
}
// given the path return a file, tell that it's a directory, or error out // given the path return a file, tell that it's a directory, or error out
func safeOpen(fi os.FileInfo, fm os.FileMode, fpath string, outdir string) (io.WriteCloser, error) { func safeOpen(fi os.FileInfo, fm os.FileMode, fpath string, outdir string) (io.WriteCloser, error) {
// Keep it clean // Keep it clean
// https://github.com/snyk/zip-slip-vulnerability // https://github.com/snyk/zip-slip-vulnerability
cleanpath, _ := filepath.Abs(filepath.Clean(fpath)) cleanpath, _ := filepath.Abs(filepath.Clean(fpath))
cleandest, _ := filepath.Abs(filepath.Clean(outdir)) cleandest, _ := filepath.Abs(filepath.Clean(outdir))
//fmt.Println(cleandest)
//fmt.Println(cleanpath + string(os.PathSeparator)) // foo/ foo => foo// foo/
if !strings.HasPrefix(cleanpath, cleandest) { // foo/ foo/bar.md => foo// foo/bar.md/
if !strings.HasPrefix(cleanpath+string(os.PathSeparator), cleandest+string(os.PathSeparator)) {
return nil, fmt.Errorf("Malicious file path: %s", fpath) return nil, fmt.Errorf("Malicious file path: %s", fpath)
} }
fpath = cleanpath fpath = cleanpath