{"record":{"id":"1502f1b3990d3e30","repo":"pinpoint-apm/pinpoint","slug":"webhook-url-host-is-not-allowed","errorCode":null,"errorMessage":"Webhook URL host is not allowed","messagePattern":"Webhook URL host 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":123,"sourceCode":"\n    private static void validateScheme(URI uri) {\n        String scheme = uri.getScheme();\n        if (scheme == null) {\n            throw new IllegalArgumentException(\"Webhook URL scheme is required\");\n        }\n\n        String normalizedScheme = scheme.toLowerCase(Locale.ROOT);\n        if (!\"http\".equals(normalizedScheme) && !\"https\".equals(normalizedScheme)) {\n            throw new IllegalArgumentException(\"Webhook URL scheme must be http or https\");\n        }\n    }\n\n    private static void validateAuthority(URI uri) {\n        if (uri.getHost() == null || uri.getHost().isBlank()) {\n            throw new IllegalArgumentException(\"Webhook URL host is required\");\n        }\n        if (isBlockedHostLiteral(uri.getHost())) {\n            throw new IllegalArgumentException(\"Webhook URL host is not allowed\");\n        }\n        if (uri.getRawUserInfo() != null) {\n            throw new IllegalArgumentException(\"Webhook URL user info is not allowed\");\n        }\n        if (uri.getRawFragment() != null) {\n            throw new IllegalArgumentException(\"Webhook URL fragment is not allowed\");\n        }\n        int port = uri.getPort();\n        if (port == -1 && hasExplicitPort(uri)) {\n            throw new IllegalArgumentException(\"Webhook URL port is not valid\");\n        }\n        if (port == 0 || port > MAX_PORT) {\n            throw new IllegalArgumentException(\"Webhook URL port is not allowed\");\n        }\n    }\n\n    private static boolean hasExplicitPort(URI uri) {\n        String rawAuthority = uri.getRawAuthority();","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/commons-server/src/main/java/com/navercorp/pinpoint/common/server/webhook/WebhookUrlValidator.java#L105-L141","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a publicly reachable host name / IP for the webhook target","Expose the local test receiver via a tunnel (e.g. ngrok) and use its public URL","If an internal host is legitimately required, extend isBlockedHostLiteral's allowlist consciously (understand the SSRF risk)"],"exampleFix":"// before\nvalidator.validateSyntax(\"http://localhost:8080/webhook\");\n// after\nvalidator.validateSyntax(\"https://hooks.example.com/webhook\");","handlingStrategy":"validation","validationCode":"java.net.InetAddress a = java.net.InetAddress.getByName(java.net.URI.create(url).getHost()); if (a.isLoopbackAddress() || a.isLinkLocalAddress() || a.isSiteLocalAddress()) reject(url);","typeGuard":"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; } }","tryCatchPattern":"try { WebhookUrlValidator.validateSyntax(url); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"not allowed\")) { reject internal host (SSRF guard); } }","preventionTips":["Use publicly reachable endpoints for webhooks; tunnel local dev receivers via ngrok","Never target 169.254.169.254 or RFC1918 ranges","Do not weaken the blocked-literal list without an SSRF risk review"],"tags":["java","webhook","security","ssrf"],"backgroundTag":"path-traversal-blocked","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"}