Ver código fonte

Merge pull request #921 from javascript-obfuscator/control-flow-flattening-middle-rest-argument

Fixed invalid code generation for start/middle rest arguments
Timofey Kachalov 4 anos atrás
pai
commit
33df42be0a

+ 1 - 0
CHANGELOG.md

@@ -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

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/index.browser.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/index.cli.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
dist/index.js


+ 12 - 10
src/custom-nodes/control-flow-flattening-nodes/CallExpressionFunctionNode.ts

@@ -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
test/dev/dev.ts

@@ -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();
 

+ 67 - 3
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts

@@ -118,7 +118,71 @@ describe('CallExpressionControlFlowReplacer', function () {
             });
         });
 
-        describe('Variant #4 - rest call argument', () => {
+        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}\\) *\\{` +
+                    `return *_0x([a-f0-9]){4,6}\\(\.\.\._0x([a-f0-9]){4,6}\\);` +
+                `\\}` +
+            ``);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/rest-as-start-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 #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}\\) *\\{` +
@@ -129,7 +193,7 @@ describe('CallExpressionControlFlowReplacer', function () {
             let obfuscatedCode: string;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/rest-call-argument.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/rest-as-last-call-argument.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -145,7 +209,7 @@ 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 inside control flow storage node function', () => {
                 assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
             });
         });

+ 0 - 0
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-call-argument.js → test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-last-call-argument.js


+ 7 - 0
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-middle-call-argument.js

@@ -0,0 +1,7 @@
+(function () {
+    const log = console.log;
+    const first = 'foo';
+    const rest = ['bar', 'baz', 'bark'];
+    const last = 'hawk';
+    log(first, ...rest, last);
+})();

+ 6 - 0
test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/fixtures/rest-as-start-call-argument.js

@@ -0,0 +1,6 @@
+(function () {
+    const log = console.log;
+    const rest = ['foo', 'bar', 'baz'];
+    const last = 'bark';
+    log(...rest, last);
+})();

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff