{"record":{"id":"6a4a2e15286a05c3","repo":"pinpoint-apm/pinpoint","slug":"webhook-url-resolves-to-a-private-address-that-is","errorCode":null,"errorMessage":"Webhook URL resolves to a private address that is not allowed","messagePattern":"Webhook URL resolves to a private address that is not allowed","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"commons-server/src/main/java/com/navercorp/pinpoint/common/server/webhook/WebhookUrlValidator.java","lineNumber":177,"sourceCode":"            throw new IllegalArgumentException(\"Webhook URL host is not allowed\");\n        }\n\n        IPAddress address = toHostLiteralAddress(normalizedHost);\n        if (address != null) {\n            validateResolvedAddress(normalizedHost, address.toInetAddress(), WebhookHostPolicy.denyAll());\n        }\n    }\n\n    public static void validateResolvedAddress(String host, InetAddress address, WebhookHostPolicy policy) {\n        Objects.requireNonNull(policy, \"policy\");\n        if (address == null) {\n            throw new IllegalArgumentException(\"Webhook URL resolved address is required\");\n        }\n        if (isBlockedAddress(address)) {\n            throw new IllegalArgumentException(\"Webhook URL resolves to a non-public address\");\n        }\n        if (isPrivateAddress(address) && !isAllowedPrivateHost(host, policy)) {\n            throw new IllegalArgumentException(\"Webhook URL resolves to a private address that is not allowed\");\n        }\n    }\n\n    /**\n     * The policy matches host names only. Allowing an IP literal to match would let a caller\n     * reach an internal address without going through an allowed host name.\n     */\n    private static boolean isAllowedPrivateHost(String host, WebhookHostPolicy policy) {\n        if (host == null) {\n            return false;\n        }\n        String normalizedHost = normalizeHost(host);\n        if (toHostLiteralAddress(normalizedHost) != null) {\n            return false;\n        }\n        return policy.isAllowed(normalizedHost);\n    }\n","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/commons-server/src/main/java/com/navercorp/pinpoint/common/server/webhook/WebhookUrlValidator.java#L159-L195","documentation":"validateResolvedAddress allows private addresses only when the host name matches the supplied WebhookHostPolicy allowlist (isAllowedPrivateHost). If the address is private but the host is not allowed by policy, this error is thrown. The policy intentionally matches host names only — an IP literal can never satisfy the allowlist, preventing callers from bypassing it.","triggerScenarios":"Calling validateResolvedAddress with a private address (e.g. 10.x.x.x, 192.168.x.x, 172.16-31.x.x) while isAllowedPrivateHost(host, policy) returns false — either no allowlist configured or the host name differs from policy entries; also IP-literal URLs, which are always denied.","commonSituations":"Pointing webhooks at internal on-prem services (10.x/192.168.x) without configuring the private-host allowlist, or using a raw IP literal hoping the allowlist matches.","solutions":["Add the specific internal host name to the WebhookHostPolicy allowlist for private addresses.","Use a host name (not an IP literal) so it can match the policy allowlist.","If the endpoint should be public, reconfigure it on a public address."],"exampleFix":"// before\nWebhookUrlValidator.validateResolvedAddress(\"internal.svc\", addr, WebhookHostPolicy.denyAll());\n// after\nWebhookHostPolicy policy = WebhookHostPolicy.builder().allowPrivateHost(\"internal.svc\").build();\nWebhookUrlValidator.validateResolvedAddress(\"internal.svc\", addr, policy);","handlingStrategy":"validation","validationCode":"InetAddress addr = InetAddress.getByName(host);\nif (addr.isSiteLocalAddress() && !policyAllows(host)) {\n    throw new IllegalArgumentException(\"private address requires an allowlisted host: \" + host);\n}","typeGuard":"boolean privateHostAllowed(String host, WebhookHostPolicy p) { return p != null && p.allows(host); }","tryCatchPattern":"try {\n    WebhookUrlValidator.validateResolvedAddress(host, addr, policy);\n} catch (IllegalArgumentException e) {\n    throw new ConfigurationException(\"Private webhook host not allowlisted: \" + host);\n}","preventionTips":["Always use host names (never IP literals) so the policy allowlist can match","Maintain an explicit allowlist of internal hosts that need webhooks","Document that policies match host names only, not IP literals"],"tags":["ssrf","security","webhook","private-network"],"backgroundTag":"invalid-url","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}