Skip to content

JS: Stop treating parseInt and parseFloat as whole-string number guards - #23121

Merged
sam-robson merged 2 commits into
mainfrom
sam-robson/js-isnumberguard-parseint
Nov 13, 2026
Merged

sam-robson merged 2 commits into
mainfrom
sam-robson/js-isnumberguard-parseint

Conversation

@sam-robson

@sam-robson sam-robson commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #23115

Summary

TaintTracking::isNumberGuard treated isNaN(parseInt(x)) and isNaN(parseFloat(x)) as checks that x is a number. Both functions read only a leading numeric prefix and ignore trailing characters, so the original string x can still carry a payload to a sink. Remove the parseInt and parseFloat branch from isNumberGuard. Whole-value checks such as isNaN(x), isNaN(+x), isNaN(x - 0), and typeof x === "number" remain unchanged.

x = "1; rm -rf /"
  isNaN(x)            -> ToNumber(x) is NaN  -> guard rejects x
  isNaN(parseInt(x))  -> parseInt(x) is 1    -> guard passes, unparsed x reaches sink

Evidence

  • codeql test run passes on UnsafeShellCommandConstruction and the other isNumberGuard query suites.

@sam-robson
sam-robson marked this pull request as ready for review November 12, 2026 17:29
@sam-robson
sam-robson requested a review from a team as a code owner November 12, 2026 17:29
Copilot AI dynamic review requested due to automatic review settings November 12, 2026 17:29

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

The shared guard change can introduce prototype-pollution false positives for numerically guarded keys, requiring a query-specific guard and regression coverage.

2 open issues
What changed in this PR

This PR corrects JavaScript taint analysis so numeric-prefix parsing no longer sanitizes the original string, allowing injection queries to detect trailing payloads.

Changes:

  • Removes parseInt and parseFloat handling from isNumberGuard, preserving whole-value checks.
  • Updates parseInt alert expectations and adds parseFloat regression coverage plus a safe parsed-value case.
  • Adds a change note describing potentially newly reported alerts.
File Description
javascript/​ql/​test/​query-tests/​Security/​CWE-078/​UnsafeShellCommandConstruction/​UnsafeShellCommandConstruction.expected Records alerts for unsafe numeric-prefix guards.
javascript/​ql/​test/​query-tests/​Security/​CWE-078/​UnsafeShellCommandConstruction/​lib/​lib.js Updates guard expectations and adds regression cases.
javascript/​ql/​lib/​semmle/​javascript/​dataflow/​TaintTracking.qll Stops treating prefix parsing as whole-string validation.
javascript/​ql/​lib/​change-notes/​2026-11-12-isnumberguard-parseint.md Documents the analysis change and affected alerts.

Review effort: Max (auto)

@@ -989,13 +989,6 @@ module TaintTracking {
|
operand = isNaN.getArgument(0).asExpr()
or

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 investigated before merging.

@@ -989,13 +989,6 @@ module TaintTracking {
|
operand = isNaN.getArgument(0).asExpr()
or
owen-mc
owen-mc previously approved these changes Nov 12, 2026
Comment on lines -992 to -998
exists(DataFlow::CallNode parse |
parse = DataFlow::globalVarRef(["parseInt", "parseFloat"]).getACall()
|
parse = isNaN.getArgument(0) and
operand = parse.getArgument(0).asExpr()
)
or

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.

A corresponding disjunct should be added to the characteristic predicate of NumberGuard for prototype pollution, as the case that is being removed is still valid for the purpose of that class.

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.

👍🏻 Added in 1317435

@sam-robson
sam-robson requested a review from owen-mc November 13, 2026 11:21
@sam-robson
sam-robson merged commit d23bbc4 into main Nov 13, 2026
21 of 22 checks passed
@sam-robson
sam-robson deleted the sam-robson/js-isnumberguard-parseint branch November 13, 2026 12:14
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.

JS: Should isNaN(parseInt(x)) count as a check that x is a number, given that parseInt only reads a numeric prefix?

3 participants