mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Backport some fixes from Pro version (#1374)
This commit is contained in:
@@ -2,6 +2,7 @@ import 'reflect-metadata';
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { evalLocal } from '../../../helpers/evalLocal';
|
||||
import { readFileAsString } from '../../../helpers/readFileAsString';
|
||||
|
||||
import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
|
||||
@@ -43,5 +44,82 @@ describe('ScopeAnalyzer', () => {
|
||||
assert.equal(error, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: Annex B function hoisting', () => {
|
||||
describe('Variant #1: basic block-scoped function hoisting', () => {
|
||||
const samplesCount: number = 50;
|
||||
|
||||
let testFunc: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/annex-b-function-hoisting.js');
|
||||
|
||||
testFunc = () => {
|
||||
for (let i = 0; i < samplesCount; i++) {
|
||||
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(code, {
|
||||
seed: i
|
||||
}).getObfuscatedCode();
|
||||
|
||||
const result = evalLocal(obfuscatedCode);
|
||||
|
||||
if (result.test1 !== 'foo') {
|
||||
throw new Error('test1 failed: expected foo, got ' + result.test1);
|
||||
}
|
||||
|
||||
if (result.test2 !== 'bar') {
|
||||
throw new Error('test2 failed: expected bar, got ' + result.test2);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should correctly handle Annex B function hoisting references', () => {
|
||||
assert.doesNotThrow(testFunc);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: strict mode should not apply Annex B hoisting', () => {
|
||||
let testFunc: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/annex-b-strict-mode.js');
|
||||
|
||||
testFunc = () => {
|
||||
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(code, {
|
||||
seed: 12345
|
||||
}).getObfuscatedCode();
|
||||
|
||||
eval(obfuscatedCode);
|
||||
};
|
||||
});
|
||||
|
||||
it('should correctly handle strict mode block-scoped functions', () => {
|
||||
assert.doesNotThrow(testFunc);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #3: let/const shadowing should prevent Annex B hoisting', () => {
|
||||
let testFunc: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/annex-b-let-const-shadowing.js');
|
||||
|
||||
testFunc = () => {
|
||||
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(code, {
|
||||
seed: 12345
|
||||
}).getObfuscatedCode();
|
||||
|
||||
const result = eval(obfuscatedCode);
|
||||
if (result !== 'outer') {
|
||||
throw new Error('Expected outer, got: ' + result);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should not hoist when let/const shadows the function name', () => {
|
||||
assert.doesNotThrow(testFunc);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Basic Annex B case: function in block referenced after block
|
||||
function test1() {
|
||||
if (true) {
|
||||
function foo() {
|
||||
return 'foo';
|
||||
}
|
||||
}
|
||||
return foo();
|
||||
}
|
||||
|
||||
// Function in switch case
|
||||
function test2(x) {
|
||||
switch (x) {
|
||||
case 1:
|
||||
function bar() {
|
||||
return 'bar';
|
||||
}
|
||||
break;
|
||||
}
|
||||
return bar();
|
||||
}
|
||||
|
||||
// Multiple blocks with same function name
|
||||
function test3() {
|
||||
if (true) {
|
||||
function baz() {
|
||||
return 'first';
|
||||
}
|
||||
}
|
||||
if (false) {
|
||||
function baz() {
|
||||
return 'second';
|
||||
}
|
||||
}
|
||||
return baz();
|
||||
}
|
||||
|
||||
// Return results for testing
|
||||
({ test1: test1(), test2: test2(1) });
|
||||
@@ -0,0 +1,9 @@
|
||||
function test() {
|
||||
let foo = 'outer';
|
||||
if (true) {
|
||||
function foo() { return 'inner'; }
|
||||
foo(); // block-scoped foo
|
||||
}
|
||||
return foo; // should be 'outer', not the function
|
||||
}
|
||||
test();
|
||||
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
function test() {
|
||||
let foo;
|
||||
if (true) {
|
||||
function foo() { return 'inner'; }
|
||||
foo(); // This refers to block-scoped foo
|
||||
}
|
||||
// foo here is the outer let, which is undefined
|
||||
return typeof foo;
|
||||
}
|
||||
test();
|
||||
+59
@@ -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,62 @@ describe('ObjectPatternPropertiesTransformer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #3: destructuring default parameter with var shadowing', () => {
|
||||
describe('Variant #1: destructuring default should reference outer var, not inner var', () => {
|
||||
let code: string;
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
code = readFileAsString(__dirname + '/fixtures/destructuring-default-outer-var.js');
|
||||
|
||||
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', () => {
|
||||
let code: string;
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
code = readFileAsString(__dirname + '/fixtures/nested-destructuring-default-outer-var.js');
|
||||
|
||||
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', () => {
|
||||
let code: string;
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
code = readFileAsString(__dirname + '/fixtures/simple-default-param-outer-var.js');
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
(function() {
|
||||
var x = 'outer';
|
||||
function f({ a = x } = {}) {
|
||||
var x = 'inner';
|
||||
return a;
|
||||
}
|
||||
return f();
|
||||
})();
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
(function() {
|
||||
var x = 'outer';
|
||||
function f({ a: { b = x } } = { a: {} }) {
|
||||
var x = 'inner';
|
||||
return b;
|
||||
}
|
||||
return f();
|
||||
})();
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
(function() {
|
||||
var x = 'outer';
|
||||
function f(a = x) {
|
||||
var x = 'inner';
|
||||
return a;
|
||||
}
|
||||
return f();
|
||||
})();
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Evaluates code using indirect eval.
|
||||
* Indirect eval runs in global scope without inheriting strict mode from the calling context.
|
||||
* This is needed for testing features like Annex B function hoisting.
|
||||
*
|
||||
* @param {string} code
|
||||
* @returns {any}
|
||||
*/
|
||||
export const evalLocal = (code: string): any => (0, eval)(code);
|
||||
Reference in New Issue
Block a user