-
Notifications
You must be signed in to change notification settings - Fork 253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(NODE-4874): support EJSON parse for BigInt from $numberLong #552
feat(NODE-4874): support EJSON parse for BigInt from $numberLong #552
Conversation
src/extended_json.ts
Outdated
options = { | ||
useBigInt64: options?.useBigInt64 ?? false, | ||
relaxed: options?.relaxed ?? true, | ||
legacy: options?.legacy ?? false | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did this to clean up how default options are handled. It is not handled cleanly in a number of places in this file
b9c0850
to
2f5db05
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Starting with code change comments, looking at the tests now
…tput of Long.fromExtendedJSON
src/long.ts
Outdated
const defaults = { useBigInt64: false, relaxed: true }; | ||
const ejsonOptions = { ...defaults, ...options }; | ||
if (ejsonOptions.useBigInt64) { | ||
return BigInt(doc.$numberLong); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unlike in BSON and the 'number' case for EJSON, there isn't restrictions on value size or length.
Luckily BigInt casting function will throw an error for bad syntax unlike Number (yay!) (can we add a test? {$numberLong: "blah!"}
)
But we have added support here for syntax that should not work:
{ $numberLong: "0xA" }
should fail{ $numberLong: "0o7" }
should fail{ $numberLong: "0b1" }
should fail
And we've unintentionally allowed strings of any length to be provided here, I'm not sure what would be the best approach here, we can either figure out some way to assert some maximum length. Or we can construct a Long.fromString and use long.toBigInt here, asserting that the fromString logic would prevent an extremely long string from being converted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From discussion: We want to assert a maximum length on the string, this isn't the same as what Long does but our newer API can be better about preventing malformed input. There should be no leading zeros permitted but a plus or minus sign needs to be supported. We can iterate the string or use regexp to assert this.
Consider making a new BSONError subclass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no leading zeros permitted
They are currently permitted when deserializing to Long
, as far as I can tell. I would recommend to keep validation consistent between deserializing to Long
and deserializing to bigint
(and changing validation for deserializing to Long
would probably be breaking, but this might be exactly the right time for that if it’s something you want to do).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're all in agreement (@dariakp @baileympearson @durran @W-A-James) I think we can do the validation for both bigint
and Long
cases, I was aiming to only impact the bigint case here since it would be opt-in, but useBigInt64 and increasing the validation are divergent concerns so we shouldn't overload the option's behavior.
We certainly don't want to add support for the hex, oct, and bin formats just because useBigInt64 is turned on, so that assertion I think we're all on board with.
Long.fromString
currently allows a number of things that probably should be errors like
"-----------123"
- the number of minus signs corresponds to the number of recursivefromString
calls (bigint will throw on this input)"00000000123"
- leading zeros are ignored"12.23"
- becomes 12'8'.repeat(25)
- too large, can be determined by string length (gets truncated)"9223372036854775808"
- too large int64.max + 1 (gets truncated)
We can decide to apply all, some, or none of the above validations, I think the most valuable and least pedantic is applying a string length check, but they're all great if we want to improve things :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that at the very least, we should add the string length check to inputs of fromExtendedJSON
in both the bigInt
and Long
cases to keep with the canonical EJSON spec and to prevent hanging on strings that would be too long to be a valid 64-bit integer and may be the result of malicious user input.
Canonical EJSON representation of a BSON.Long:
{"$numberLong": <64-bit signed integer as a string>}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating with results of Slack discussion;
Going to change bigint case to truncate rather than throw an error on encountering a bigint outside of int64 range to be consistent with behaviour of Long.toString
test/node/bigint.test.ts
Outdated
@@ -367,7 +364,7 @@ describe('BSON BigInt support', function () { | |||
} | |||
}); | |||
|
|||
describe('relaxed double input where double is outside of int32 range and useBigInt64 is true', function () { | |||
describe('relaxed double input where double is outside of int32 range and useBigInt64 is true', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add a test that checks BigInt usage is all behind gated logic. Take a look at the example where we test the library with and without a crypto global here: https://github.com/mongodb/js-bson/blob/main/test/node/byte_utils.test.ts#L603
We can load the BSON library with BigInt set to undefined, that doesn't cover using trailing n syntax but it's something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be appropriate to do this in another PR as just removing the BigInt
field from the globalThis
object isn't enough to be sure that our BigInt work doesn't break anything in environments that don't support BigInts. We'd have to go through the ES BigInt spec and make sure that we're not using any of the associated functionality and that seems outside the scope of this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, let's file a follow up
Description
Adding support to parse
bigint
s from EJSONIs there new documentation needed for these changes?
No
What is the motivation for this change?
Continue BigInt support improvement.
Double check the following
npm run lint
scripttype(NODE-xxxx)[!]: description
feat(NODE-1234)!: rewriting everything in coffeescript