From b9af1d04c934715785f995e60cd9e1aa5e268ed7 Mon Sep 17 00:00:00 2001 From: Keshav Malik Date: Tue, 15 Sep 2026 21:23:23 +0530 Subject: [PATCH] Improve URI host allowlist checks --- .../2026-09-15-uri-host-comparisons.md | 4 + .../code/java/security/RequestForgery.qll | 34 ++- .../HostComparisonRedirect.java | 248 ++++++++++++++++++ .../UrlRedirect.expected | 144 ++++++++++ .../UrlRedirect.qlref | 4 + .../security/host-comparison-redirect/options | 1 + .../HostComparison.java | 247 +++++++++++++++++ .../RequestForgery.expected | 162 ++++++++++++ .../RequestForgery.qlref | 4 + .../ssrf-host-comparison-repro/options | 1 + 10 files changed, 845 insertions(+), 4 deletions(-) create mode 100644 java/ql/lib/change-notes/2026-09-15-uri-host-comparisons.md create mode 100644 java/ql/test/query-tests/security/host-comparison-redirect/HostComparisonRedirect.java create mode 100644 java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.expected create mode 100644 java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.qlref create mode 100644 java/ql/test/query-tests/security/host-comparison-redirect/options create mode 100644 java/ql/test/query-tests/security/ssrf-host-comparison-repro/HostComparison.java create mode 100644 java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.expected create mode 100644 java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.qlref create mode 100644 java/ql/test/query-tests/security/ssrf-host-comparison-repro/options diff --git a/java/ql/lib/change-notes/2026-09-15-uri-host-comparisons.md b/java/ql/lib/change-notes/2026-09-15-uri-host-comparisons.md new file mode 100644 index 000000000000..9ad96608e3ef --- /dev/null +++ b/java/ql/lib/change-notes/2026-09-15-uri-host-comparisons.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The queries `java/ssrf` and `java/unvalidated-url-redirection` recognize case-insensitive comparisons of `URI.getHost()` with fixed string allowlist entries, as well as equality and case-insensitive comparisons when the host is stored in a local variable. diff --git a/java/ql/lib/semmle/code/java/security/RequestForgery.qll b/java/ql/lib/semmle/code/java/security/RequestForgery.qll index 489b45dffa2c..e1c77a721535 100644 --- a/java/ql/lib/semmle/code/java/security/RequestForgery.qll +++ b/java/ql/lib/semmle/code/java/security/RequestForgery.qll @@ -13,6 +13,7 @@ import semmle.code.java.frameworks.Properties private import semmle.code.java.controlflow.Guards private import semmle.code.java.dataflow.StringPrefixes private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.SSA private import semmle.code.java.security.Sanitizers /** @@ -122,11 +123,21 @@ private class ExternalRequestForgerySanitizer extends RequestForgerySanitizer { ExternalRequestForgerySanitizer() { barrierNode(this, "request-forgery") } } -/** - * A comparison on the host of a url, that is a sanitizer for URL redirects. - * E.g. `"example.org".equals(url.getHost())"` - */ +/** Gets a host read, following local assignments with a single explicit SSA definition. */ +private MethodCall getCheckedHost(Expr checked) { + result = checked.getUnderlyingExpr() and + result.getMethod().hasQualifiedName("java.net", "URI", "getHost") + or + exists(SsaExplicitWrite def | + checked.getUnderlyingExpr().(VarRead).getVariable() instanceof LocalScopeVariable and + def.getARead() = checked.getUnderlyingExpr() and + result = getCheckedHost(def.getValue()) + ) +} + +/** Holds if a comparison validates the host of a URI. */ private predicate isHostComparisonSanitizer(Guard guard, Expr e, boolean branch) { + // Direct equality checks also accept nonconstant comparison operands. guard = any(MethodCall equalsCall | equalsCall.getMethod().getName() = "equals" and @@ -137,6 +148,21 @@ private predicate isHostComparisonSanitizer(Guard guard, Expr e, boolean branch) e = hostCall.getQualifier() ) ) + or + exists(MethodCall comparison, Expr checked, Expr allowed | + guard = comparison and + comparison.getMethod().getDeclaringType() instanceof TypeString and + comparison.getMethod().hasName(["equals", "equalsIgnoreCase"]) and + branch = true and + ( + checked = comparison.getQualifier() and allowed = comparison.getArgument(0) + or + checked = comparison.getArgument(0) and allowed = comparison.getQualifier() + ) and + // New cases require a fixed allowlist entry, not another untrusted value. + exists(allowed.(CompileTimeConstantExpr).getStringValue()) and + e = getCheckedHost(checked).getQualifier() + ) } /** diff --git a/java/ql/test/query-tests/security/host-comparison-redirect/HostComparisonRedirect.java b/java/ql/test/query-tests/security/host-comparison-redirect/HostComparisonRedirect.java new file mode 100644 index 000000000000..edc5638bec26 --- /dev/null +++ b/java/ql/test/query-tests/security/host-comparison-redirect/HostComparisonRedirect.java @@ -0,0 +1,248 @@ +import java.net.URI; +import java.util.Arrays; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HostComparisonRedirect { + public void unguarded(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + response.sendRedirect(uri.toString()); // $ Alert + } + + public void directEquals(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + if ("example.com".equals(uri.getHost())) { + response.sendRedirect(uri.toString()); + } + } + + public void directEqualsIgnoreCase(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + if ("example.com".equalsIgnoreCase(uri.getHost())) { + response.sendRedirect(uri.toString()); + } + } + + public void reversedEqualsIgnoreCase(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + if (uri.getHost().equalsIgnoreCase("example.com")) { + response.sendRedirect(uri.toString()); + } + } + + public void localEquals(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if ("example.com".equals(host)) { + response.sendRedirect(uri.toString()); + } + } + + public void localEqualsIgnoreCase(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if ("example.com".equalsIgnoreCase(host)) { + response.sendRedirect(uri.toString()); + } + } + + public void streamEquals(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ SPURIOUS: Source + if (Arrays.stream(new String[]{"example.com"}).anyMatch(uri.getHost()::equals)) { + response.sendRedirect(uri.toString()); // $ SPURIOUS: Alert + } + } + + public void reportedStreamHelper(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ SPURIOUS: Source + if (isAllowedStream(uri.getHost(), "example.com")) { + response.sendRedirect(uri.toString()); // $ SPURIOUS: Alert + } + } + + private static boolean isAllowedStream(String host, String... allowedHosts) { + return Arrays.stream(allowedHosts).anyMatch(host::equalsIgnoreCase); + } + + public void reportedLoopHelper(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ SPURIOUS: Source + if (isAllowedLoop(uri.getHost(), "example.com")) { + response.sendRedirect(uri.toString()); // $ SPURIOUS: Alert + } + } + + private static boolean isAllowedLoop(String host, String... allowedHosts) { + for (String allowed : allowedHosts) { + if (allowed.equalsIgnoreCase(host)) { + return true; + } + } + return false; + } + + public void wrongBranch(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (!"example.com".equals(uri.getHost())) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void differentUri(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI checked = new URI(request.getParameter("checked")); + URI used = new URI(request.getParameter("url")); // $ Source + if ("example.com".equals(checked.getHost())) { + response.sendRedirect(used.toString()); // $ Alert + } + } + + public void constantUrl(HttpServletResponse response) throws Exception { + response.sendRedirect("https://example.com/"); + } + + public void caseInsensitiveWrongBranch(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (!"example.com".equalsIgnoreCase(uri.getHost())) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void caseInsensitiveDifferentUri(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI checked = new URI(request.getParameter("checked")); + URI used = new URI(request.getParameter("url")); // $ Source + if ("example.com".equalsIgnoreCase(checked.getHost())) { + response.sendRedirect(used.toString()); // $ Alert + } + } + + public void reassignedHost(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + host = "example.com"; + if ("example.com".equalsIgnoreCase(host)) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void conditionallyReassignedHost(HttpServletRequest request, HttpServletResponse response, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + if (skip) { + host = "example.com"; + } + if ("example.com".equalsIgnoreCase(host)) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void reassignedUri(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI("https://example.com/"); + String host = uri.getHost(); + uri = new URI(request.getParameter("url")); // $ Source + if ("example.com".equalsIgnoreCase(host)) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void untrustedComparison(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (request.getParameter("allowed").equalsIgnoreCase(uri.getHost())) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void unrelatedComparison(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (new FakeComparison().equalsIgnoreCase(uri.getHost())) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + private static class FakeComparison { + boolean equalsIgnoreCase(String host) { + return true; + } + } + + public void aliasChain(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + String alias = host; + String secondAlias = alias; + if ("example.com".equalsIgnoreCase(secondAlias)) { + response.sendRedirect(uri.toString()); + } + } + + public void finallyOverwrite(HttpServletRequest request, HttpServletResponse response, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + try { + if (skip) return; + host = "example.com"; + } finally { + if ("example.com".equalsIgnoreCase(host)) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + } + + public void rejectionReturns(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if (!"example.com".equalsIgnoreCase(host)) return; + response.sendRedirect(uri.toString()); + } + + private static final String ALLOWED_HOST = "example.com"; + + public void constantAllowlistField(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if (ALLOWED_HOST.equalsIgnoreCase(host)) { + response.sendRedirect(uri.toString()); + } + } + + public void orBypass(HttpServletRequest request, HttpServletResponse response, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + if ("example.com".equalsIgnoreCase(host) || skip) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void assignmentCast(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + Object value = uri.getHost(); + String host = (String) value; + if ("example.com".equalsIgnoreCase(host)) { + response.sendRedirect(uri.toString()); + } + } + + public void comparisonCast(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); + Object host = uri.getHost(); + if ("example.com".equalsIgnoreCase((String) host)) { + response.sendRedirect(uri.toString()); + } + } + + public void castHostOverwrite(HttpServletRequest request, HttpServletResponse response) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + Object host = uri.getHost(); + host = "example.com"; + if ("example.com".equalsIgnoreCase((String) host)) { + response.sendRedirect(uri.toString()); // $ Alert + } + } + + public void castHostConditionalOverwrite(HttpServletRequest request, HttpServletResponse response, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + Object host = uri.getHost(); + if (skip) host = "example.com"; + if ("example.com".equalsIgnoreCase((String) host)) { + response.sendRedirect(uri.toString()); // $ Alert + } + } +} diff --git a/java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.expected b/java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.expected new file mode 100644 index 000000000000..9ad424c06406 --- /dev/null +++ b/java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.expected @@ -0,0 +1,144 @@ +#select +| HostComparisonRedirect.java:9:31:9:44 | toString(...) | HostComparisonRedirect.java:8:27:8:53 | getParameter(...) : String | HostComparisonRedirect.java:9:31:9:44 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:8:27:8:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:52:35:52:48 | toString(...) | HostComparisonRedirect.java:50:27:50:53 | getParameter(...) : String | HostComparisonRedirect.java:52:35:52:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:50:27:50:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:59:35:59:48 | toString(...) | HostComparisonRedirect.java:57:27:57:53 | getParameter(...) : String | HostComparisonRedirect.java:59:35:59:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:57:27:57:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:70:35:70:48 | toString(...) | HostComparisonRedirect.java:68:27:68:53 | getParameter(...) : String | HostComparisonRedirect.java:70:35:70:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:68:27:68:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:86:35:86:48 | toString(...) | HostComparisonRedirect.java:84:27:84:53 | getParameter(...) : String | HostComparisonRedirect.java:86:35:86:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:84:27:84:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:94:35:94:49 | toString(...) | HostComparisonRedirect.java:92:28:92:54 | getParameter(...) : String | HostComparisonRedirect.java:94:35:94:49 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:92:28:92:54 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:105:35:105:48 | toString(...) | HostComparisonRedirect.java:103:27:103:53 | getParameter(...) : String | HostComparisonRedirect.java:105:35:105:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:103:27:103:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:113:35:113:49 | toString(...) | HostComparisonRedirect.java:111:28:111:54 | getParameter(...) : String | HostComparisonRedirect.java:113:35:113:49 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:111:28:111:54 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:122:35:122:48 | toString(...) | HostComparisonRedirect.java:118:27:118:53 | getParameter(...) : String | HostComparisonRedirect.java:122:35:122:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:118:27:118:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:133:35:133:48 | toString(...) | HostComparisonRedirect.java:127:27:127:53 | getParameter(...) : String | HostComparisonRedirect.java:133:35:133:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:127:27:127:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:142:35:142:48 | toString(...) | HostComparisonRedirect.java:140:23:140:49 | getParameter(...) : String | HostComparisonRedirect.java:142:35:142:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:140:23:140:49 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:149:35:149:48 | toString(...) | HostComparisonRedirect.java:147:27:147:53 | getParameter(...) : String | HostComparisonRedirect.java:149:35:149:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:147:27:147:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:156:35:156:48 | toString(...) | HostComparisonRedirect.java:154:27:154:53 | getParameter(...) : String | HostComparisonRedirect.java:156:35:156:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:154:27:154:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:184:39:184:52 | toString(...) | HostComparisonRedirect.java:177:27:177:53 | getParameter(...) : String | HostComparisonRedirect.java:184:39:184:52 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:177:27:177:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:210:35:210:48 | toString(...) | HostComparisonRedirect.java:207:27:207:53 | getParameter(...) : String | HostComparisonRedirect.java:210:35:210:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:207:27:207:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:236:35:236:48 | toString(...) | HostComparisonRedirect.java:232:27:232:53 | getParameter(...) : String | HostComparisonRedirect.java:236:35:236:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:232:27:232:53 | getParameter(...) | user-provided value | +| HostComparisonRedirect.java:245:35:245:48 | toString(...) | HostComparisonRedirect.java:241:27:241:53 | getParameter(...) : String | HostComparisonRedirect.java:245:35:245:48 | toString(...) | Untrusted URL redirection depends on a $@. | HostComparisonRedirect.java:241:27:241:53 | getParameter(...) | user-provided value | +edges +| HostComparisonRedirect.java:8:19:8:54 | new URI(...) : URI | HostComparisonRedirect.java:9:31:9:33 | uri : URI | provenance | | +| HostComparisonRedirect.java:8:27:8:53 | getParameter(...) : String | HostComparisonRedirect.java:8:19:8:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:9:31:9:33 | uri : URI | HostComparisonRedirect.java:9:31:9:44 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:50:19:50:54 | new URI(...) : URI | HostComparisonRedirect.java:52:35:52:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:50:27:50:53 | getParameter(...) : String | HostComparisonRedirect.java:50:19:50:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:52:35:52:37 | uri : URI | HostComparisonRedirect.java:52:35:52:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:57:19:57:54 | new URI(...) : URI | HostComparisonRedirect.java:59:35:59:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:57:27:57:53 | getParameter(...) : String | HostComparisonRedirect.java:57:19:57:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:59:35:59:37 | uri : URI | HostComparisonRedirect.java:59:35:59:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:68:19:68:54 | new URI(...) : URI | HostComparisonRedirect.java:70:35:70:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:68:27:68:53 | getParameter(...) : String | HostComparisonRedirect.java:68:19:68:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:70:35:70:37 | uri : URI | HostComparisonRedirect.java:70:35:70:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:84:19:84:54 | new URI(...) : URI | HostComparisonRedirect.java:86:35:86:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:84:27:84:53 | getParameter(...) : String | HostComparisonRedirect.java:84:19:84:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:86:35:86:37 | uri : URI | HostComparisonRedirect.java:86:35:86:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:92:20:92:55 | new URI(...) : URI | HostComparisonRedirect.java:94:35:94:38 | used : URI | provenance | | +| HostComparisonRedirect.java:92:28:92:54 | getParameter(...) : String | HostComparisonRedirect.java:92:20:92:55 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:94:35:94:38 | used : URI | HostComparisonRedirect.java:94:35:94:49 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:103:19:103:54 | new URI(...) : URI | HostComparisonRedirect.java:105:35:105:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:103:27:103:53 | getParameter(...) : String | HostComparisonRedirect.java:103:19:103:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:105:35:105:37 | uri : URI | HostComparisonRedirect.java:105:35:105:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:111:20:111:55 | new URI(...) : URI | HostComparisonRedirect.java:113:35:113:38 | used : URI | provenance | | +| HostComparisonRedirect.java:111:28:111:54 | getParameter(...) : String | HostComparisonRedirect.java:111:20:111:55 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:113:35:113:38 | used : URI | HostComparisonRedirect.java:113:35:113:49 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:118:19:118:54 | new URI(...) : URI | HostComparisonRedirect.java:122:35:122:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:118:27:118:53 | getParameter(...) : String | HostComparisonRedirect.java:118:19:118:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:122:35:122:37 | uri : URI | HostComparisonRedirect.java:122:35:122:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:127:19:127:54 | new URI(...) : URI | HostComparisonRedirect.java:133:35:133:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:127:27:127:53 | getParameter(...) : String | HostComparisonRedirect.java:127:19:127:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:133:35:133:37 | uri : URI | HostComparisonRedirect.java:133:35:133:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:140:15:140:50 | new URI(...) : URI | HostComparisonRedirect.java:142:35:142:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:140:23:140:49 | getParameter(...) : String | HostComparisonRedirect.java:140:15:140:50 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:142:35:142:37 | uri : URI | HostComparisonRedirect.java:142:35:142:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:147:19:147:54 | new URI(...) : URI | HostComparisonRedirect.java:149:35:149:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:147:27:147:53 | getParameter(...) : String | HostComparisonRedirect.java:147:19:147:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:149:35:149:37 | uri : URI | HostComparisonRedirect.java:149:35:149:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:154:19:154:54 | new URI(...) : URI | HostComparisonRedirect.java:156:35:156:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:154:27:154:53 | getParameter(...) : String | HostComparisonRedirect.java:154:19:154:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:156:35:156:37 | uri : URI | HostComparisonRedirect.java:156:35:156:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:177:19:177:54 | new URI(...) : URI | HostComparisonRedirect.java:184:39:184:41 | uri : URI | provenance | | +| HostComparisonRedirect.java:177:27:177:53 | getParameter(...) : String | HostComparisonRedirect.java:177:19:177:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:184:39:184:41 | uri : URI | HostComparisonRedirect.java:184:39:184:52 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:207:19:207:54 | new URI(...) : URI | HostComparisonRedirect.java:210:35:210:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:207:27:207:53 | getParameter(...) : String | HostComparisonRedirect.java:207:19:207:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:210:35:210:37 | uri : URI | HostComparisonRedirect.java:210:35:210:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:232:19:232:54 | new URI(...) : URI | HostComparisonRedirect.java:236:35:236:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:232:27:232:53 | getParameter(...) : String | HostComparisonRedirect.java:232:19:232:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:236:35:236:37 | uri : URI | HostComparisonRedirect.java:236:35:236:48 | toString(...) | provenance | MaD:3 | +| HostComparisonRedirect.java:241:19:241:54 | new URI(...) : URI | HostComparisonRedirect.java:245:35:245:37 | uri : URI | provenance | | +| HostComparisonRedirect.java:241:27:241:53 | getParameter(...) : String | HostComparisonRedirect.java:241:19:241:54 | new URI(...) : URI | provenance | Src:MaD:1 MaD:2 | +| HostComparisonRedirect.java:245:35:245:37 | uri : URI | HostComparisonRedirect.java:245:35:245:48 | toString(...) | provenance | MaD:3 | +models +| 1 | Source: javax.servlet; ServletRequest; false; getParameter; (String); ; ReturnValue; remote; manual | +| 2 | Summary: java.net; URI; false; URI; (String); ; Argument[0]; Argument[this]; taint; manual | +| 3 | Summary: java.net; URI; false; toString; ; ; Argument[this]; ReturnValue; taint; manual | +nodes +| HostComparisonRedirect.java:8:19:8:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:8:27:8:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:9:31:9:33 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:9:31:9:44 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:50:19:50:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:50:27:50:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:52:35:52:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:52:35:52:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:57:19:57:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:57:27:57:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:59:35:59:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:59:35:59:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:68:19:68:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:68:27:68:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:70:35:70:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:70:35:70:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:84:19:84:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:84:27:84:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:86:35:86:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:86:35:86:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:92:20:92:55 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:92:28:92:54 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:94:35:94:38 | used : URI | semmle.label | used : URI | +| HostComparisonRedirect.java:94:35:94:49 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:103:19:103:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:103:27:103:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:105:35:105:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:105:35:105:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:111:20:111:55 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:111:28:111:54 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:113:35:113:38 | used : URI | semmle.label | used : URI | +| HostComparisonRedirect.java:113:35:113:49 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:118:19:118:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:118:27:118:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:122:35:122:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:122:35:122:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:127:19:127:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:127:27:127:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:133:35:133:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:133:35:133:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:140:15:140:50 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:140:23:140:49 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:142:35:142:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:142:35:142:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:147:19:147:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:147:27:147:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:149:35:149:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:149:35:149:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:154:19:154:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:154:27:154:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:156:35:156:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:156:35:156:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:177:19:177:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:177:27:177:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:184:39:184:41 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:184:39:184:52 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:207:19:207:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:207:27:207:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:210:35:210:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:210:35:210:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:232:19:232:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:232:27:232:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:236:35:236:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:236:35:236:48 | toString(...) | semmle.label | toString(...) | +| HostComparisonRedirect.java:241:19:241:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparisonRedirect.java:241:27:241:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparisonRedirect.java:245:35:245:37 | uri : URI | semmle.label | uri : URI | +| HostComparisonRedirect.java:245:35:245:48 | toString(...) | semmle.label | toString(...) | +subpaths diff --git a/java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.qlref b/java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.qlref new file mode 100644 index 000000000000..f41f720f7251 --- /dev/null +++ b/java/ql/test/query-tests/security/host-comparison-redirect/UrlRedirect.qlref @@ -0,0 +1,4 @@ +query: Security/CWE/CWE-601/UrlRedirect.ql +postprocess: + - utils/test/PrettyPrintModels.ql + - utils/test/InlineExpectationsTestQuery.ql diff --git a/java/ql/test/query-tests/security/host-comparison-redirect/options b/java/ql/test/query-tests/security/host-comparison-redirect/options new file mode 100644 index 000000000000..4e9c82fc62ae --- /dev/null +++ b/java/ql/test/query-tests/security/host-comparison-redirect/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/javax-servlet-2.5 diff --git a/java/ql/test/query-tests/security/ssrf-host-comparison-repro/HostComparison.java b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/HostComparison.java new file mode 100644 index 000000000000..163a74d1d8ae --- /dev/null +++ b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/HostComparison.java @@ -0,0 +1,247 @@ +import java.net.URI; +import java.util.Arrays; +import javax.servlet.http.HttpServletRequest; + +public class HostComparison { + public void unguarded(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + uri.toURL().openConnection(); // $ Alert + } + + public void directEquals(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + if ("example.com".equals(uri.getHost())) { + uri.toURL().openConnection(); + } + } + + public void directEqualsIgnoreCase(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + if ("example.com".equalsIgnoreCase(uri.getHost())) { + uri.toURL().openConnection(); + } + } + + public void reversedEqualsIgnoreCase(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + if (uri.getHost().equalsIgnoreCase("example.com")) { + uri.toURL().openConnection(); + } + } + + public void localEquals(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if ("example.com".equals(host)) { + uri.toURL().openConnection(); + } + } + + public void localEqualsIgnoreCase(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if ("example.com".equalsIgnoreCase(host)) { + uri.toURL().openConnection(); + } + } + + public void streamEquals(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ SPURIOUS: Source + if (Arrays.stream(new String[]{"example.com"}).anyMatch(uri.getHost()::equals)) { + uri.toURL().openConnection(); // $ SPURIOUS: Alert + } + } + + public void reportedStreamHelper(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ SPURIOUS: Source + if (isAllowedStream(uri.getHost(), "example.com")) { + uri.toURL().openConnection(); // $ SPURIOUS: Alert + } + } + + private static boolean isAllowedStream(String host, String... allowedHosts) { + return Arrays.stream(allowedHosts).anyMatch(host::equalsIgnoreCase); + } + + public void reportedLoopHelper(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ SPURIOUS: Source + if (isAllowedLoop(uri.getHost(), "example.com")) { + uri.toURL().openConnection(); // $ SPURIOUS: Alert + } + } + + private static boolean isAllowedLoop(String host, String... allowedHosts) { + for (String allowed : allowedHosts) { + if (allowed.equalsIgnoreCase(host)) { + return true; + } + } + return false; + } + + public void wrongBranch(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (!"example.com".equals(uri.getHost())) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void differentUri(HttpServletRequest request) throws Exception { + URI checked = new URI(request.getParameter("checked")); + URI used = new URI(request.getParameter("url")); // $ Source + if ("example.com".equals(checked.getHost())) { + used.toURL().openConnection(); // $ Alert + } + } + + public void constantUrl() throws Exception { + new URI("https://example.com/").toURL().openConnection(); + } + + public void caseInsensitiveWrongBranch(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (!"example.com".equalsIgnoreCase(uri.getHost())) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void caseInsensitiveDifferentUri(HttpServletRequest request) throws Exception { + URI checked = new URI(request.getParameter("checked")); + URI used = new URI(request.getParameter("url")); // $ Source + if ("example.com".equalsIgnoreCase(checked.getHost())) { + used.toURL().openConnection(); // $ Alert + } + } + + public void reassignedHost(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + host = "example.com"; + if ("example.com".equalsIgnoreCase(host)) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void conditionallyReassignedHost(HttpServletRequest request, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + if (skip) { + host = "example.com"; + } + if ("example.com".equalsIgnoreCase(host)) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void reassignedUri(HttpServletRequest request) throws Exception { + URI uri = new URI("https://example.com/"); + String host = uri.getHost(); + uri = new URI(request.getParameter("url")); // $ Source + if ("example.com".equalsIgnoreCase(host)) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void untrustedComparison(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (request.getParameter("allowed").equalsIgnoreCase(uri.getHost())) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void unrelatedComparison(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + if (new FakeComparison().equalsIgnoreCase(uri.getHost())) { + uri.toURL().openConnection(); // $ Alert + } + } + + private static class FakeComparison { + boolean equalsIgnoreCase(String host) { + return true; + } + } + + public void aliasChain(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + String alias = host; + String secondAlias = alias; + if ("example.com".equalsIgnoreCase(secondAlias)) { + uri.toURL().openConnection(); + } + } + + public void finallyOverwrite(HttpServletRequest request, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + try { + if (skip) return; + host = "example.com"; + } finally { + if ("example.com".equalsIgnoreCase(host)) { + uri.toURL().openConnection(); // $ Alert + } + } + } + + public void rejectionReturns(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if (!"example.com".equalsIgnoreCase(host)) return; + uri.toURL().openConnection(); + } + + private static final String ALLOWED_HOST = "example.com"; + + public void constantAllowlistField(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + String host = uri.getHost(); + if (ALLOWED_HOST.equalsIgnoreCase(host)) { + uri.toURL().openConnection(); + } + } + + public void orBypass(HttpServletRequest request, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + String host = uri.getHost(); + if ("example.com".equalsIgnoreCase(host) || skip) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void assignmentCast(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + Object value = uri.getHost(); + String host = (String) value; + if ("example.com".equalsIgnoreCase(host)) { + uri.toURL().openConnection(); + } + } + + public void comparisonCast(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); + Object host = uri.getHost(); + if ("example.com".equalsIgnoreCase((String) host)) { + uri.toURL().openConnection(); + } + } + + public void castHostOverwrite(HttpServletRequest request) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + Object host = uri.getHost(); + host = "example.com"; + if ("example.com".equalsIgnoreCase((String) host)) { + uri.toURL().openConnection(); // $ Alert + } + } + + public void castHostConditionalOverwrite(HttpServletRequest request, boolean skip) throws Exception { + URI uri = new URI(request.getParameter("url")); // $ Source + Object host = uri.getHost(); + if (skip) host = "example.com"; + if ("example.com".equalsIgnoreCase((String) host)) { + uri.toURL().openConnection(); // $ Alert + } + } +} diff --git a/java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.expected b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.expected new file mode 100644 index 000000000000..bd3b7e368158 --- /dev/null +++ b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.expected @@ -0,0 +1,162 @@ +#select +| HostComparison.java:8:9:8:19 | toURL(...) | HostComparison.java:7:27:7:53 | getParameter(...) : String | HostComparison.java:8:9:8:19 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:7:27:7:53 | getParameter(...) | user-provided value | +| HostComparison.java:51:13:51:23 | toURL(...) | HostComparison.java:49:27:49:53 | getParameter(...) : String | HostComparison.java:51:13:51:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:49:27:49:53 | getParameter(...) | user-provided value | +| HostComparison.java:58:13:58:23 | toURL(...) | HostComparison.java:56:27:56:53 | getParameter(...) : String | HostComparison.java:58:13:58:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:56:27:56:53 | getParameter(...) | user-provided value | +| HostComparison.java:69:13:69:23 | toURL(...) | HostComparison.java:67:27:67:53 | getParameter(...) : String | HostComparison.java:69:13:69:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:67:27:67:53 | getParameter(...) | user-provided value | +| HostComparison.java:85:13:85:23 | toURL(...) | HostComparison.java:83:27:83:53 | getParameter(...) : String | HostComparison.java:85:13:85:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:83:27:83:53 | getParameter(...) | user-provided value | +| HostComparison.java:93:13:93:24 | toURL(...) | HostComparison.java:91:28:91:54 | getParameter(...) : String | HostComparison.java:93:13:93:24 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:91:28:91:54 | getParameter(...) | user-provided value | +| HostComparison.java:104:13:104:23 | toURL(...) | HostComparison.java:102:27:102:53 | getParameter(...) : String | HostComparison.java:104:13:104:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:102:27:102:53 | getParameter(...) | user-provided value | +| HostComparison.java:112:13:112:24 | toURL(...) | HostComparison.java:110:28:110:54 | getParameter(...) : String | HostComparison.java:112:13:112:24 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:110:28:110:54 | getParameter(...) | user-provided value | +| HostComparison.java:121:13:121:23 | toURL(...) | HostComparison.java:117:27:117:53 | getParameter(...) : String | HostComparison.java:121:13:121:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:117:27:117:53 | getParameter(...) | user-provided value | +| HostComparison.java:132:13:132:23 | toURL(...) | HostComparison.java:126:27:126:53 | getParameter(...) : String | HostComparison.java:132:13:132:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:126:27:126:53 | getParameter(...) | user-provided value | +| HostComparison.java:141:13:141:23 | toURL(...) | HostComparison.java:139:23:139:49 | getParameter(...) : String | HostComparison.java:141:13:141:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:139:23:139:49 | getParameter(...) | user-provided value | +| HostComparison.java:148:13:148:23 | toURL(...) | HostComparison.java:146:27:146:53 | getParameter(...) : String | HostComparison.java:148:13:148:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:146:27:146:53 | getParameter(...) | user-provided value | +| HostComparison.java:155:13:155:23 | toURL(...) | HostComparison.java:153:27:153:53 | getParameter(...) : String | HostComparison.java:155:13:155:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:153:27:153:53 | getParameter(...) | user-provided value | +| HostComparison.java:183:17:183:27 | toURL(...) | HostComparison.java:176:27:176:53 | getParameter(...) : String | HostComparison.java:183:17:183:27 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:176:27:176:53 | getParameter(...) | user-provided value | +| HostComparison.java:209:13:209:23 | toURL(...) | HostComparison.java:206:27:206:53 | getParameter(...) : String | HostComparison.java:209:13:209:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:206:27:206:53 | getParameter(...) | user-provided value | +| HostComparison.java:235:13:235:23 | toURL(...) | HostComparison.java:231:27:231:53 | getParameter(...) : String | HostComparison.java:235:13:235:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:231:27:231:53 | getParameter(...) | user-provided value | +| HostComparison.java:244:13:244:23 | toURL(...) | HostComparison.java:240:27:240:53 | getParameter(...) : String | HostComparison.java:244:13:244:23 | toURL(...) | Potential server-side request forgery due to a $@. | HostComparison.java:240:27:240:53 | getParameter(...) | user-provided value | +edges +| HostComparison.java:7:19:7:54 | new URI(...) : URI | HostComparison.java:8:9:8:11 | uri : URI | provenance | | +| HostComparison.java:7:27:7:53 | getParameter(...) : String | HostComparison.java:7:19:7:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:7:27:7:53 | getParameter(...) : String | HostComparison.java:7:19:7:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:8:9:8:11 | uri : URI | HostComparison.java:8:9:8:19 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:49:19:49:54 | new URI(...) : URI | HostComparison.java:51:13:51:15 | uri : URI | provenance | | +| HostComparison.java:49:27:49:53 | getParameter(...) : String | HostComparison.java:49:19:49:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:49:27:49:53 | getParameter(...) : String | HostComparison.java:49:19:49:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:51:13:51:15 | uri : URI | HostComparison.java:51:13:51:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:56:19:56:54 | new URI(...) : URI | HostComparison.java:58:13:58:15 | uri : URI | provenance | | +| HostComparison.java:56:27:56:53 | getParameter(...) : String | HostComparison.java:56:19:56:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:56:27:56:53 | getParameter(...) : String | HostComparison.java:56:19:56:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:58:13:58:15 | uri : URI | HostComparison.java:58:13:58:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:67:19:67:54 | new URI(...) : URI | HostComparison.java:69:13:69:15 | uri : URI | provenance | | +| HostComparison.java:67:27:67:53 | getParameter(...) : String | HostComparison.java:67:19:67:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:67:27:67:53 | getParameter(...) : String | HostComparison.java:67:19:67:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:69:13:69:15 | uri : URI | HostComparison.java:69:13:69:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:83:19:83:54 | new URI(...) : URI | HostComparison.java:85:13:85:15 | uri : URI | provenance | | +| HostComparison.java:83:27:83:53 | getParameter(...) : String | HostComparison.java:83:19:83:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:83:27:83:53 | getParameter(...) : String | HostComparison.java:83:19:83:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:85:13:85:15 | uri : URI | HostComparison.java:85:13:85:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:91:20:91:55 | new URI(...) : URI | HostComparison.java:93:13:93:16 | used : URI | provenance | | +| HostComparison.java:91:28:91:54 | getParameter(...) : String | HostComparison.java:91:20:91:55 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:91:28:91:54 | getParameter(...) : String | HostComparison.java:91:20:91:55 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:93:13:93:16 | used : URI | HostComparison.java:93:13:93:24 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:102:19:102:54 | new URI(...) : URI | HostComparison.java:104:13:104:15 | uri : URI | provenance | | +| HostComparison.java:102:27:102:53 | getParameter(...) : String | HostComparison.java:102:19:102:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:102:27:102:53 | getParameter(...) : String | HostComparison.java:102:19:102:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:104:13:104:15 | uri : URI | HostComparison.java:104:13:104:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:110:20:110:55 | new URI(...) : URI | HostComparison.java:112:13:112:16 | used : URI | provenance | | +| HostComparison.java:110:28:110:54 | getParameter(...) : String | HostComparison.java:110:20:110:55 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:110:28:110:54 | getParameter(...) : String | HostComparison.java:110:20:110:55 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:112:13:112:16 | used : URI | HostComparison.java:112:13:112:24 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:117:19:117:54 | new URI(...) : URI | HostComparison.java:121:13:121:15 | uri : URI | provenance | | +| HostComparison.java:117:27:117:53 | getParameter(...) : String | HostComparison.java:117:19:117:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:117:27:117:53 | getParameter(...) : String | HostComparison.java:117:19:117:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:121:13:121:15 | uri : URI | HostComparison.java:121:13:121:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:126:19:126:54 | new URI(...) : URI | HostComparison.java:132:13:132:15 | uri : URI | provenance | | +| HostComparison.java:126:27:126:53 | getParameter(...) : String | HostComparison.java:126:19:126:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:126:27:126:53 | getParameter(...) : String | HostComparison.java:126:19:126:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:132:13:132:15 | uri : URI | HostComparison.java:132:13:132:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:139:15:139:50 | new URI(...) : URI | HostComparison.java:141:13:141:15 | uri : URI | provenance | | +| HostComparison.java:139:23:139:49 | getParameter(...) : String | HostComparison.java:139:15:139:50 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:139:23:139:49 | getParameter(...) : String | HostComparison.java:139:15:139:50 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:141:13:141:15 | uri : URI | HostComparison.java:141:13:141:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:146:19:146:54 | new URI(...) : URI | HostComparison.java:148:13:148:15 | uri : URI | provenance | | +| HostComparison.java:146:27:146:53 | getParameter(...) : String | HostComparison.java:146:19:146:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:146:27:146:53 | getParameter(...) : String | HostComparison.java:146:19:146:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:148:13:148:15 | uri : URI | HostComparison.java:148:13:148:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:153:19:153:54 | new URI(...) : URI | HostComparison.java:155:13:155:15 | uri : URI | provenance | | +| HostComparison.java:153:27:153:53 | getParameter(...) : String | HostComparison.java:153:19:153:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:153:27:153:53 | getParameter(...) : String | HostComparison.java:153:19:153:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:155:13:155:15 | uri : URI | HostComparison.java:155:13:155:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:176:19:176:54 | new URI(...) : URI | HostComparison.java:183:17:183:19 | uri : URI | provenance | | +| HostComparison.java:176:27:176:53 | getParameter(...) : String | HostComparison.java:176:19:176:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:176:27:176:53 | getParameter(...) : String | HostComparison.java:176:19:176:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:183:17:183:19 | uri : URI | HostComparison.java:183:17:183:27 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:206:19:206:54 | new URI(...) : URI | HostComparison.java:209:13:209:15 | uri : URI | provenance | | +| HostComparison.java:206:27:206:53 | getParameter(...) : String | HostComparison.java:206:19:206:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:206:27:206:53 | getParameter(...) : String | HostComparison.java:206:19:206:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:209:13:209:15 | uri : URI | HostComparison.java:209:13:209:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:231:19:231:54 | new URI(...) : URI | HostComparison.java:235:13:235:15 | uri : URI | provenance | | +| HostComparison.java:231:27:231:53 | getParameter(...) : String | HostComparison.java:231:19:231:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:231:27:231:53 | getParameter(...) : String | HostComparison.java:231:19:231:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:235:13:235:15 | uri : URI | HostComparison.java:235:13:235:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +| HostComparison.java:240:19:240:54 | new URI(...) : URI | HostComparison.java:244:13:244:15 | uri : URI | provenance | | +| HostComparison.java:240:27:240:53 | getParameter(...) : String | HostComparison.java:240:19:240:54 | new URI(...) : URI | provenance | Src:MaD:2 Config | +| HostComparison.java:240:27:240:53 | getParameter(...) : String | HostComparison.java:240:19:240:54 | new URI(...) : URI | provenance | Src:MaD:2 MaD:3 | +| HostComparison.java:244:13:244:15 | uri : URI | HostComparison.java:244:13:244:23 | toURL(...) | provenance | MaD:4 Sink:MaD:1 | +models +| 1 | Sink: java.net; URL; false; openConnection; ; ; Argument[this]; request-forgery; manual | +| 2 | Source: javax.servlet; ServletRequest; false; getParameter; (String); ; ReturnValue; remote; manual | +| 3 | Summary: java.net; URI; false; URI; (String); ; Argument[0]; Argument[this]; taint; manual | +| 4 | Summary: java.net; URI; false; toURL; ; ; Argument[this]; ReturnValue; taint; manual | +nodes +| HostComparison.java:7:19:7:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:7:27:7:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:8:9:8:11 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:8:9:8:19 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:49:19:49:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:49:27:49:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:51:13:51:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:51:13:51:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:56:19:56:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:56:27:56:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:58:13:58:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:58:13:58:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:67:19:67:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:67:27:67:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:69:13:69:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:69:13:69:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:83:19:83:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:83:27:83:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:85:13:85:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:85:13:85:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:91:20:91:55 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:91:28:91:54 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:93:13:93:16 | used : URI | semmle.label | used : URI | +| HostComparison.java:93:13:93:24 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:102:19:102:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:102:27:102:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:104:13:104:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:104:13:104:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:110:20:110:55 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:110:28:110:54 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:112:13:112:16 | used : URI | semmle.label | used : URI | +| HostComparison.java:112:13:112:24 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:117:19:117:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:117:27:117:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:121:13:121:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:121:13:121:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:126:19:126:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:126:27:126:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:132:13:132:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:132:13:132:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:139:15:139:50 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:139:23:139:49 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:141:13:141:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:141:13:141:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:146:19:146:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:146:27:146:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:148:13:148:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:148:13:148:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:153:19:153:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:153:27:153:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:155:13:155:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:155:13:155:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:176:19:176:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:176:27:176:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:183:17:183:19 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:183:17:183:27 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:206:19:206:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:206:27:206:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:209:13:209:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:209:13:209:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:231:19:231:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:231:27:231:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:235:13:235:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:235:13:235:23 | toURL(...) | semmle.label | toURL(...) | +| HostComparison.java:240:19:240:54 | new URI(...) : URI | semmle.label | new URI(...) : URI | +| HostComparison.java:240:27:240:53 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| HostComparison.java:244:13:244:15 | uri : URI | semmle.label | uri : URI | +| HostComparison.java:244:13:244:23 | toURL(...) | semmle.label | toURL(...) | +subpaths diff --git a/java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.qlref b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.qlref new file mode 100644 index 000000000000..be2312049e7d --- /dev/null +++ b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/RequestForgery.qlref @@ -0,0 +1,4 @@ +query: Security/CWE/CWE-918/RequestForgery.ql +postprocess: + - utils/test/PrettyPrintModels.ql + - utils/test/InlineExpectationsTestQuery.ql diff --git a/java/ql/test/query-tests/security/ssrf-host-comparison-repro/options b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/options new file mode 100644 index 000000000000..4e9c82fc62ae --- /dev/null +++ b/java/ql/test/query-tests/security/ssrf-host-comparison-repro/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/javax-servlet-2.5