import { assert } from 'chai'; import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes'; import { readFileAsString } from '../../../../helpers/readFileAsString'; import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade'; import { getRegExpMatch } from '../../../../helpers/getRegExpMatch'; describe('EvalCallExpressionTransformer', () => { describe('Variant #1: identifier reference', () => { const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/; const evalExpressionRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/; let functionIdentifierName: string | null, obfuscatedCode: string, variableReferenceIdentifierName: string | null; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/identifier-reference.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET } ).getObfuscatedCode(); functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp); variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp); }); it('should obfuscate eval string', () => { assert.match(obfuscatedCode, evalExpressionRegExp); }); it('should correctly transform function parameter inside eval expression', () => { assert.equal(functionIdentifierName, variableReferenceIdentifierName); }); }); describe('Variant #2: call expression with identifier reference', () => { const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/; const evalExpressionRegExp: RegExp = /eval *\('console\[\\'log\\']\((_0x(?:[a-f0-9]){4,6})\);'\);/; let functionIdentifierName: string | null, obfuscatedCode: string, variableReferenceIdentifierName: string | null; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/call-expression-identifier-reference.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET } ).getObfuscatedCode(); functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp); variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp); }); it('should obfuscate eval string', () => { assert.match(obfuscatedCode, evalExpressionRegExp); }); it('should correctly transform function parameter inside eval expression', () => { assert.equal(functionIdentifierName, variableReferenceIdentifierName); }); }); describe('Variant #3: multiple statements in eval', () => { const regExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}; *_0x([a-f0-9]){4,6};'\);/; let obfuscatedCode: string; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/multiple-statements-eval.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET } ).getObfuscatedCode(); }); it('should obfuscate eval string', () => { assert.match(obfuscatedCode, regExp); }); }); describe('Variant #4: string array calls wrapper call', () => { const stringArrayRegExp: RegExp = /^function _0x([a-f0-9]){4}\(\){return\['log','bar'\];}/; const stringArrayCallsWrapperRegExp: RegExp = /eval *\('console\[_0x([a-f0-9]){4,6}\(\\'0x0\\'\)]\(_0x([a-f0-9]){4,6}\(\\'0x1\\'\)\);'\);/; let obfuscatedCode: string; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/string-array-calls-wrapper-call.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET, stringArray: true, stringArrayThreshold: 1 } ).getObfuscatedCode(); }); it('match #1: should add strings from eval expression to the string array', () => { assert.match(obfuscatedCode, stringArrayRegExp); }); it('match #1: should replace string with call to the string array calls wrapper', () => { assert.match(obfuscatedCode, stringArrayCallsWrapperRegExp); }); }); describe('Variant #5: eval expression as argument', () => { const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/; const evalExpressionRegExp: RegExp = /console\['log']\(eval *\('(_0x(?:[a-f0-9]){4,6});'\)\);/; let functionIdentifierName: string | null, obfuscatedCode: string, variableReferenceIdentifierName: string | null; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/eval-expression-as-argument.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET } ).getObfuscatedCode(); functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp); variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp); }); it('should obfuscate eval string', () => { assert.match(obfuscatedCode, evalExpressionRegExp); }); it('should correctly transform function parameter inside eval expression', () => { assert.equal(functionIdentifierName, variableReferenceIdentifierName); }); }); describe('Variant #6: nested eval expressions', () => { const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6}), *(_0x(?:[a-f0-9]){4,6})\)/; const evalExpressionMatch: string = `` + `eval *\\('` + `var *(_0x(?:[a-f0-9]){4,6}) *= *(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` + `eval\\(\\\\'` + `(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` + `\\\\'\\);` + `'\\);` + ``; const evalExpressionRegExp: RegExp = new RegExp(evalExpressionMatch); const expectedEvaluationResult: number = 4; let evaluationResult: number, functionIdentifierAName: string | null, functionIdentifierBName: string | null, obfuscatedCode: string, variableReferenceIdentifierAName1: string | null, variableReferenceIdentifierAName2: string | null, variableReferenceIdentifierBName: string | null, variableReferenceIdentifierCName1: string | null, variableReferenceIdentifierCName2: string | null; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/nested-eval-expressions.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET } ).getObfuscatedCode(); functionIdentifierAName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 0); functionIdentifierBName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 1); // parameter `a` reference inside parent eval expression variableReferenceIdentifierAName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 1); // parameter `a` reference inside nested eval expression variableReferenceIdentifierAName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 3); // parameter `b` reference inside parent eval expression variableReferenceIdentifierBName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 2); // variable declaration `c` inside parent eval expression variableReferenceIdentifierCName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 0); // variable `c` reference inside nested eval expression variableReferenceIdentifierCName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 4); evaluationResult = eval(obfuscatedCode); }); it('should obfuscate eval string', () => { assert.match(obfuscatedCode, evalExpressionRegExp); }); it('should generate correct code', () => { assert.equal(evaluationResult, expectedEvaluationResult); }); it('match #1: should correctly transform function parameter `a` inside eval expression', () => { assert.equal(functionIdentifierAName, variableReferenceIdentifierAName1); }); it('match #2: should correctly transform function parameter `a` inside nested eval expression', () => { assert.equal(functionIdentifierAName, variableReferenceIdentifierAName2); }); it('match #3: should correctly transform function parameter `b` inside eval expression', () => { assert.equal(functionIdentifierBName, variableReferenceIdentifierBName); }); it('match #4: should correctly transform variable declaration and variable reference inside eval and nested eval expressions', () => { assert.equal(variableReferenceIdentifierCName1, variableReferenceIdentifierCName2); }); }); describe('Variant #7: wrong eval string', () => { const evalExpressionRegExp: RegExp = /eval *\('~'\);/; let obfuscatedCode: string before(() => { const code: string = readFileAsString(__dirname + '/fixtures/wrong-eval-string.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET } ).getObfuscatedCode(); }); it('should skip obfuscation of eval string', () => { assert.match(obfuscatedCode, evalExpressionRegExp); }); }); describe('Variant #8: template literal inside eval expression', () => { const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/; const evalExpressionRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/; let functionIdentifierName: string | null, obfuscatedCode: string, variableReferenceIdentifierName: string | null; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/eval-expression-template-literal.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET } ).getObfuscatedCode(); functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp); variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp); }); it('should obfuscate eval string', () => { assert.match(obfuscatedCode, evalExpressionRegExp); }); it('should correctly transform function parameter inside eval expression', () => { assert.equal(functionIdentifierName, variableReferenceIdentifierName); }); }); describe('Variant #9: integration with control flow flattening', () => { const variableMatch: string = '_0x([a-f0-9]){4,6}'; const controlFlowStorageNodeMatch: string = `` + `var *${variableMatch} *= *\\{` + `'\\w{5}' *: *function *\\(${variableMatch}, *${variableMatch}\\) *\\{` + `return *${variableMatch} *\\+ *${variableMatch};` + `\\}` + `\\};` + ``; const controlFlowStorageNodeRegExp: RegExp = new RegExp(controlFlowStorageNodeMatch); const evalExpressionRegExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}\[\\'\w{5}\\']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);'\);/; let obfuscatedCode: string; before(() => { const code: string = readFileAsString(__dirname + '/fixtures/control-flow-flattening-integration.js'); obfuscatedCode = JavaScriptObfuscator.obfuscate( code, { ...NO_ADDITIONAL_NODES_PRESET, controlFlowFlattening: true, controlFlowFlatteningThreshold: 1 } ).getObfuscatedCode(); }); it('should add control flow storage node', () => { assert.match(obfuscatedCode, controlFlowStorageNodeRegExp); }); it('should obfuscate eval string', () => { assert.match(obfuscatedCode, evalExpressionRegExp); }); }); });