SanitizedPathJoin performs filepath.Join(root, reqPath) that is safe against directory traversal attacks. It uses logic similar to that in the Go standard library, specifically in the implementation of http.Dir. The root is assumed to be a trusted path, but reqPath is not; and the output will never
(root, reqPath string)
| 250 | // not local according to lexical processing (i.e. ignoring links), |
| 251 | // it will be rejected as unsafe and only the root will be returned. |
| 252 | func SanitizedPathJoin(root, reqPath string) string { |
| 253 | if root == "" { |
| 254 | root = "." |
| 255 | } |
| 256 | |
| 257 | relPath := path.Clean("/" + reqPath)[1:] // clean path and trim the leading / |
| 258 | if relPath != "" && !filepath.IsLocal(relPath) { |
| 259 | // path is unsafe (see https://github.com/golang/go/issues/56336#issuecomment-1416214885) |
| 260 | return root |
| 261 | } |
| 262 | |
| 263 | path := filepath.Join(root, filepath.FromSlash(relPath)) |
| 264 | |
| 265 | // filepath.Join also cleans the path, and cleaning strips |
| 266 | // the trailing slash, so we need to re-add it afterwards. |
| 267 | // if the length is 1, then it's a path to the root, |
| 268 | // and that should return ".", so we don't append the separator. |
| 269 | if strings.HasSuffix(reqPath, "/") && len(reqPath) > 1 { |
| 270 | path += separator |
| 271 | } |
| 272 | |
| 273 | return path |
| 274 | } |
| 275 | |
| 276 | // CleanPath cleans path p according to path.Clean(), but only |
| 277 | // merges repeated slashes if collapseSlashes is true, and always |
no outgoing calls