diff --git a/src/node/NodeUtils.ts b/src/node/NodeUtils.ts index 2834fb66..11cf6f30 100644 --- a/src/node/NodeUtils.ts +++ b/src/node/NodeUtils.ts @@ -122,6 +122,11 @@ export class NodeUtils { return node; } + // Handle primitives directly - don't try to clone them as objects + if (typeof node !== 'object') { + return node; + } + const copy: Partial = {}; const nodeKeys: (keyof T)[] = <(keyof T)[]>Object.keys(node); diff --git a/test/functional-tests/node-transformers/converting-transformers/object-pattern-properties-transformer/ObjectPatternPropertiesTransformer.spec.ts b/test/functional-tests/node-transformers/converting-transformers/object-pattern-properties-transformer/ObjectPatternPropertiesTransformer.spec.ts index 69a87036..0a6f85a0 100644 --- a/test/functional-tests/node-transformers/converting-transformers/object-pattern-properties-transformer/ObjectPatternPropertiesTransformer.spec.ts +++ b/test/functional-tests/node-transformers/converting-transformers/object-pattern-properties-transformer/ObjectPatternPropertiesTransformer.spec.ts @@ -3,6 +3,7 @@ import { assert } from 'chai'; import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes'; import { readFileAsString } from '../../../../helpers/readFileAsString'; +import { evalLocal } from '../../../../helpers/evalLocal'; import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade'; @@ -142,4 +143,86 @@ describe('ObjectPatternPropertiesTransformer', () => { }); }); }); + + describe('Variant #3: destructuring default parameter with var shadowing', () => { + describe('Variant #1: destructuring default should reference outer var, not inner var', () => { + const code = ` + (function() { + var x = 'outer'; + function f({ a = x } = {}) { + var x = 'inner'; + return a; + } + return f(); + })(); + `; + + let obfuscatedCode: string; + + before(() => { + obfuscatedCode = JavaScriptObfuscator.obfuscate(code, { + ...NO_ADDITIONAL_NODES_PRESET + }).getObfuscatedCode(); + }); + + it('should correctly resolve destructuring default to outer variable', () => { + // Default parameter `a = x` should use outer 'x' value ('outer'), + // NOT the inner var x = 'inner' which is in the function body scope + assert.equal(evalLocal(code), 'outer'); + assert.equal(evalLocal(obfuscatedCode), evalLocal(code)); + }); + }); + + describe('Variant #2: nested destructuring default should reference outer var', () => { + const code = ` + (function() { + var x = 'outer'; + function f({ a: { b = x } } = { a: {} }) { + var x = 'inner'; + return b; + } + return f(); + })(); + `; + + let obfuscatedCode: string; + + before(() => { + obfuscatedCode = JavaScriptObfuscator.obfuscate(code, { + ...NO_ADDITIONAL_NODES_PRESET + }).getObfuscatedCode(); + }); + + it('should correctly resolve nested destructuring default to outer variable', () => { + assert.equal(evalLocal(code), 'outer'); + assert.equal(evalLocal(obfuscatedCode), evalLocal(code)); + }); + }); + + describe('Variant #3: simple default param with var shadow', () => { + const code = ` + (function() { + var x = 'outer'; + function f(a = x) { + var x = 'inner'; + return a; + } + return f(); + })(); + `; + + let obfuscatedCode: string; + + before(() => { + obfuscatedCode = JavaScriptObfuscator.obfuscate(code, { + ...NO_ADDITIONAL_NODES_PRESET + }).getObfuscatedCode(); + }); + + it('should correctly resolve default param to outer variable', () => { + assert.equal(evalLocal(code), 'outer'); + assert.equal(evalLocal(obfuscatedCode), evalLocal(code)); + }); + }); + }); });