From 2e8d0fcbbd2ff1469739f2a48e6f87533fc4271d Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Fri, 2 Oct 2026 17:47:10 +0530 Subject: [PATCH 1/2] fix: detect outer functions before serializing method bodies --- index.js | 5 ++-- test/unit/method-bodies.js | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 test/unit/method-bodies.js diff --git a/index.js b/index.js index 8ddc89b..0bb33b2 100644 --- a/index.js +++ b/index.js @@ -15,8 +15,9 @@ var PLACE_HOLDER_REGEXP = new RegExp('(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + // call could start past a `[native code]` match and wrongly report a native // function as safe to serialize. var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/; -var IS_PURE_FUNCTION = /function.*?\(/; -var IS_ARROW_FUNCTION = /.*?=>.*?/; +// Classify the outer function, not a declaration or arrow inside its body. +var IS_PURE_FUNCTION = /^(?:async\s+)?function\b/; +var IS_ARROW_FUNCTION = /^(?:async\s+)?(?:\([\s\S]*?\)|[^\s()]+)\s*=>/; var UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g; // Matches a script end tag (case-insensitive) for XSS protection: either a // literal ``, or a bare ` value; + return nested({ value: 42 }); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(output.method(), 42); + }); + + it('round trips a method containing a function declaration', function () { + const input = { + method() { + function nested() { return 42; } + return nested(); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(output.method(), 42); + }); + + it('preserves async methods containing arrows', async function () { + const input = { + async method() { + const nested = () => 42; + return nested(); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(await output.method(), 42); + }); + + it('preserves generator methods containing arrows', function () { + const input = { + *method() { + const nested = () => 42; + yield nested(); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(output.method().next().value, 42); + }); +}); From 418d062dfa10cba813610e01b398c36d6bf7db92 Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Fri, 2 Oct 2026 17:50:56 +0530 Subject: [PATCH 2/2] test: preserve commented async function signatures --- index.js | 4 ++-- test/unit/method-bodies.js | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 0bb33b2..c3ae087 100644 --- a/index.js +++ b/index.js @@ -16,8 +16,8 @@ var PLACE_HOLDER_REGEXP = new RegExp('(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + // function as safe to serialize. var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/; // Classify the outer function, not a declaration or arrow inside its body. -var IS_PURE_FUNCTION = /^(?:async\s+)?function\b/; -var IS_ARROW_FUNCTION = /^(?:async\s+)?(?:\([\s\S]*?\)|[^\s()]+)\s*=>/; +var IS_PURE_FUNCTION = /^(?:async(?:\s|\/\*[\s\S]*?\*\/|\/\/[^\n]*\n)+)?function\b/; +var IS_ARROW_FUNCTION = /^(?:async(?:\s|\/\*[\s\S]*?\*\/|\/\/[^\n]*\n)+)?(?:\([\s\S]*?\)|[^\s()]+)\s*=>/; var UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g; // Matches a script end tag (case-insensitive) for XSS protection: either a // literal ``, or a bare ` 42]) { + const output = eval('(' + serialize(fn) + ')'); + strictEqual(await output(), 42); + strictEqual(output.constructor.name, 'AsyncFunction'); + } + }); + + it('preserves arrows whose default arguments contain functions', function () { + const fn = (callback = function () { return 42; }) => callback(); + const output = eval('(' + serialize(fn) + ')'); + strictEqual(output(), 42); + }); +});