{"record":{"id":"8dccfdf82c7e9f10","repo":"SonarSource/sonarqube","slug":"invalid-url-loopback-wildcard-link-local-site","errorCode":null,"errorMessage":"Invalid URL: loopback, wildcard, link-local, site-local, multicast, cloud metadata addresses and addresses of the SonarQube server itself are not allowed for webhooks.","messagePattern":"Invalid URL: loopback, wildcard, link-local, site-local, multicast, cloud metadata addresses and addresses of the SonarQube server itself are not allowed for webhooks\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java","lineNumber":68,"sourceCode":"    userSession.checkEntityPermission(ProjectPermission.ADMIN, projectDto);\n  }\n\n  void checkPermission() {\n    userSession.checkPermission(GlobalPermission.ADMINISTER);\n  }\n\n  void checkUrlPattern(String url, String message, Object... messageArguments) {\n    try {\n      HttpUrl okUrl = HttpUrl.parse(url);\n      if (okUrl == null) {\n        throw new IllegalArgumentException(String.format(message, messageArguments));\n      }\n      InetAddress address = InetAddress.getByName(okUrl.host());\n\n      if (configuration.getBoolean(SONAR_VALIDATE_WEBHOOKS_PROPERTY)\n        .orElse(SONAR_VALIDATE_WEBHOOKS_DEFAULT_VALUE)\n        && WebhookAddressValidator.isBlockedAddress(address, networkInterfaceProvider)) {\n        throw new IllegalArgumentException(WebhookAddressValidator.INVALID_ADDRESS_MESSAGE);\n      }\n    } catch (UnknownHostException e) {\n      // if a host can not be resolved the deliveries will fail - no need to block it from being set\n      // this will only happen for public URLs\n    } catch (SocketException e) {\n      throw new IllegalStateException(\"Can not retrieve a network interfaces\", e);\n    }\n  }\n}\n","sourceCodeStart":50,"sourceCodeEnd":78,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java#L50-L78","documentation":"Thrown by WebhookSupport.checkUrlPattern (via WebhookAddressValidator.INVALID_ADDRESS_MESSAGE) when the resolved webhook host points to a blocked address: loopback, wildcard, link-local, site-local, multicast, cloud metadata, or the SonarQube server itself. This SSRF protection applies when sonar.validate.webhooks (or the configured property) is enabled, which is the default.","triggerScenarios":"Creating/updating a webhook whose URL host resolves to 127.0.0.1/localhost, 0.0.0.0, 169.254.x.x (including the 169.254.169.254 cloud metadata endpoint), 10.x/172.16-31.x/192.168.x addresses, multicast ranges, or the SonarQube server's own address.","commonSituations":"Pointing webhooks at an internal CI listener (e.g. http://localhost:9001/hook) in a containerized/on-prem setup; administrators disabling the validation property to allow internal hosts when they control the network; Kubernetes setups where the internal service IP is site-local.","solutions":["Use a public HTTPS URL for the webhook receiver, or a DNS name that resolves to a public address.","If internal webhooks are intentionally required, set sonar.validate.webhooks=false in sonar.properties (understand the SSRF trade-off) and restart.","Expose the internal receiver through a public reverse proxy/relay instead."],"exampleFix":"// before\ncurl -u $TOKEN -X POST \"$SONAR/api/webhooks/create?name=ci&url=http://localhost:9001/hook\" // blocked\n// after\n# allow internal (trusted network) by disabling validation in sonar.properties:\n# sonar.validate.webhooks=false\ncurl -u $TOKEN -X POST \"$SONAR/api/webhooks/create?name=ci&url=https://ci.example.com/sonarqube/hook\"","handlingStrategy":"validation","validationCode":"InetAddress addr = InetAddress.getByName(host);\nif (addr.isLoopbackAddress() || addr.isAnyLocalAddress() || addr.isLinkLocalAddress()\n    || addr.isSiteLocalAddress() || addr.isMulticastAddress()\n    || host.equals(\"169.254.169.254\")) {\n  throw new IllegalArgumentException(\"Webhook URL points to a blocked/internal address\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  createWebhook(name, url);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"not allowed for webhooks\")) {\n    throw new ConfigurationException(\"Use a public webhook URL or disable sonar.validate.webhooks for trusted internal networks\");\n  }\n}","preventionTips":["Point webhooks at public endpoints; expose internal receivers through a public proxy.","Document why sonar.validate.webhooks is disabled if you must disable SSRF protection.","Resolve the hostname in staging to confirm it is not loopback/link-local/site-local."],"tags":["webhook","ssrf","security","url-validation"],"backgroundTag":"invalid-url","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}