Skip to content

Java: Add Spring MVC URL redirect sinks - #22362

Open
theinfosecguy wants to merge 6 commits into
github:mainfrom
theinfosecguy:java-spring-mvc-redirect-sinks
Open

theinfosecguy wants to merge 6 commits into
github:mainfrom
theinfosecguy:java-spring-mvc-redirect-sinks

Conversation

@theinfosecguy

@theinfosecguy theinfosecguy commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #21771.

Adds stable java/unvalidated-url-redirection coverage for Spring MVC redirect view names and returned RedirectView objects, building on the existing experimental Spring coverage.

Covers leading redirect: concatenations, conditional and branch-assigned names, helpers used directly or as returned ModelAndView names, RedirectView constructors and setUrl, and ModelAndView(View) wrappers. Returned view objects remain covered under response-body annotations; string return values retain their response-body and model-attribute semantics.

The sinks follow values used by a returned view. Regression tests cover ordinary and embedded-prefix names, discarded and model-only values, reassignment, distinct receivers, overloads, and sanitizers. As with existing redirect sinks, later property writes do not suppress alerts.

Validation:

  • CodeQL CLI 2.27.0: stable CWE-601/CWE-552, experimental CWE-601/CWE-625, Apache HTTP, JAX-RS, and Spring framework suites (25 tests)
  • Strict compilation of four affected or overlapping queries with compiler warnings treated as errors
  • QL formatting and whitespace checks on the complete contribution

@theinfosecguy
theinfosecguy marked this pull request as ready for review August 17, 2026 10:28
@theinfosecguy
theinfosecguy requested a review from a team as a code owner August 17, 2026 10:28

@owen-mc owen-mc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in reviewing this.

Comment thread java/ql/lib/semmle/code/java/security/UrlRedirect.qll Outdated
SpringUrlRedirectHelperSink() {
exists(MethodCall call |
this.asExpr() = call and
isSpringMvcViewResult(call) and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This misses return new ModelAndView(redirect(next));, where the helper returns "redirect:" + next. The helper call supplies the view name, but the returned value is the enclosing ModelAndView, so this direct-return check fails. Please also recognize helper calls used as the name of a returned ModelAndView, and add a regression test for this case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this and added a regression test for new ModelAndView(redirect(next)).

Comment thread java/ql/lib/semmle/code/java/security/UrlRedirect.qll Outdated

@owen-mc owen-mc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other sinks in UrlRedirect.qll and UrlForwardQuery.qll do not do post-write overwrite analysis, so no need to do it here. It is overly complicated.

/** Gets the leading expression of a complete view name, following only left-hand concatenation. */
pragma[nomagic]
private Expr getViewNamePrefix(Expr value) {
result = getLocalViewValue(value) and not result instanceof AddExpr

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prefix walk stops at conditional expressions, so return next == null ? redirect("/") : redirect(next); produces no alert when next is request-controlled. This is the same ternary-return pattern as the original report in #21771, so that reported case is still missed. Conditional concatenations and branch-assigned local names are also missed. Please analyze the possible value branches and add the original pattern as a positive regression test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed ternaries and branch-assigned names. Added the original pattern as a regression test.

exists(ReturnStmt ret |
ret.getEnclosingCallable() = value.getEnclosingCallable() and
ret.getEnclosingCallable() instanceof SpringRequestMappingMethod and
DataFlow::localFlow(DataFlow::exprNode(value), DataFlow::exprNode(ret.getExpr())) and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring direct local flow to the return expression misses return new ModelAndView(new RedirectView(next));. The returned object is the outer ModelAndView, so the inner redirect view fails this check. The same happens with view.setUrl(next); return new ModelAndView(view);. Both perform redirects, but neither reports when next is request-controlled. Please follow the ModelAndView(View) argument when identifying returned redirect views, and add regression tests for both forms.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed both forms and added regression tests.


pragma[nomagic]
private predicate appendedToRedirectPrefix(SpringRedirectPrefix prefix, DataFlow::ExprNode exprNode) {
exprNode.asExpr() = prefix.getAnAppendedExpression()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Straight-line local prefix variables (String prefix = "redirect:"; return prefix + next;) fail because getViewNamePrefix follows prefix back to the "redirect:" literal in the variable declaration, where it has no enclosing AddExpr, so InterestingPrefix.getAnAppendedExpression in appendedToRedirectPrefix returns nothing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed local prefix handling and added regression tests.

@theinfosecguy

Copy link
Copy Markdown
Contributor Author

Overwrite analysis: Removed it to match the existing sinks.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False negative in java/unvalidated-url-redirection for Spring MVC redirect view names

3 participants