diff --git a/java/ql/lib/change-notes/2026-04-04-trust-boundary-regexp-barrier.md b/java/ql/lib/change-notes/2026-04-04-trust-boundary-regexp-barrier.md new file mode 100644 index 000000000000..b80c0611b6de --- /dev/null +++ b/java/ql/lib/change-notes/2026-04-04-trust-boundary-regexp-barrier.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `java/trust-boundary-violation` query now recognizes regular expression checks (including `String.matches()` guards and `@javax.validation.constraints.Pattern` annotations) as sanitizers, consistent with the existing treatment of ESAPI validators. This reduces false positives when input is validated against a pattern before being stored in a session. diff --git a/java/ql/lib/semmle/code/java/security/TrustBoundaryViolationQuery.qll b/java/ql/lib/semmle/code/java/security/TrustBoundaryViolationQuery.qll index d234f3df20ce..91e9b18cc9ba 100644 --- a/java/ql/lib/semmle/code/java/security/TrustBoundaryViolationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/TrustBoundaryViolationQuery.qll @@ -40,7 +40,8 @@ module TrustBoundaryConfig implements DataFlow::ConfigSig { predicate isBarrier(DataFlow::Node node) { node instanceof TrustBoundaryValidationSanitizer or node.getType() instanceof HttpServletSession or - node instanceof SimpleTypeSanitizer + node instanceof SimpleTypeSanitizer or + node instanceof RegexpCheckBarrier } predicate isSink(DataFlow::Node sink) { sink instanceof TrustBoundaryViolationSink } diff --git a/java/ql/test/query-tests/security/CWE-501/TrustBoundaryViolations.java b/java/ql/test/query-tests/security/CWE-501/TrustBoundaryViolations.java index d676e3e96783..06e9c6cc929f 100644 --- a/java/ql/test/query-tests/security/CWE-501/TrustBoundaryViolations.java +++ b/java/ql/test/query-tests/security/CWE-501/TrustBoundaryViolations.java @@ -31,5 +31,19 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) { } } catch (Exception e) { } + + // GOOD: A direct String.matches(...) regex check constrains the input before it is written to the session. + String input4 = request.getParameter("input4"); + if (input4.matches("[a-zA-Z0-9]+")) { + request.getSession().setAttribute("input4", input4); + } + } + + @javax.validation.constraints.Pattern(regexp = "^[a-zA-Z0-9]+$") + String validatedField; + + public void doPost(HttpServletRequest request, HttpServletResponse response) { + // GOOD: The field is constrained by a @Pattern annotation. + request.getSession().setAttribute("validated", validatedField); } } diff --git a/java/ql/test/query-tests/security/CWE-501/options b/java/ql/test/query-tests/security/CWE-501/options index 37d627da7e82..15ba67d18321 100644 --- a/java/ql/test/query-tests/security/CWE-501/options +++ b/java/ql/test/query-tests/security/CWE-501/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/esapi-2.0.1:${testdir}/../../../stubs/javax-servlet-2.5 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/esapi-2.0.1:${testdir}/../../../stubs/javax-servlet-2.5:${testdir}/../../../stubs/javax-validation-constraints