Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions lib/safe-parse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
function isObject(thing) {
return Object.prototype.toString.call(thing) === "[object Object]";
}

function safeJsonParse(thing) {
if (isObject(thing)) return thing;
try {
return JSON.parse(thing);
} catch (e) {
return undefined;
}
}

// Custom JSON parser that preserves big integer precision
function safeJsonParseWithBigInt(text) {
// Use regex to match big integers (including negative numbers) and convert them to strings
var processedText = text.replace(
/:(\s*)(-?\d{16,})/g,
function (match, space, number) {
// If the number exceeds the safe integer range, convert it to a string
if (
parseInt(number) > Number.MAX_SAFE_INTEGER ||
parseInt(number) < Number.MIN_SAFE_INTEGER
) {
return ":" + space + '"' + number + '"';
}
return match;
}
);

try {
return JSON.parse(processedText);
} catch (e) {
// If the processed text fails to parse, try the original text
return JSON.parse(text);
}
}

exports.safeJsonParse = safeJsonParse;
exports.safeJsonParseWithBigInt = safeJsonParseWithBigInt;
21 changes: 9 additions & 12 deletions lib/verify-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@ var toString = require('./tostring');
var util = require('util');
var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/;

function isObject(thing) {
return Object.prototype.toString.call(thing) === '[object Object]';
}

function safeJsonParse(thing) {
if (isObject(thing))
return thing;
try { return JSON.parse(thing); }
catch (e) { return undefined; }
}
var { safeJsonParse, safeJsonParseWithBigInt } = require("./safe-parse");

function headerFromJWS(jwsSig) {
var encodedHeader = jwsSig.split('.', 1)[0];
Expand Down Expand Up @@ -67,8 +58,14 @@ function jwsDecode(jwsSig, opts) {
return null;

var payload = payloadFromJWS(jwsSig);
if (header.typ === 'JWT' || opts.json)
payload = JSON.parse(payload, opts.encoding);
if (header.typ === 'JWT' || opts.json) {
// Use custom parser to preserve big integer precision
if (opts.preserveBigInt !== false) {
payload = safeJsonParseWithBigInt(payload);
} else {
payload = JSON.parse(payload, opts.encoding);
}
}

return {
header: header,
Expand Down
151 changes: 151 additions & 0 deletions test/safeJsonParseWithBigInt.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*global process*/
const test = require("tape");
const { safeJsonParseWithBigInt } = require("../lib/safe-parse");

test("safeJsonParseWithBigInt: Basic functionality test", function (t) {
// Use the specified payload string
const payloadString = '{"userId":13123213123163776887779878}';

const result = safeJsonParseWithBigInt(payloadString);

t.ok(result !== null, "Parse should succeed");
t.equal(
typeof result.userId,
"string",
"Big integer should be parsed as string"
);
t.equal(
result.userId,
"13123213123163776887779878",
"Big integer should maintain precision"
);

// Verify precision is maintained
const originalNumber = 13123213123163776887779878;
t.notEqual(
result.userId,
originalNumber,
"Parsed value should not equal original number (due to precision loss)"
);
t.equal(
result.userId,
"13123213123163776887779878",
"Parsed value should equal the string representation of original number"
);

t.end();
});

test("safeJsonParseWithBigInt: Boundary value test", function (t) {
const testCases = [
{
name: "Maximum safe integer",
json: '{"num":9007199254740991}',
expectedType: "number",
expectedValue: 9007199254740991,
},
{
name: "Exceeds safe integer range",
json: '{"num":9007199254740992}',
expectedType: "string",
expectedValue: "9007199254740992",
},
{
name: "Very large number",
json: '{"num":123456789012345678901234567890}',
expectedType: "string",
expectedValue: "123456789012345678901234567890",
},
{
name: "Minimum safe integer",
json: '{"num":-9007199254740991}',
expectedType: "number",
expectedValue: -9007199254740991,
},
{
name: "Exceeds safe integer range (negative)",
json: '{"num":-9007199254740992}',
expectedType: "string",
expectedValue: "-9007199254740992",
},
];

testCases.forEach(function (testCase) {
const result = safeJsonParseWithBigInt(testCase.json);

t.equal(
typeof result.num,
testCase.expectedType,
`${testCase.name}: Number type should be correct`
);
t.same(
result.num,
testCase.expectedValue,
`${testCase.name}: Number value should match`
);
});

t.end();
});

test("safeJsonParseWithBigInt: Complex object test", function (t) {
const complexJson =
'{"user":{"id":13123213123163776887779878,"name":"test"},"normalNumber":12345,"bigNumber":987654321098765432109876543210}';

const result = safeJsonParseWithBigInt(complexJson);

t.ok(result !== null, "Complex object parsing should succeed");
t.equal(
typeof result.user.id,
"string",
"Big integer in nested object should be parsed as string"
);
t.equal(
result.user.id,
"13123213123163776887779878",
"Big integer in nested object should maintain precision"
);
t.equal(
typeof result.normalNumber,
"number",
"Normal number should remain as number type"
);
t.equal(
typeof result.bigNumber,
"string",
"Big integer should be parsed as string"
);
t.equal(
result.bigNumber,
"987654321098765432109876543210",
"Big integer should maintain precision"
);

t.end();
});

test("safeJsonParseWithBigInt: Array test", function (t) {
const arrayJson =
'[{"id":13123213123163776887779878,"name":"user1"},{"id":12345,"name":"user2"}]';

const result = safeJsonParseWithBigInt(arrayJson);

t.ok(Array.isArray(result), "Result should be an array");
t.equal(
typeof result[0].id,
"string",
"Big integer in array should be parsed as string"
);
t.equal(
result[0].id,
"13123213123163776887779878",
"Big integer in array should maintain precision"
);
t.equal(
typeof result[1].id,
"number",
"Normal number in array should remain as number type"
);

t.end();
});