github.com/moby/docker@v26.1.3+incompatible/internal/safepath/common.go (about)

     1  package safepath
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  
     7  	"github.com/pkg/errors"
     8  )
     9  
    10  // evaluatePath evaluates symlinks in the concatenation of path and subpath. If
    11  // err is nil, resolvedBasePath will contain result of resolving all symlinks
    12  // in the given path, and resolvedSubpath will contain a relative path rooted
    13  // at the resolvedBasePath pointing to the concatenation after resolving all
    14  // symlinks.
    15  func evaluatePath(path, subpath string) (resolvedBasePath string, resolvedSubpath string, err error) {
    16  	baseResolved, err := filepath.EvalSymlinks(path)
    17  	if err != nil {
    18  		if errors.Is(err, os.ErrNotExist) {
    19  			return "", "", &ErrNotAccessible{Path: path, Cause: err}
    20  		}
    21  		return "", "", errors.Wrapf(err, "error while resolving symlinks in base directory %q", path)
    22  	}
    23  
    24  	combinedPath := filepath.Join(baseResolved, subpath)
    25  	combinedResolved, err := filepath.EvalSymlinks(combinedPath)
    26  	if err != nil {
    27  		if errors.Is(err, os.ErrNotExist) {
    28  			return "", "", &ErrNotAccessible{Path: combinedPath, Cause: err}
    29  		}
    30  		return "", "", errors.Wrapf(err, "error while resolving symlinks in combined path %q", combinedPath)
    31  	}
    32  
    33  	subpart, err := filepath.Rel(baseResolved, combinedResolved)
    34  	if err != nil {
    35  		return "", "", &ErrEscapesBase{Base: baseResolved, Subpath: subpath}
    36  	}
    37  
    38  	if !filepath.IsLocal(subpart) {
    39  		return "", "", &ErrEscapesBase{Base: baseResolved, Subpath: subpath}
    40  	}
    41  
    42  	return baseResolved, subpart, nil
    43  }
    44  
    45  // isLocalTo reports whether path, using lexical analysis only, has all of these properties:
    46  //   - is within the subtree rooted at basepath
    47  //   - is not empty
    48  //   - on Windows, is not a reserved name such as "NUL"
    49  //
    50  // If isLocalTo(path, basepath) returns true, then
    51  //
    52  //	filepath.Rel(basepath, path)
    53  //
    54  // will always produce an unrooted path with no `..` elements.
    55  //
    56  // isLocalTo is a purely lexical operation. In particular, it does not account for the effect of any symbolic links that may exist in the filesystem.
    57  //
    58  // Both path and basepath are expected to be absolute paths.
    59  func isLocalTo(path, basepath string) bool {
    60  	rel, err := filepath.Rel(basepath, path)
    61  	if err != nil {
    62  		return false
    63  	}
    64  
    65  	return filepath.IsLocal(rel)
    66  }