mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 19:06:18 +01:00 
			
		
		
		
	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:
		| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user