Skip to content

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

Open

Description

@ebickle

Description of the issue

CodeQL fails to detect JavaScript request forgery vulnerabilities where the taint (user input) is passed through the base argument of the new URL(url, base) constructor.

Reproduction steps

CodeQL correctly detects a server-side request forgery issue in this code:

app.get("/head", async (req, res) => {
  const via = req.headers["via"];
  const url = new URL(`https://${via}/assets/head.html`);
  const response = await fetch(url.href);
  res.send(await response.text());
});

But fails to detect the following issue:

  const via = req.headers["via"];
  const url = new URL("/assets/head.html", `https://${via}`);
  const response = await fetch(url.href);
  res.send(await response.text());

Other details
The base argument of new URL(url, base) doesn't flow taint into the URL object in every situation; for example, an absolute URL supplied in the url could cause base to be ignored. For that reason, it's unclear whether this was an intentional design to reduce false positives or an omission; I'm suspecting the later.

The code with the issue is likely here:

exists(DataFlow::NewNode url | url = DataFlow::globalVarRef("URL").getAnInstantiation() |
succ = url and
pred = url.getArgument(0)
)

Note the "url.getArgument(0)" but no equivalent for the second argument.

Activity

  1. ebickle commented on Nov 9, 2026

    @ebickle
    ContributorAuthor

    Sample and reproduction source code:
    ssrf-uri-examples.zip

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions