Skip to content

Java: Add Spring MVC URL redirect sinks - #22362

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

theinfosecguy wants to merge 8 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.

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)).

@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.

@theinfosecguy

Copy link
Copy Markdown
Contributor Author

Overwrite analysis: Removed it to match the existing sinks.

result = value.getUnderlyingExpr() and
not result instanceof ConditionalExpr and
(
not exists(SsaDefinition def | localViewDefinition(value, def))
Copilot AI dynamic review requested due to automatic review settings November 12, 2026 10:09

@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 re-reviewing. This is pretty much ready to merge. You just need to merge in/rebase on main and fix the merge conflict in UrlRedirect.expected by running codeql test run -j 0 --learn java/ql/test/query-tests/security/CWE-601/semmle/tests.

Copilot AI 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.

🟡 Changes recommended

Unresolved redirect-modeling issues miss tainted destinations and flag fixed or allowlisted targets, causing both false negatives and false positives.

11 open issues
What changed in this PR

Extends the stable Java URL-redirection query to Spring MVC, addressing #21771’s missed redirect view names and helper-mediated redirects.

Changes:

  • Add sink modeling for returned redirect names and RedirectView objects, including helpers and ModelAndView wrappers.
  • Add positive and negative regression cases while preserving string response-body and model-attribute semantics.
  • Update Spring stubs, generated expectations, and release notes.
File Description
java/​ql/​test/​stubs/​springframework-5.8.x/​org/​springframework/​web/​servlet/​view/​AbstractView.java Add the base View implementation stub.
java/​ql/​test/​stubs/​springframework-5.8.x/​org/​springframework/​web/​servlet/​view/​AbstractUrlBasedView.java Connect URL-based views to the stub hierarchy.
java/​ql/​test/​query-tests/​security/​CWE-601/​semmle/​tests/​UrlRedirect.expected Record generated Spring redirect results and flow paths.
java/​ql/​test/​query-tests/​security/​CWE-601/​semmle/​tests/​SpringUrlRedirect.java Add redirect coverage and false-positive regression fixtures.
java/​ql/​test/​query-tests/​security/​CWE-601/​semmle/​tests/​options Include Spring stubs during test extraction.
java/​ql/​lib/​semmle/​code/​java/​security/​UrlRedirect.qll Model Spring redirect values used by returned views.
java/​ql/​lib/​change-notes/​2026-08-17-spring-url-redirect-sinks.md Document the expanded stable-query coverage.

Review effort: Max (auto)

Comment on lines +61 to +62
this.asExpr() = getRedirectTarget(name) and
TaintTracking::localTaint(this, DataFlow::exprNode(name))
Comment on lines +79 to +82
/** The exact Spring redirect prefix, without a fixed target path or host. */
private class SpringRedirectPrefix extends CompileTimeConstantExpr {
SpringRedirectPrefix() { this.getStringValue() = "redirect:" }
}

/** Relates a local read to its reaching SSA definitions. */
private predicate localViewDefinition(Expr value, SsaDefinition def) {
value.getUnderlyingExpr().(VarRead).getVariable() instanceof LocalVariableDecl 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 seems like a small change worth doing.

Comment on lines +94 to +109
not result instanceof ConditionalExpr and
(
not localViewDefinition(value, _)
or
exists(SsaDefinition def |
localViewDefinition(value, def) and
not exists(def.getAnUltimateDefinition().(SsaExplicitWrite).getValue())
)
)
or
exists(SsaDefinition def |
localViewDefinition(value, def) and
result = getAViewNameValue(def.getAnUltimateDefinition().(SsaExplicitWrite).getValue())
)
or
result = getAViewNameValue(value.getUnderlyingExpr().(ConditionalExpr).getABranchExpr())

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 seems like a small change that is worth doing.

exists(MethodCall call, ReturnStmt ret |
callable.(Method).getReturnType() instanceof TypeString and
ret = unique(ReturnStmt r | r.getEnclosingCallable() = callable) and
call = unique(Expr prefix | prefix = getViewNamePrefix(ret.getExpr())) and
Comment on lines +232 to +234
setUrl
.getQualifier()
.getType()

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 seems worth accepting.

Comment on lines +283 to +284
String next = request.getParameter("next"); // $ Source
RedirectView view = new RedirectView(next); // $ Alert

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 should be addressed (assuming you after that they are all FPs)

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

5 participants