pinpoint-apm/pinpoint · error · IllegalArgumentException

Webhook URL host is not allowed

Error message

Webhook URL host is not allowed

What it means

WebhookUrlValidator.validateAuthority throws IllegalArgumentException when the URL host is a blocked host literal (isBlockedHostLiteral), e.g. localhost, loopback, link-local, or private IP literals. This is an SSRF protection: webhooks must not target internal infrastructure.

Source

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

    private static void validateScheme(URI uri) {
        String scheme = uri.getScheme();
        if (scheme == null) {
            throw new IllegalArgumentException("Webhook URL scheme is required");
        }

        String normalizedScheme = scheme.toLowerCase(Locale.ROOT);
        if (!"http".equals(normalizedScheme) && !"https".equals(normalizedScheme)) {
            throw new IllegalArgumentException("Webhook URL scheme must be http or https");
        }
    }

    private static void validateAuthority(URI uri) {
        if (uri.getHost() == null || uri.getHost().isBlank()) {
            throw new IllegalArgumentException("Webhook URL host is required");
        }
        if (isBlockedHostLiteral(uri.getHost())) {
            throw new IllegalArgumentException("Webhook URL host is not allowed");
        }
        if (uri.getRawUserInfo() != null) {
            throw new IllegalArgumentException("Webhook URL user info is not allowed");
        }
        if (uri.getRawFragment() != null) {
            throw new IllegalArgumentException("Webhook URL fragment is not allowed");
        }
        int port = uri.getPort();
        if (port == -1 && hasExplicitPort(uri)) {
            throw new IllegalArgumentException("Webhook URL port is not valid");
        }
        if (port == 0 || port > MAX_PORT) {
            throw new IllegalArgumentException("Webhook URL port is not allowed");
        }
    }

    private static boolean hasExplicitPort(URI uri) {
        String rawAuthority = uri.getRawAuthority();

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Use a publicly reachable host name / IP for the webhook target
  2. Expose the local test receiver via a tunnel (e.g. ngrok) and use its public URL
  3. If an internal host is legitimately required, extend isBlockedHostLiteral's allowlist consciously (understand the SSRF risk)

Example fix

// before
validator.validateSyntax("http://localhost:8080/webhook");
// after
validator.validateSyntax("https://hooks.example.com/webhook");
Defensive patterns

Strategy: validation

Validate before calling

java.net.InetAddress a = java.net.InetAddress.getByName(java.net.URI.create(url).getHost()); if (a.isLoopbackAddress() || a.isLinkLocalAddress() || a.isSiteLocalAddress()) reject(url);

Type guard

boolean isPublicHost(java.net.URI uri) { try { java.net.InetAddress a = java.net.InetAddress.getByName(uri.getHost()); return !a.isLoopbackAddress() && !a.isLinkLocalAddress() && !a.isSiteLocalAddress(); } catch (Exception e) { return false; } }

Try / catch

try { WebhookUrlValidator.validateSyntax(url); } catch (IllegalArgumentException e) { if (e.getMessage().contains("not allowed")) { reject internal host (SSRF guard); } }

Prevention

When it happens

Trigger: Configuring webhook URLs like 'http://127.0.0.1/hook', 'http://localhost:8080', 'http://169.254.169.254/', 'http://10.0.0.5/hook', 'http://[::1]/' to validateSyntax/uri.

Common situations: Developer points webhook at a local test server; someone tries to reach cloud metadata endpoints (169.254.169.254); internal service URLs mistakenly used as public webhook targets.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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