node-soap icon indicating copy to clipboard operation
node-soap copied to clipboard

Potential loss of precision when parsing long and integer types

Open Gigiz opened this issue 6 months ago • 2 comments

Hi

I noticed a potential issue in how XML numeric types are parsed. In particular, this section of code:

if (name === 'int' || name === 'integer' || name === 'short' || name === 'long') {
   value = parseInt(text, 10);
}

While this works fine for short and int (which are within JavaScript's safe integer range), it can lead to loss of precision for long and integer types.

In XML Schema:

long is typically a 64-bit signed integer, with values up to ±9.22e18

integer is unbounded, and can be arbitrarily large

JavaScript’s Number type cannot safely represent integers beyond 2^53 - 1 (Number.MAX_SAFE_INTEGER). Using parseInt here may result in inaccurate values for large numbers.

For example:

parseInt("9223372036854775807", 10) // returns 9223372036854776000

which is incorrect

Maybe, consider using BigInt for parsing long and integer values:

if (name === 'long' || name === 'integer') {
   value = BigInt(text);
} 

xs:integer (unlimited)
xs:long (64 bit)
xs:unsignedLong (0 … 2⁶⁴ − 1)
xs:negativeInteger, xs:positiveInteger, xs:nonNegativeInteger, xs:nonPositiveInteger (all subset of xs:integer)
xs:decimal

This would ensure large values are parsed accurately.

Let me know what you think — happy to help with a PR if this approach sounds good.

Thanks again!

Gigiz avatar Jun 26 '25 15:06 Gigiz

Speaking as a user and an occasional contributor...

@Gigiz PR is always a good idea with tests to illustrate the context and the problem. Also please tell the maintainers how you came across this issue, if possible. To me it makes sense to address any kind of overflow issues. I also think "unbounded", even if the spec allows, should be somehow reasonably bounded to avoid any kind of possible buffer overflow attacks, but the bound perhaps can be somehow configurable? Is Number.MAX_SAFE_INTEGER) reasonable here to cover the unbounded case?

smokhov avatar Jun 27 '25 18:06 smokhov

Well, it may use BigInt for cases when value is type of long. But I think this will require some code refactoring. There are maybe issues with strict comparisons in some places cause BigInt('1') != 1

I agree that under some conditions you may hit this issue. But it seems like it was not seen in the wild yet.

I am happy to review a PR if @Gigiz is willing to help with this and try to implement. But this cand be a bit challenging.

w666 avatar Jul 09 '25 08:07 w666