From 9d4ed3a3236df06e54c80c4f6633b66d68ad3673 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Thu, 17 Jun 2021 09:59:08 -0600 Subject: caddyhttp: Refactor and export SanitizedPathJoin for use in fastcgi (#4207) --- modules/caddyhttp/caddyhttp.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'modules/caddyhttp/caddyhttp.go') diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go index d93a5c8..784b2b9 100644 --- a/modules/caddyhttp/caddyhttp.go +++ b/modules/caddyhttp/caddyhttp.go @@ -20,7 +20,9 @@ import ( "io" "net" "net/http" + "path/filepath" "strconv" + "strings" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" @@ -217,6 +219,31 @@ func StatusCodeMatches(actual, configured int) bool { return false } +// 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 be outside of root. The resulting path can be used +// with the local file system. +func SanitizedPathJoin(root, reqPath string) string { + if root == "" { + root = "." + } + + path := filepath.Join(root, filepath.Clean("/"+reqPath)) + + // filepath.Join also cleans the path, and cleaning strips + // the trailing slash, so we need to re-add it afterwards. + // if the length is 1, then it's a path to the root, + // and that should return ".", so we don't append the separator. + if strings.HasSuffix(reqPath, "/") && len(reqPath) > 1 { + path += separator + } + + return path +} + // tlsPlaceholderWrapper is a no-op listener wrapper that marks // where the TLS listener should be in a chain of listener wrappers. // It should only be used if another listener wrapper must be placed @@ -242,6 +269,8 @@ const ( DefaultHTTPSPort = 443 ) +const separator = string(filepath.Separator) + // Interface guard var _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil) var _ caddyfile.Unmarshaler = (*tlsPlaceholderWrapper)(nil) -- cgit v1.2.3