pinpoint-apm/pinpoint · error · IllegalArgumentException

Webhook URL resolves to a non-public address

Error message

Webhook URL resolves to a non-public address

What it means

validateResolvedAddress rejects addresses that isBlockedAddress classifies as non-public: loopback, link-local, unspecified (0.0.0.0/::), multicast, or other reserved ranges. This prevents webhook calls from reaching internal infrastructure (SSRF mitigation). The URL host may look public but its DNS result points at a protected range.

Source

Thrown at commons-server/src/main/java/com/navercorp/pinpoint/common/server/webhook/WebhookUrlValidator.java:174

    private static void validateHostWithoutResolving(String host) {
        String normalizedHost = normalizeHost(host);
        if (isBlockedHostName(normalizedHost)) {
            throw new IllegalArgumentException("Webhook URL host is not allowed");
        }

        IPAddress address = toHostLiteralAddress(normalizedHost);
        if (address != null) {
            validateResolvedAddress(normalizedHost, address.toInetAddress(), WebhookHostPolicy.denyAll());
        }
    }

    public static void validateResolvedAddress(String host, InetAddress address, WebhookHostPolicy policy) {
        Objects.requireNonNull(policy, "policy");
        if (address == null) {
            throw new IllegalArgumentException("Webhook URL resolved address is required");
        }
        if (isBlockedAddress(address)) {
            throw new IllegalArgumentException("Webhook URL resolves to a non-public address");
        }
        if (isPrivateAddress(address) && !isAllowedPrivateHost(host, policy)) {
            throw new IllegalArgumentException("Webhook URL resolves to a private address that is not allowed");
        }
    }

    /**
     * The policy matches host names only. Allowing an IP literal to match would let a caller
     * reach an internal address without going through an allowed host name.
     */
    private static boolean isAllowedPrivateHost(String host, WebhookHostPolicy policy) {
        if (host == null) {
            return false;
        }
        String normalizedHost = normalizeHost(host);
        if (toHostLiteralAddress(normalizedHost) != null) {
            return false;
        }

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Point the webhook at a genuinely public endpoint; remove internal/metadata addresses from webhook config.
  2. If the address is intentionally internal and trusted, extend WebhookHostPolicy to explicitly allow it (e.g. private-host allowlist).
  3. Audit DNS for the webhook host to see why it resolves to a blocked range.

Example fix

// before
String url = "http://169.254.169.254/latest/meta-data";
// after
String url = "https://hooks.example.com/webhook";
Defensive patterns

Strategy: validation

Validate before calling

InetAddress addr = InetAddress.getByName(host);
if (addr.isLoopbackAddress() || addr.isLinkLocalAddress() || addr.isAnyLocalAddress() || addr.isMulticastAddress()) {
    throw new IllegalArgumentException("webhook host resolves to a non-public address");
}

Type guard

boolean isPublicAddress(InetAddress a) { return !(a.isLoopbackAddress() || a.isLinkLocalAddress() || a.isAnyLocalAddress() || a.isMulticastAddress()); }

Try / catch

try {
    WebhookUrlValidator.validateResolvedAddress(host, addr, policy);
} catch (IllegalArgumentException e) {
    securityLog.warn("Blocked webhook to non-public address: {} -> {}", host, addr);
}

Prevention

When it happens

Trigger: Calling validateResolvedAddress with an InetAddress that is loopback, link-local (169.254.x.x, fe80::), any-local (0.0.0.0), multicast, or otherwise in the blocked set — e.g. 'http://127.0.0.1/hook' or a DNS name resolving to 169.254.169.254.

Common situations: SSRF attempts or misconfiguration where a webhook points to cloud metadata services (169.254.169.254), internal loopback services, or hostnames that recently started resolving to internal IPs.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/a0c494f1a22c5c52. Report an issue: GitHub.