{"record":{"id":"d580ce8959927422","repo":"Tencent/WeKnora","slug":"gateway-url-failed-ssrf-validation-w-for-privat","errorCode":null,"errorMessage":"gateway_url failed SSRF validation: %w (for private deployments, add the hostname to SSRF_WHITELIST)","messagePattern":"gateway_url failed SSRF validation: %w \\(for private deployments, add the hostname to SSRF_WHITELIST\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/qqbot/client.go","lineNumber":106,"sourceCode":"\t}\n\treturn nil\n}\n\nfunc validateGatewayURL(raw string) error {\n\tif strings.TrimSpace(raw) == \"\" {\n\t\treturn nil\n\t}\n\tu, err := url.Parse(raw)\n\tif err != nil || u.Host == \"\" {\n\t\treturn fmt.Errorf(\"gateway_url must be a valid wss URL\")\n\t}\n\tif u.Scheme != \"wss\" {\n\t\treturn fmt.Errorf(\"gateway_url must use wss\")\n\t}\n\tcheckURL := *u\n\tcheckURL.Scheme = \"https\"\n\tif err := secutils.ValidateURLForSSRF(checkURL.String()); err != nil {\n\t\treturn fmt.Errorf(\n\t\t\t\"gateway_url failed SSRF validation: %w (for private deployments, add the hostname to SSRF_WHITELIST)\",\n\t\t\terr,\n\t\t)\n\t}\n\treturn nil\n}\n\nfunc (c *Client) SendC2CMessage(ctx context.Context, openID, content, msgID string) error {\n\tpath := fmt.Sprintf(\"/v2/users/%s/messages\", openID)\n\treturn c.sendText(ctx, path, content, msgID)\n}\n\nfunc (c *Client) SendGroupMessage(ctx context.Context, groupOpenID, content, msgID string) error {\n\tpath := fmt.Sprintf(\"/v2/groups/%s/messages\", groupOpenID)\n\treturn c.sendText(ctx, path, content, msgID)\n}\n\nfunc (c *Client) sendText(ctx context.Context, path, content, msgID string) error {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/qqbot/client.go#L88-L124","documentation":"The gateway host is SSRF-checked by rewriting the wss URL to https and running secutils.ValidateURLForSSRF. Blocked hosts (private IPs, loopback, etc.) fail NewClient/GatewayURL with this wrapped error, which explains the SSRF_WHITELIST opt-in.","triggerScenarios":"gateway_url points at a private/loopback/blocked hostname and SSRF_WHITELIST does not include it.","commonSituations":"Self-hosted or proxied QQ gateway on an internal network; local gateway in development; SSRF_WHITELIST not propagated to the running process.","solutions":["Add the gateway hostname to the SSRF_WHITELIST environment variable","Use the official wss gateway endpoint","Confirm the hostname resolves publicly if you expected it to pass"],"exampleFix":"// before\nGATEWAY_URL=wss://gw.internal:8080/ws  // rejected\n// after\n// export SSRF_WHITELIST=gw.internal\nGATEWAY_URL=wss://gw.internal:8080/ws","handlingStrategy":"validation","validationCode":"u, err := url.Parse(cfg.GatewayURL)\nif err == nil && u.Host != \"\" {\n    httpsURL := *u\n    httpsURL.Scheme = \"https\"\n    _ = httpsURL // run your own SSRF check or ensure host is in SSRF_WHITELIST\n}","typeGuard":null,"tryCatchPattern":"client, err := NewClient(cfg)\nif err != nil && strings.Contains(err.Error(), \"SSRF_WHITELIST\") {\n    return fmt.Errorf(\"gateway host blocked by SSRF guard: %w\", err)\n}","preventionTips":["Whitelist private gateway hosts in SSRF_WHITELIST","Use the official wss gateway endpoint","Check that the hostname resolves publicly"],"tags":["ssrf","security","websocket","qqbot"],"backgroundTag":"ssrf-validation-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}