{"record":{"id":"545b9a9d17cbe040","repo":"chenhg5/cc-connect","slug":"remote-image-host-resolved-to-blocked-ip-s","errorCode":null,"errorMessage":"remote image host resolved to blocked IP %s","messagePattern":"remote image host resolved to blocked IP (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/feishu/feishu.go","lineNumber":6673,"sourceCode":"\t\t}\n\t\tfor _, addr := range resolved {\n\t\t\tips = append(ips, addr.IP)\n\t\t}\n\t}\n\n\tvar firstBlocked net.IP\n\tfor _, ip := range ips {\n\t\tif isBlockedRichCardImageIP(ip) {\n\t\t\tif firstBlocked == nil {\n\t\t\t\tfirstBlocked = ip\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tdialer := &net.Dialer{Timeout: richCardImageFinalWait}\n\t\treturn dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port))\n\t}\n\tif firstBlocked != nil {\n\t\treturn nil, fmt.Errorf(\"remote image host resolved to blocked IP %s\", firstBlocked.String())\n\t}\n\treturn nil, errors.New(\"remote image host resolved to no usable IPs\")\n}\n\nfunc isBlockedRichCardImageIP(ip net.IP) bool {\n\taddr, err := netip.ParseAddr(ip.String())\n\tif err != nil {\n\t\treturn true\n\t}\n\taddr = addr.Unmap()\n\treturn !addr.IsGlobalUnicast() ||\n\t\taddr.IsLoopback() ||\n\t\taddr.IsPrivate() ||\n\t\taddr.IsLinkLocalUnicast() ||\n\t\taddr.IsLinkLocalMulticast() ||\n\t\taddr.IsMulticast() ||\n\t\taddr.IsUnspecified() ||\n\t\trichCardImageIPInBlockedPrefix(addr)","sourceCodeStart":6655,"sourceCodeEnd":6691,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/feishu.go#L6655-L6691","documentation":"Part of SSRF protection: the custom dialer resolves the image host and checks each IP against isBlockedRichCardImageIP (private/loopback/link-local/metadata ranges). All candidate IPs were blocked, so the dial is refused and this error names the blocked IP for diagnosis.","triggerScenarios":"A card image URL whose hostname resolves only to a blocked address — localhost, 127.0.0.1, 10.x/172.16.x/192.168.x, 169.254.x (cloud metadata), IPv6 loopback/ULA.","commonSituations":"Configured image URL using localhost or an internal hostname; DNS rebinding where public name resolves to internal IP; misconfigured internal CDN name; literal internal IP pasted as the image host.","solutions":["Host the image on a genuinely public endpoint and update the URL","If internal images are required, proxy them through a service with an approved egress path","Check DNS records for the hostname — it must not resolve to RFC1918/link-local ranges","Do not bypass the SSRF check; it protects against cloud metadata credential theft"],"exampleFix":"// before\n\"image_url\": \"http://internal-nas.local/photo.png\"\n// after\n\"image_url\": \"https://public-cdn.example.com/photo.png\"","handlingStrategy":"validation","validationCode":"ips, err := net.LookupHost(host)\n// reject if any ip is private/loopback/link-local before configuring the URL","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never configure internal/localhost image URLs","Audit DNS for image hostnames","Use a public CDN for card images"],"tags":["ssrf","security","dns","image"],"backgroundTag":"path-traversal-blocked","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}