{"record":{"id":"80138dc6243591f0","repo":"siyuan-note/siyuan","slug":"access-to-private-internal-ip-is-prohibited","errorCode":null,"errorMessage":"access to private/internal IP is prohibited","messagePattern":"access to private/internal IP is prohibited","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/httprequest.go","lineNumber":57,"sourceCode":"\nconst (\n\tmaxHTTPRequestBytes     = 5 * 1024 * 1024  // text/html、text/plain、application/json 等文本类响应上限\n\tmaxHTTPRequestFileBytes = 10 * 1024 * 1024 // 二进制响应落盘上限\n\tmaxHTTPRequestChars     = 50000\n)\n\n// CheckHostSSRF 校验主机名解析出的 IP 不落在内网/回环等不可达地址段，\n// 防止智能体被诱导发起 SSRF 攻击。web_fetch 与 http_request 共用此校验。\n// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-rg26-cg95-gq6p\nfunc CheckHostSSRF(host string) error {\n\tips, err := net.LookupIP(host)\n\tif err != nil {\n\t\treturn errors.New(\"failed to resolve host: \" + err.Error())\n\t}\n\tfor _, ip := range ips {\n\t\t// 与 SSRFSafeDialer 共用 isPrivateIP，覆盖 NAT64、6to4、Teredo 等 IPv6 过渡地址。\n\t\tif isPrivateIP(ip) {\n\t\t\treturn errors.New(\"access to private/internal IP is prohibited\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// ssrfSafeClient 是智能体出站请求专用的 HTTP 客户端：直连时将目标固定到已校验的公网 IP，\n// 使用代理时则先与用户配置的代理建立隧道，再通过隧道连接固定后的目标 IP，同时保留原始 Host 和 TLS SNI。\n// 两种方式都不会在校验后再次按目标域名解析，避免 DNS 重绑定 TOCTOU 绕过。\n// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-x8gv-g2g3-65fj\nvar ssrfSafeClient = newSSRFSafeClient()\n\nfunc newSSRFSafeClient() *http.Client {\n\treturn newSSRFSafeClientWithResolver(net.DefaultResolver.LookupIPAddr)\n}\n\ntype lookupIPAddrFunc func(context.Context, string) ([]net.IPAddr, error)\n\ntype ssrfSafeTransport struct {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/httprequest.go#L39-L75","documentation":"CheckHostSSRF resolves the host and rejects the request if any resolved IP falls into private/loopback/link-local (or IPv6 transition) ranges, via the shared isPrivateIP helper. This blocks SSRF attacks where an agent is tricked into fetching internal resources (see GHSA-rg26-cg95-gq6p).","triggerScenarios":"HTTPRequest, WebFetch, downloadGeneratedImage, or downloadSkillSource is called with a URL whose host resolves to 127.0.0.1, 10.x/172.16.x/192.168.x, 169.254.x, ::1, fc00::/7, fe80::/10, or NAT64/6to4/Teredo-mapped private addresses.","commonSituations":"AI agent prompt-injection attempting to read localhost admin endpoints or cloud metadata (169.254.169.254); developers testing against a local server through the agent fetch tools; DNS rebinding to a private address.","solutions":["Test local servers through the browser/editor instead of the agent HTTP fetch tools","Use the public URL of the resource; internal addresses are intentionally prohibited","Expose a staging instance on a public host if automated fetching is required","Never disable or bypass CheckHostSSRF — it addresses a published security advisory"],"exampleFix":"// before\nurl: \"http://169.254.169.254/latest/meta-data/\"\n// after\nurl: \"https://api.example.com/public-resource\"","handlingStrategy":"validation","validationCode":"ips, err := net.LookupIP(host)\nif err == nil {\n    for _, ip := range ips {\n        if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() {\n            return fmt.Errorf(\"host %s resolves to a private IP; blocked\", host)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"private/internal IP\") {\n    // treat as intentional block; do not bypass, do not retry\n}","preventionTips":["Never point agent fetch tools at localhost or RFC1918 addresses","DNS rebinding to private IPs is also blocked — trust the check","Use public endpoints for anything the agent must fetch","Do not disable CheckHostSSRF — it fixes a published advisory"],"tags":["network","security","ssrf","private-ip"],"backgroundTag":"path-traversal-blocked","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}