JuliusBrussee/caveman · error

dot segments are not allowed in path

Error message

dot segments are not allowed in path

What it means

validatePathComponents rejects '.' and '..' segments outright in /compat/ paths and upstream base URLs. Dot segments allow path traversal and are resolved differently by different HTTP stacks, so a path could escape its route prefix after a decode hop. This is a deliberate fail-closed security choice rather than lexical normalization.

Solutions

  1. Remove dot segments and send the canonical absolute path
  2. Stay within the intended route prefix instead of using '..' to reach other routes
  3. Fix the configured upstream BaseURL to an absolute normalized path

Example fix

// before
GET /compat/openai/v1/../v0/models

// after
GET /compat/openai/v0/models (if that route exists) or use the correct named route
Defensive patterns

Strategy: validation

Validate before calling

if strings.Contains(path, "/../") || strings.HasSuffix(path, "/..") || strings.Contains(path, "/./") {
    return errors.New("dot segments are not allowed")
}

Try / catch

if err := ValidateRequestPath(req.URL); err != nil {
    http.Error(w, "path traversal blocked", http.StatusBadRequest)
    return
}

Prevention

When it happens

Trigger: Request path like /compat/../admin or /compat/openai/./v1; BaseURL config containing '..'; traversal probes with multiple dot segments.

Common situations: Path traversal attacks/scanners; clients trying to reach sibling routes by climbing out of the compat prefix; misconfigured upstream URLs.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/9cbfe6c55e4457cc. Report an issue: GitHub.

Appendix: source

Thrown at proxy/providers/openaicompat/openaicompat.go:497

		return nil
	}
	if err := validatePathComponents(path, rawPath); err != nil {
		return fmt.Errorf("compat route path rejected: %w", err)
	}
	return nil
}

func validatePathComponents(path, rawPath string) error {
	if strings.Contains(path, `\`) {
		return fmt.Errorf("backslash is not allowed in path")
	}
	segments := strings.Split(path, "/")
	for i, segment := range segments {
		if segment == "" && i > 0 && i < len(segments)-1 {
			return fmt.Errorf("repeated path separators are not allowed")
		}
		if segment == "." || segment == ".." {
			return fmt.Errorf("dot segments are not allowed in path")
		}
	}
	// URL.Path is decoded by net/url while RawPath retains a valid escaped
	// spelling. Reject separators, backslashes, and dot bytes in either form so
	// a path cannot change route identity after another decoder or proxy hop.
	for _, escape := range []string{"%2f", "%5c", "%2e"} {
		if strings.Contains(strings.ToLower(path), escape) || strings.Contains(strings.ToLower(rawPath), escape) {
			return fmt.Errorf("ambiguous escaped path sequence %s", escape)
		}
	}
	return nil
}

func parseBaseURL(raw, provider string) (*url.URL, error) {
	if strings.TrimSpace(raw) == "" {
		return nil, fmt.Errorf("provider %q has no configured upstream URL", provider)
	}
	u, err := url.Parse(strings.TrimSpace(raw))

View on GitHub (pinned to 3ee70a1026)