Skip to content

fix: reject sign/radix prefix with no digits in BigInt parsing - #110

Open
spokodev wants to merge 1 commit into
GoogleChromeLabs:mainfrom
spokodev:fix/bigint-parse-sign-prefix-no-digits
Open

fix: reject sign/radix prefix with no digits in BigInt parsing#110
spokodev wants to merge 1 commit into
GoogleChromeLabs:mainfrom
spokodev:fix/bigint-parse-sign-prefix-no-digits

Conversation

@spokodev

Copy link
Copy Markdown

Bug

JSBI.BigInt(string) silently returns 0n for strings that consist of a sign or a radix prefix followed by whitespace and no digits, where the native BigInt() parser throws a SyntaxError:

JSBI.BigInt("+ ")   // 0n   (native BigInt: SyntaxError)
JSBI.BigInt("- ")   // 0n   (native BigInt: SyntaxError)
JSBI.BigInt("0x ")  // 0n   (native BigInt: SyntaxError)
JSBI.BigInt("0b ")  // 0n   (native BigInt: SyntaxError)
JSBI.BigInt("0o ")  // 0n   (native BigInt: SyntaxError)

Adjacent inputs are already handled correctly, which is why this slipped through: "+" and "0x" (no trailing whitespace) throw, "" / " " parse to 0n, and "+0" / " 0 " parse fine. The break is specifically a sign/prefix plus trailing whitespace with no digit in between.

Spec

ECMAScript StringToBigInt (via StringIntegerLiteral) requires at least one digit after a sign or a 0x / 0o / 0b radix prefix. A sign or prefix with no following digit is not a valid integer literal, so it must produce SyntaxError, not 0n.

Root cause

__fromString in lib/jsbi.ts consumes an optional sign and an optional 0x / 0o / 0b prefix, then enters the digit-parsing loop. When the remaining body is empty (the next char is whitespace), the digit loop breaks immediately and the result stays 0. The trailing-whitespace cleanup at the end of the function then accepts the whitespace and returns the zero result instead of null. Nothing verifies that at least one value digit was actually consumed after a sign or prefix.

Fix

Record the cursor position where the value digits begin (after the sign, the radix prefix, and any leading zeros). After the digit loop, return null when the cursor has not moved and no leading zero was seen, but a sign or non-decimal radix was present:

if (cursor === bodyStart && !leadingZero && (sign !== 0 || radix !== 10)) {
  return null;
}

leadingZero guards the legitimate "0" / "0x0" style inputs that set it before the loop. The sign !== 0 || radix !== 10 condition keeps the existing behavior for a bare empty / whitespace string (which has no sign and decimal radix and must remain 0n).

Verification

  • Added the five inputs to the existing invalid-input test in tests/tests.mjs. Before the fix the assertion fails (the values parse to 0n instead of throwing); after the fix it passes.
  • Full test suite green (npm test).
  • Differential fuzz of 600,000 inputs (structured cartesian over whitespace / sign / prefix / digit combinations plus random strings) comparing JSBI.BigInt against native BigInt: 0 divergences, no regression on any previously valid input.

@google-cla

google-cla Bot commented Jun 25, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@spokodev

Copy link
Copy Markdown
Author

@googlebot I signed it!

JSBI.BigInt() returned 0n for strings that are a sign or a radix prefix
followed by whitespace and no digits, where native BigInt() throws a
SyntaxError:

  JSBI.BigInt("+ ")   // 0n   (native: SyntaxError)
  JSBI.BigInt("- ")   // 0n   (native: SyntaxError)
  JSBI.BigInt("0x ")  // 0n   (native: SyntaxError)
  JSBI.BigInt("0b ")  // 0n   (native: SyntaxError)
  JSBI.BigInt("0o ")  // 0n   (native: SyntaxError)

__fromString consumed the sign and/or 0x/0o/0b prefix but never checked
that at least one value digit followed. With an empty body the digit loop
breaks immediately on the whitespace and the trailing-whitespace cleanup
returns 0n instead of null. ECMAScript StringToBigInt requires at least
one digit after a sign or radix prefix.

Capture the cursor position where the value digits start and return null
when no digit was consumed after a sign or non-decimal radix prefix.
@spokodev
spokodev force-pushed the fix/bigint-parse-sign-prefix-no-digits branch from e8ffecd to e096470 Compare June 25, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant