forked from forgejo/forgejo
Introduce path Clean/Join helper functions (#23495)
Since #23493 has conflicts with latest commits, this PR is my proposal for fixing #23371 Details are in the comments And refactor the `modules/options` module, to make it always use "filepath" to access local files. Benefits: * No need to do `util.CleanPath(strings.ReplaceAll(p, "\\", "/"))), "/")` any more (not only one before) * The function behaviors are clearly defined
This commit is contained in:
parent
253a00aaac
commit
ce9dee5a1e
16 changed files with 261 additions and 152 deletions
|
@ -5,6 +5,7 @@ package util
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -14,21 +15,92 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// CleanPath ensure to clean the path
|
||||
func CleanPath(p string) string {
|
||||
if strings.HasPrefix(p, "/") {
|
||||
return path.Clean(p)
|
||||
// PathJoinRel joins the path elements into a single path, each element is cleaned by path.Clean separately.
|
||||
// It only returns the following values (like path.Join), any redundant part (empty, relative dots, slashes) is removed.
|
||||
// It's caller's duty to make every element not bypass its own directly level, to avoid security issues.
|
||||
//
|
||||
// empty => ``
|
||||
// `` => ``
|
||||
// `..` => `.`
|
||||
// `dir` => `dir`
|
||||
// `/dir/` => `dir`
|
||||
// `foo\..\bar` => `foo\..\bar`
|
||||
// {`foo`, ``, `bar`} => `foo/bar`
|
||||
// {`foo`, `..`, `bar`} => `foo/bar`
|
||||
func PathJoinRel(elem ...string) string {
|
||||
elems := make([]string, len(elem))
|
||||
for i, e := range elem {
|
||||
if e == "" {
|
||||
continue
|
||||
}
|
||||
elems[i] = path.Clean("/" + e)
|
||||
}
|
||||
p := path.Join(elems...)
|
||||
if p == "" {
|
||||
return ""
|
||||
} else if p == "/" {
|
||||
return "."
|
||||
} else {
|
||||
return p[1:]
|
||||
}
|
||||
return path.Clean("/" + p)[1:]
|
||||
}
|
||||
|
||||
// EnsureAbsolutePath ensure that a path is absolute, making it
|
||||
// relative to absoluteBase if necessary
|
||||
func EnsureAbsolutePath(path, absoluteBase string) string {
|
||||
if filepath.IsAbs(path) {
|
||||
return path
|
||||
// PathJoinRelX joins the path elements into a single path like PathJoinRel,
|
||||
// and covert all backslashes to slashes. (X means "extended", also means the combination of `\` and `/`).
|
||||
// It's caller's duty to make every element not bypass its own directly level, to avoid security issues.
|
||||
// It returns similar results as PathJoinRel except:
|
||||
//
|
||||
// `foo\..\bar` => `bar` (because it's processed as `foo/../bar`)
|
||||
//
|
||||
// All backslashes are handled as slashes, the result only contains slashes.
|
||||
func PathJoinRelX(elem ...string) string {
|
||||
elems := make([]string, len(elem))
|
||||
for i, e := range elem {
|
||||
if e == "" {
|
||||
continue
|
||||
}
|
||||
elems[i] = path.Clean("/" + strings.ReplaceAll(e, "\\", "/"))
|
||||
}
|
||||
return filepath.Join(absoluteBase, path)
|
||||
return PathJoinRel(elems...)
|
||||
}
|
||||
|
||||
const pathSeparator = string(os.PathSeparator)
|
||||
|
||||
// FilePathJoinAbs joins the path elements into a single file path, each element is cleaned by filepath.Clean separately.
|
||||
// All slashes/backslashes are converted to path separators before cleaning, the result only contains path separators.
|
||||
// The first element must be an absolute path, caller should prepare the base path.
|
||||
// It's caller's duty to make every element not bypass its own directly level, to avoid security issues.
|
||||
// Like PathJoinRel, any redundant part (empty, relative dots, slashes) is removed.
|
||||
//
|
||||
// {`/foo`, ``, `bar`} => `/foo/bar`
|
||||
// {`/foo`, `..`, `bar`} => `/foo/bar`
|
||||
func FilePathJoinAbs(elem ...string) string {
|
||||
elems := make([]string, len(elem))
|
||||
|
||||
// POISX filesystem can have `\` in file names. Windows: `\` and `/` are both used for path separators
|
||||
// to keep the behavior consistent, we do not allow `\` in file names, replace all `\` with `/`
|
||||
if isOSWindows() {
|
||||
elems[0] = filepath.Clean(elem[0])
|
||||
} else {
|
||||
elems[0] = filepath.Clean(strings.ReplaceAll(elem[0], "\\", pathSeparator))
|
||||
}
|
||||
if !filepath.IsAbs(elems[0]) {
|
||||
// This shouldn't happen. If there is really necessary to pass in relative path, return the full path with filepath.Abs() instead
|
||||
panic(fmt.Sprintf("FilePathJoinAbs: %q (for path %v) is not absolute, do not guess a relative path based on current working directory", elems[0], elems))
|
||||
}
|
||||
|
||||
for i := 1; i < len(elem); i++ {
|
||||
if elem[i] == "" {
|
||||
continue
|
||||
}
|
||||
if isOSWindows() {
|
||||
elems[i] = filepath.Clean(pathSeparator + elem[i])
|
||||
} else {
|
||||
elems[i] = filepath.Clean(pathSeparator + strings.ReplaceAll(elem[i], "\\", pathSeparator))
|
||||
}
|
||||
}
|
||||
// the elems[0] must be an absolute path, just join them together
|
||||
return filepath.Join(elems...)
|
||||
}
|
||||
|
||||
// IsDir returns true if given path is a directory,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue