{"record":{"id":"b0b727b79e2a1bac","repo":"JuliusBrussee/caveman","slug":"provider-q-upstream-url-path-rejected-w","errorCode":null,"errorMessage":"provider %q upstream URL path rejected: %w","messagePattern":"provider %q upstream URL path rejected: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/openaicompat/openaicompat.go","lineNumber":532,"sourceCode":"\t}\n\tu, err := url.Parse(strings.TrimSpace(raw))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL scheme %q is not allowed\", provider, u.Scheme)\n\t}\n\tif !u.IsAbs() || u.Host == \"\" || u.Hostname() == \"\" {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL must be an absolute URL with a host\", provider)\n\t}\n\tif u.User != nil {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL must not include userinfo\", provider)\n\t}\n\tif u.Fragment != \"\" {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL must not include a fragment\", provider)\n\t}\n\tif err := validatePathComponents(u.Path, u.RawPath); err != nil {\n\t\treturn nil, fmt.Errorf(\"provider %q upstream URL path rejected: %w\", provider, err)\n\t}\n\treturn u, nil\n}\n","sourceCodeStart":514,"sourceCodeEnd":536,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/openaicompat/openaicompat.go#L514-L536","documentation":"This is a wrapped error: parseBaseURL runs validatePathComponents on the configured base URL's Path/RawPath (openaicompat.go:531) and re-wraps any rejection as 'provider %q upstream URL path rejected: %w'. validatePathComponents forbids backslashes, empty interior segments (repeated '//'), dot segments ('.'/'..'), and ambiguous escaped sequences (%2f, %5c, %2e) so a URL cannot change route identity after another decoder or proxy hop.","triggerScenarios":"Call ResolveUpstreamURL or ValidateBaseURL with a base URL whose path contains e.g. 'https://api.example.com//v1', '.../v1/../v2', a backslash ('https://api.example.com\\v1'), or an escaped '%2f'/'%2e' in RawPath. The %w verb reveals the specific cause (backslash / repeated separators / dot segments / ambiguous escape).","commonSituations":"Hand-edited YAML with double slashes, Windows-style paths pasted with backslashes, or intentionally obfuscated/redirect-style URLs that use encoded slashes to slip past path-based routing.","solutions":["Read the wrapped cause at the end of the message and remove the offending construct (backslash, '//', '.'/'..', or %2f/%5c/%2e) from the base URL path.","Normalize the path to plain '/'-separated segments and confirm it matches the vendor's documented base path.","Re-run ValidateBaseURL; also check ValidateRequestPath on inbound /compat/ routes, which applies the same rules to request paths."],"exampleFix":"// before\nbase_url: https://api.example.com/v1/../prod\n// after\nbase_url: https://api.example.com/prod","handlingStrategy":"validation","validationCode":"u, _ := url.Parse(raw)\nif err := validatePathComponents(u.Path, u.RawPath); err != nil {\n    return fmt.Errorf(\"base URL path rejected: %w — remove backslashes, '//' runs, dot segments, and %2f/%5c/%2e escapes\", err)\n}","typeGuard":"func hasCleanPath(raw string) bool {\n    u, err := url.Parse(raw)\n    return err == nil && validatePathComponents(u.Path, u.RawPath) == nil\n}","tryCatchPattern":"if _, err := parseBaseURL(raw, provider); err != nil {\n    var cause error\n    if errors.As(err, &cause) || true {\n        log.Fatalf(\"fix the base URL path (see wrapped cause): %v\", err)\n    }\n}","preventionTips":["Write base URL paths as plain '/'-separated lowercase segments; never use '..' or '//'.","Avoid escaped slashes/percent tricks in base URLs — they change route identity across decoders.","Use forward-slash paths even when copying from Windows tooling.","Reuse ValidateRequestPath on inbound routes to catch the same issues symmetrically."],"tags":["go","url-validation","path-traversal","security"],"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"}