fix(R-1187 b): restored links point only downward — no absolute target, no '..', no link on the way
gates / gates (push) Successful in 54s
gates / gates (push) Successful in 54s
Third automated security review finding, red-proved.
This commit is contained in:
@@ -1203,6 +1203,34 @@ func extractTarGz(tgzPath, destDir string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// safeDownwardLink is the rule for a restored link: a relative target with no „.." component, and no symbolic link on
|
||||
// the way — not among the folders of the link's own path, not among the existing components of its target.
|
||||
func safeDownwardLink(rt *os.Root, name, target string) bool {
|
||||
if target == "" || filepath.IsAbs(target) {
|
||||
return false
|
||||
}
|
||||
for _, part := range strings.Split(filepath.ToSlash(target), "/") {
|
||||
if part == ".." {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if parentThroughLink(rt, name) {
|
||||
return false
|
||||
}
|
||||
acc := filepath.Dir(name)
|
||||
for _, part := range strings.Split(filepath.Clean(target), string(os.PathSeparator)) {
|
||||
acc = filepath.Join(acc, part)
|
||||
fi, err := rt.Lstat(acc)
|
||||
if err != nil {
|
||||
break // not there (yet): nothing further down can be a link that exists now
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// parentThroughLink is true when any folder on name's path (inside the root) is a symbolic link.
|
||||
func parentThroughLink(rt *os.Root, name string) bool {
|
||||
dir := filepath.Dir(name)
|
||||
@@ -1285,23 +1313,16 @@ func extractTar(tarPath, destDir string) ([]string, error) {
|
||||
out.Close()
|
||||
rt.Chmod(name, os.FileMode(hdr.Mode).Perm())
|
||||
case tar.TypeSymlink:
|
||||
// A restored link may only point DOWNWARD inside the folder, through real folders: no absolute target, no
|
||||
// „..", and no link on the way — neither on the link's own path nor in its target. Such a link can never
|
||||
// resolve outside the app's folder, whatever else the folder holds. Everything else is refused and listed.
|
||||
// (Three rounds of the automated security review: a link written through, a linked parent, a target through
|
||||
// a link. Pinned by TestR1187b_*.)
|
||||
if !safeDownwardLink(rt, name, hdr.Linkname) {
|
||||
refused = append(refused, hdr.Name)
|
||||
continue
|
||||
}
|
||||
dst := hdr.Linkname
|
||||
if filepath.IsAbs(dst) {
|
||||
refused = append(refused, hdr.Name)
|
||||
continue
|
||||
}
|
||||
// The text check below is exact only when no folder on the link's path is itself a link (a „a" → „." makes
|
||||
// „a/b/l" really „b/l"): such an entry is refused (the security review's second case, pinned by
|
||||
// TestR1187b_NoLinkThroughALinkedParent).
|
||||
if parentThroughLink(rt, name) {
|
||||
refused = append(refused, hdr.Name)
|
||||
continue
|
||||
}
|
||||
resolved := filepath.Clean(filepath.Join(filepath.Dir(filepath.Join(root, name)), dst))
|
||||
if resolved != root && !strings.HasPrefix(resolved, root+string(os.PathSeparator)) {
|
||||
refused = append(refused, hdr.Name)
|
||||
continue
|
||||
}
|
||||
if dir := filepath.Dir(name); dir != "." {
|
||||
if err := rt.MkdirAll(dir, 0755); err != nil {
|
||||
return refused, err
|
||||
|
||||
Reference in New Issue
Block a user