{"record":{"id":"168eed00af1aaad3","repo":"bytebase/bytebase","slug":"s-must-have-a-host-s","errorCode":null,"errorMessage":"%s must have a host: %s","messagePattern":"(.+?) must have a host: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/idp/wif/jwks.go","lineNumber":130,"sourceCode":"func ValidateIssuerURL(issuerURL string) error {\n\treturn validateRemoteURL(issuerURL, \"issuer URL\")\n}\n\n// ValidateJWKSURL validates that the JWKS URL is a valid HTTPS URL.\nfunc ValidateJWKSURL(jwksURL string) error {\n\treturn validateRemoteURL(jwksURL, \"JWKS URL\")\n}\n\nfunc validateRemoteURL(rawURL, label string) error {\n\tparsed, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"invalid %s\", label)\n\t}\n\tif parsed.Scheme != \"https\" {\n\t\treturn errors.Errorf(\"%s must use HTTPS: %s\", label, rawURL)\n\t}\n\tif parsed.Host == \"\" {\n\t\treturn errors.Errorf(\"%s must have a host: %s\", label, rawURL)\n\t}\n\t// Prevent localhost and private IPs in production (basic SSRF prevention)\n\thost := strings.ToLower(parsed.Hostname())\n\tif host == \"localhost\" || strings.HasPrefix(host, \"127.\") || strings.HasPrefix(host, \"10.\") ||\n\t\tstrings.HasPrefix(host, \"192.168.\") || strings.HasPrefix(host, \"172.\") {\n\t\treturn errors.Errorf(\"%s cannot be a private address: %s\", label, rawURL)\n\t}\n\treturn nil\n}\n\nfunc fetchOIDCConfig(ctx context.Context, configURL string) (*oidcConfig, error) {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, configURL, nil)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to create request\")\n\t}\n\n\tresp, err := httpClient.Do(req)\n\tif err != nil {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/idp/wif/jwks.go#L112-L148","documentation":"validateRemoteURL rejects URLs whose Host portion is empty, i.e. strings without an authority like \"/path\" or \"https:///path\". A URL must name a concrete host from which keys or configuration can be fetched.","triggerScenarios":"Calling ValidateIssuerURL or ValidateJWKSURL with a URL that has no host component — only a path, or a scheme followed by an empty authority.","commonSituations":"Config field left as just a path (\"/jwks\"); template substitution leaving \"https:///jwks\" because the host variable was empty; truncated paste losing the domain part.","solutions":["Add the full authority to the URL: https://<host>/<path>.","Verify the host portion of the config value — it must be non-empty after the scheme and //.","Check that any env/secret variable supplying the hostname is actually set and non-empty.","Re-enter the URL from the provider's documentation verbatim."],"exampleFix":"// before\njwksURL: \"https:///jwks\" // empty host\n// after\njwksURL: fmt.Sprintf(\"https://%s/jwks\", cfg.IDPHost)","handlingStrategy":"validation","validationCode":"u, err := url.Parse(raw)\nif err != nil || u.Host == \"\" {\n    return fmt.Errorf(\"remote URL must include a host\")\n}","typeGuard":"null","tryCatchPattern":"if err := ValidateJWKSURL(raw); err != nil {\n    return fmt.Errorf(\"JWKS URL rejected: %w\", err)\n}","preventionTips":["Always store full absolute URLs including scheme and host","Fail fast on empty host config fields before saving","Verify interpolated host variables are non-empty"],"tags":["url","validation","config"],"backgroundTag":"invalid-url","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}