{"record":{"id":"cc09d98e2a9d5174","repo":"JuliusBrussee/caveman","slug":"ssrf-scheme-q-not-permitted-managed-mode-requir","errorCode":null,"errorMessage":"ssrf: scheme %q not permitted (managed mode requires https)","messagePattern":"ssrf: scheme %q not permitted \\(managed mode requires https\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/ssrf/ssrf.go","lineNumber":167,"sourceCode":"func SelfHostedConfig(allowList ...string) Config {\n\treturn Config{ManagedMode: false, AllowList: allowList}\n}\n\n// ValidateURL resolves raw to a URL, validates the scheme/port constraints,\n// and checks every IP the hostname resolves to against the SSRF block lists.\n// It is a pre-flight check only — see NewDialContext for dial-time enforcement.\n//\n// Errors are safe to return to callers; they contain the blocked IP but never\n// the original credential material.\nfunc ValidateURL(ctx context.Context, raw string, cfg Config) error {\n\tu, err := url.Parse(raw)\n\tif err != nil {\n\t\t// net/url.Error includes the raw URL (and may therefore include\n\t\t// credentials or query secrets). Keep this error field-only and stable.\n\t\treturn errors.New(\"ssrf: invalid URL\")\n\t}\n\tif u.Scheme != \"https\" && !(u.Scheme == \"http\" && !cfg.ManagedMode) {\n\t\treturn fmt.Errorf(\"ssrf: scheme %q not permitted (managed mode requires https)\", u.Scheme)\n\t}\n\tif u.User != nil {\n\t\treturn fmt.Errorf(\"ssrf: credentials embedded in URL are forbidden\")\n\t}\n\thost := u.Hostname()\n\tif host == \"\" {\n\t\treturn fmt.Errorf(\"ssrf: URL must contain a host\")\n\t}\n\tport := u.Port()\n\tif cfg.ManagedMode && port != \"\" && port != \"443\" {\n\t\treturn errors.New(\"ssrf: managed mode requires port 443\")\n\t}\n\tif port == \"\" {\n\t\tif u.Scheme == \"https\" {\n\t\t\tport = \"443\"\n\t\t} else {\n\t\t\tport = \"80\"\n\t\t}","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/ssrf/ssrf.go#L149-L185","documentation":"ssrf.ValidateURL only permits https URLs; plain http is tolerated solely when Config.ManagedMode is false (self-hosted deployments). Any other scheme (http in managed mode, ftp, ws, file, ...) is rejected before any DNS or network work. Errors are safe to return to callers and deliberately exclude credential material.","triggerScenarios":"Calling ssrf.ValidateURL with an http:// URL while cfg.ManagedMode is true; or with any non-https/non-http scheme such as ftp://, ws://, file:// in either mode.","commonSituations":"A user-supplied webhook or provider endpoint configured as http:// in the managed/SaaS environment; local dev URLs (http://localhost:8000) accidentally shipped to a managed deployment; typos like 'httpss://'.","solutions":["Change the endpoint URL to https (most providers support it).","If this is a self-hosted deployment that genuinely needs http, ensure ManagedMode is false in the SSRF Config passed to ValidateURL.","Reject the URL at ingestion (form/API validation) with a clear message so users fix it before runtime."],"exampleFix":"// before\nurl := \"http://api.internal.local:8080/hook\"\nif err := ssrf.ValidateURL(ctx, url, cfg); err != nil { ... } // blocked in managed mode\n\n// after\nurl := \"https://api.internal.local:8443/hook\"\nif err := ssrf.ValidateURL(ctx, url, cfg); err != nil { ... }","handlingStrategy":"validation","validationCode":"u, err := url.Parse(raw)\nif err == nil && u.Scheme != \"https\" && !(u.Scheme == \"http\" && !cfg.ManagedMode) {\n    return fmt.Errorf(\"use an https URL\")\n}","typeGuard":"func isPermittedScheme(raw string, managed bool) bool {\n    u, err := url.Parse(raw)\n    if err != nil { return false }\n    return u.Scheme == \"https\" || (u.Scheme == \"http\" && !managed)\n}","tryCatchPattern":"if err := ssrf.ValidateURL(ctx, raw, cfg); err != nil {\n    if strings.Contains(err.Error(), \"scheme\") {\n        // ask the user for the https variant of the endpoint\n    }\n}","preventionTips":["Validate endpoint URLs at config-ingestion time, not at request time.","Default new integrations to https; treat http as a self-hosted-only exception."],"tags":["ssrf","network","validation","security","go"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}