{"record":{"id":"9cbfe6c55e4457cc","repo":"JuliusBrussee/caveman","slug":"dot-segments-are-not-allowed-in-path","errorCode":null,"errorMessage":"dot segments are not allowed in path","messagePattern":"dot segments are not allowed in path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":497,"sourceCode":"\t\treturn nil\n\t}\n\tif err := validatePathComponents(path, rawPath); err != nil {\n\t\treturn fmt.Errorf(\"compat route path rejected: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc validatePathComponents(path, rawPath string) error {\n\tif strings.Contains(path, `\\`) {\n\t\treturn fmt.Errorf(\"backslash is not allowed in path\")\n\t}\n\tsegments := strings.Split(path, \"/\")\n\tfor i, segment := range segments {\n\t\tif segment == \"\" && i > 0 && i < len(segments)-1 {\n\t\t\treturn fmt.Errorf(\"repeated path separators are not allowed\")\n\t\t}\n\t\tif segment == \".\" || segment == \"..\" {\n\t\t\treturn fmt.Errorf(\"dot segments are not allowed in path\")\n\t\t}\n\t}\n\t// URL.Path is decoded by net/url while RawPath retains a valid escaped\n\t// spelling. Reject separators, backslashes, and dot bytes in either form so\n\t// a path cannot change route identity after another decoder or proxy hop.\n\tfor _, escape := range []string{\"%2f\", \"%5c\", \"%2e\"} {\n\t\tif strings.Contains(strings.ToLower(path), escape) || strings.Contains(strings.ToLower(rawPath), escape) {\n\t\t\treturn fmt.Errorf(\"ambiguous escaped path sequence %s\", escape)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc parseBaseURL(raw, provider string) (*url.URL, error) {\n\tif strings.TrimSpace(raw) == \"\" {\n\t\treturn nil, fmt.Errorf(\"provider %q has no configured upstream URL\", provider)\n\t}\n\tu, err := url.Parse(strings.TrimSpace(raw))","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L479-L515","documentation":"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.","triggerScenarios":"Request path like /compat/../admin or /compat/openai/./v1; BaseURL config containing '..'; traversal probes with multiple dot segments.","commonSituations":"Path traversal attacks/scanners; clients trying to reach sibling routes by climbing out of the compat prefix; misconfigured upstream URLs.","solutions":["Remove dot segments and send the canonical absolute path","Stay within the intended route prefix instead of using '..' to reach other routes","Fix the configured upstream BaseURL to an absolute normalized path"],"exampleFix":"// before\nGET /compat/openai/v1/../v0/models\n\n// after\nGET /compat/openai/v0/models (if that route exists) or use the correct named route","handlingStrategy":"validation","validationCode":"if strings.Contains(path, \"/../\") || strings.HasSuffix(path, \"/..\") || strings.Contains(path, \"/./\") {\n    return errors.New(\"dot segments are not allowed\")\n}","typeGuard":null,"tryCatchPattern":"if err := ValidateRequestPath(req.URL); err != nil {\n    http.Error(w, \"path traversal blocked\", http.StatusBadRequest)\n    return\n}","preventionTips":["Treat any dot segment in API paths as an attack or a bug","Keep route ids within the prefix; never climb with '..'","Normalize paths before logging or routing"],"tags":["go","security","path-traversal","url"],"backgroundTag":"path-traversal-blocked","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}