Skip to content

JS: treat the base argument of URL as tainted in request forgery queries - #23089

Open
ebickle wants to merge 2 commits into
github:mainfrom
ebickle:js/request-forgery-url-base
Open

ebickle wants to merge 2 commits into
github:mainfrom
ebickle:js/request-forgery-url-base

Conversation

@ebickle

@ebickle ebickle commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

new URL(input, base) only propagated taint from input. A user-controlled base with a relative input decides the host of the resulting URL, so it can reach a request sink but was missed.

Extend isAdditionalRequestForgeryStep to also propagate from the second argument, for both the global URL and require("url").URL.

Fixes #23066
Follows #5630, which added the first-argument step.

`new URL(input, base)` only propagated taint from `input`.
A user-controlled `base` with a relative `input` decides the host of
the resulting URL, so it can reach a request sink but was missed.

Extend `isAdditionalRequestForgeryStep` to also propagate from the
second argument, for both the global `URL` and `require("url").URL`.
@ebickle
ebickle requested a review from a team as a code owner November 10, 2026 20:58
Copilot AI auto review requested due to automatic review settings November 10, 2026 20:58

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

Unconditional URL-base taint propagation causes false positives for absolute URLs, and negative regression tests plus an assessed DCA evaluation of the final change are still needed.

2 open issues
What changed in this PR

Extends JavaScript request-forgery analysis to track user-controlled URL bases, addressing the missed flows described in #23066.

Changes:

  • Propagates taint from both arguments of global and Node.js-imported URL constructors.
  • Adds server-side regression cases and updates expected results.
  • Documents the expanded server-side and client-side detection.
File Description
javascript/​ql/​test/​query-tests/​Security/​CWE-918/​serverSide.js Adds base-argument cases for both constructor forms.
javascript/​ql/​test/​query-tests/​Security/​CWE-918/​RequestForgery.expected Records new alerts and flow paths; adjusts shifted locations.
javascript/​ql/​lib/​semmle/​javascript/​security/​dataflow/​RequestForgeryCustomizations.qll Extends constructor taint propagation to argument 1.
javascript/​ql/​lib/​change-notes/​2026-11-10-request-forgery-url-base.md Announces the request-forgery detection improvement.

Review effort: Max (auto)

exists(DataFlow::NewNode url | url = DataFlow::globalVarRef("URL").getAnInstantiation() |
succ = url and
pred = url.getArgument(0)
pred = url.getArgument([0, 1])
exists(DataFlow::NewNode url | url = DataFlow::globalVarRef("URL").getAnInstantiation() |
succ = url and
pred = url.getArgument(0)
pred = url.getArgument([0, 1])
The base argument of `new URL(...)` is ignored when the first argument is
already an absolute URL, either a string with a scheme or another `URL`
object. Skip the base taint step in those cases.

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.

JavaScript: Request forgery taint blocked by base argument of "new URL(url, base)"

2 participants