Make project name validation more consistent and stricter.

Previously, you could issue e.g. a `GET /%2e%2e/%2e%2e` and it would
get interpreted as a parent directory path segment in the handler.
This didn't result in a path traversal vulnerability when passed to
the S3 backend because of a `path.Clean()` call indirectly done by
`makeWebRoot()`, but it's prudent to not take chances.
This commit is contained in:
Catherine
2025-12-07 20:24:46 +00:00
parent 8fa986015d
commit 62ef4a5366
2 changed files with 18 additions and 12 deletions

View File

@@ -65,10 +65,14 @@ func GetHost(r *http.Request) (string, error) {
return host, nil
}
func IsValidProjectName(name string) bool {
return !strings.HasPrefix(name, ".") && !strings.Contains(name, "%")
}
func GetProjectName(r *http.Request) (string, error) {
// path must be either `/` or `/foo/` (`/foo` is accepted as an alias)
path := strings.TrimPrefix(strings.TrimSuffix(r.URL.Path, "/"), "/")
if path == ".index" || strings.HasPrefix(path, ".index/") {
if !IsValidProjectName(path) {
return "", AuthError{http.StatusBadRequest,
fmt.Sprintf("directory name %q is reserved", ".index")}
} else if strings.Contains(path, "/") {

View File

@@ -132,18 +132,20 @@ func getPage(w http.ResponseWriter, r *http.Request) error {
err = nil
sitePath = strings.TrimPrefix(r.URL.Path, "/")
if projectName, projectPath, hasProjectSlash := strings.Cut(sitePath, "/"); projectName != "" {
var projectManifest *Manifest
var projectMetadata ManifestMetadata
projectManifest, projectMetadata, err = backend.GetManifest(
r.Context(), makeWebRoot(host, projectName),
GetManifestOptions{BypassCache: bypassCache},
)
if err == nil {
if !hasProjectSlash {
writeRedirect(w, http.StatusFound, r.URL.Path+"/")
return nil
if IsValidProjectName(projectName) {
var projectManifest *Manifest
var projectMetadata ManifestMetadata
projectManifest, projectMetadata, err = backend.GetManifest(
r.Context(), makeWebRoot(host, projectName),
GetManifestOptions{BypassCache: bypassCache},
)
if err == nil {
if !hasProjectSlash {
writeRedirect(w, http.StatusFound, r.URL.Path+"/")
return nil
}
sitePath, manifest, metadata = projectPath, projectManifest, projectMetadata
}
sitePath, manifest, metadata = projectPath, projectManifest, projectMetadata
}
}
if manifest == nil && (err == nil || errors.Is(err, ErrObjectNotFound)) {