From a256cb67cc64a7bbde9b0d3df369604bc12fb8d3 Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:40:59 +0000 Subject: [PATCH] feat: add URL.getHost() host comparison as SSRF sanitizer The existing host comparison sanitizer only recognized URI.getHost(). This extends it to also recognize java.net.URL.getHost() followed by an equals check, which is a common pattern for validating the target host before making a request. --- .../code/java/security/RequestForgery.qll | 5 ++- .../CWE-918/UrlHostValidationTest.java | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 java/ql/test/query-tests/security/CWE-918/UrlHostValidationTest.java diff --git a/java/ql/lib/semmle/code/java/security/RequestForgery.qll b/java/ql/lib/semmle/code/java/security/RequestForgery.qll index 489b45dffa2c..37b69ba0384c 100644 --- a/java/ql/lib/semmle/code/java/security/RequestForgery.qll +++ b/java/ql/lib/semmle/code/java/security/RequestForgery.qll @@ -133,7 +133,10 @@ private predicate isHostComparisonSanitizer(Guard guard, Expr e, boolean branch) branch = true and exists(MethodCall hostCall | hostCall = [equalsCall.getQualifier(), equalsCall.getArgument(0)] and - hostCall.getMethod().hasQualifiedName("java.net", "URI", "getHost") and + ( + hostCall.getMethod().hasQualifiedName("java.net", "URI", "getHost") or + hostCall.getMethod().hasQualifiedName("java.net", "URL", "getHost") + ) and e = hostCall.getQualifier() ) ) diff --git a/java/ql/test/query-tests/security/CWE-918/UrlHostValidationTest.java b/java/ql/test/query-tests/security/CWE-918/UrlHostValidationTest.java new file mode 100644 index 000000000000..ccde0537bb63 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-918/UrlHostValidationTest.java @@ -0,0 +1,35 @@ +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class UrlHostValidationTest extends HttpServlet { + private static final String ALLOWED_HOST = "api.example.com"; + private HttpClient client = HttpClient.newHttpClient(); + + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + try { + // GOOD: URL.getHost() is validated before use + String userUrl = request.getParameter("url"); + URL parsedUrl = new URL(userUrl); + if (ALLOWED_HOST.equals(parsedUrl.getHost())) { + HttpRequest r = HttpRequest.newBuilder(parsedUrl.toURI()).build(); + client.send(r, null); + } + + // BAD: no host validation + String unsafeUrl = request.getParameter("url2"); // $ Source + HttpRequest unsafeReq = HttpRequest.newBuilder(new URI(unsafeUrl)).build(); // $ Alert + client.send(unsafeReq, null); // $ Alert + } catch (Exception e) { + // handle exception + } + } +}