Fixed invalid code generation for start/middle rest arguments when controlFlowFlattenig option is enabled

Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/920
This commit is contained in:
sanex
2021-05-23 11:06:04 +03:00
parent bcd7adb25f
commit a13c764fc6
10 changed files with 111 additions and 33 deletions
+1
View File
@@ -2,6 +2,7 @@ Change Log
v2.13.0
---
* Fixed invalid code generation for start/middle rest arguments when `controlFlowFlattenig` option is enabled. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/920
* **Internal**: Added support of `node@16` and dropped support of `node@10`. This should not affect obfuscated code
v2.12.0
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
@@ -68,16 +68,18 @@ export class CallExpressionFunctionNode extends AbstractCustomNode {
const baseIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(`param${i + 1}`);
params.push(
isSpreadCallArgument
? NodeFactory.restElementNode(baseIdentifierNode)
: baseIdentifierNode
);
callArguments.push(
isSpreadCallArgument
? NodeFactory.spreadElementNode(baseIdentifierNode)
: baseIdentifierNode
);
if (isSpreadCallArgument) {
params.push(NodeFactory.restElementNode(baseIdentifierNode));
callArguments.push(NodeFactory.spreadElementNode(baseIdentifierNode));
const isMiddleSpreadCallArgument: boolean = i < argumentsLength - 1;
if (isMiddleSpreadCallArgument) {
break;
}
} else {
params.push(baseIdentifierNode);
callArguments.push(baseIdentifierNode);
}
}
const structure: TStatement = NodeFactory.expressionStatementNode(
+12 -14
View File
@@ -7,24 +7,22 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
var variable1 = '5' - 3;
var variable2 = '5' + 3;
var variable3 = '5' + - '2';
var variable4 = ['10','10','10','10','10'].map(parseInt);
var variable5 = 'foo ' + 1 + 1;
console.log(variable1);
console.log(variable2);
console.log(variable3);
console.log(variable4);
console.log(variable5);
function foo(a, b, c, d) {
console.log(a, b, c, d)
}
function bar(...args) {
foo(...args, 5)
}
bar(...[1, 2, 3], 4)
`,
{
...NO_ADDITIONAL_NODES_PRESET,
compact: false,
stringArray: true,
stringArrayThreshold: 1,
rotateStringArray: true,
stringArrayWrappersCount: 3
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1,
identifierNamesGenerator: 'mangled'
}
).getObfuscatedCode();
@@ -118,18 +118,18 @@ describe('CallExpressionControlFlowReplacer', function () {
});
});
describe('Variant #4 - rest call argument', () => {
const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\);/;
describe('Variant #4 - rest as start call argument', () => {
const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);/;
const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
`'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
`return *_0x([a-f0-9]){4,6}\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\);` +
`'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
`return *_0x([a-f0-9]){4,6}\\(\.\.\._0x([a-f0-9]){4,6}\\);` +
`\\}` +
``);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/rest-call-argument.js');
const code: string = readFileAsString(__dirname + '/fixtures/rest-as-start-call-argument.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
@@ -145,7 +145,71 @@ describe('CallExpressionControlFlowReplacer', function () {
assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
});
it('should keep spread parameter and rest call argument inside control flow storage node function', () => {
it('should keep rest parameter and rest call argument, but remove all function parameters after rest parameter', () => {
assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
});
});
describe('Variant #5 - rest as middle call argument', () => {
const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);/;
const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
`'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
`return *_0x([a-f0-9]){4,6}\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\);` +
`\\}` +
``);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/rest-as-middle-call-argument.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
});
it('should replace call expression node with call to control flow storage node', () => {
assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
});
it('should keep rest parameter and rest call argument, but remove all function parameters after rest parameter', () => {
assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
});
});
describe('Variant #6 - rest as last call argument', () => {
const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\);/;
const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
`'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
`return *_0x([a-f0-9]){4,6}\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\);` +
`\\}` +
``);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/rest-as-last-call-argument.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
});
it('should replace call expression node with call to control flow storage node', () => {
assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
});
it('should keep rest parameter and rest call argument inside control flow storage node function', () => {
assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
});
});
@@ -0,0 +1,7 @@
(function () {
const log = console.log;
const first = 'foo';
const rest = ['bar', 'baz', 'bark'];
const last = 'hawk';
log(first, ...rest, last);
})();
@@ -0,0 +1,6 @@
(function () {
const log = console.log;
const rest = ['foo', 'bar', 'baz'];
const last = 'bark';
log(...rest, last);
})();