瀏覽代碼

Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/195

sanex3339 7 年之前
父節點
當前提交
5f2273cc3d

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 ===
+v0.14.3
+---
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/195
+
 v0.14.2
 ---
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/181

File diff suppressed because it is too large
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.14.2",
+  "version": "0.14.3",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

+ 64 - 13
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -6,6 +6,7 @@ import * as ESTree from 'estree';
 
 import { TDeadNodeInjectionCustomNodeFactory } from '../../types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory';
 import { TNodeWithBlockScope } from '../../types/node/TNodeWithBlockScope';
+import { TNodeWithScope } from '../../types/node/TNodeWithScope';
 
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../interfaces/options/IOptions';
@@ -97,15 +98,62 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @param {Node} blockStatementNode
+     * @param {Node} targetNode
      * @returns {boolean}
      */
-    private static isValidCollectedBlockStatementNode (blockStatementNode: ESTree.Node): boolean {
-        const isProhibitedNode: (node: ESTree.Node) => boolean =
-            (node: ESTree.Node): boolean => NodeGuards.isBreakStatementNode(node) ||
-                NodeGuards.isContinueStatementNode(node) ||
-                NodeGuards.isAwaitExpressionNode(node) ||
-                NodeGuards.isSuperNode(node);
+    private static isProhibitedNodeInsideCollectedBlockStatement (targetNode: ESTree.Node): boolean {
+        return NodeGuards.isBreakStatementNode(targetNode)
+            || NodeGuards.isContinueStatementNode(targetNode)
+            || NodeGuards.isAwaitExpressionNode(targetNode)
+            || NodeGuards.isSuperNode(targetNode);
+    }
+
+    /**
+     * @param {Node} targetNode
+     * @returns {boolean}
+     */
+    private static isScopeHoistingFunctionDeclaration (targetNode: ESTree.Node): boolean {
+        if (!NodeGuards.isFunctionDeclarationNode(targetNode)) {
+            return false;
+        }
+
+        const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(targetNode);
+        const scopeBody: ESTree.Statement[] = !NodeGuards.isSwitchCaseNode(scopeNode)
+            ? <ESTree.Statement[]>scopeNode.body
+            : scopeNode.consequent;
+        const indexInScope: number = scopeBody.indexOf(targetNode);
+
+        if (indexInScope === 0) {
+            return false;
+        }
+
+        const slicedBody: ESTree.Statement[] = scopeBody.slice(0, indexInScope);
+        const hostBlockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode(slicedBody);
+        const functionDeclarationName: string = targetNode.id.name;
+
+        let isScopeHoistedFunctionDeclaration: boolean = false;
+
+        estraverse.traverse(hostBlockStatementNode, {
+            enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+                if (NodeGuards.isIdentifierNode(node) && node.name === functionDeclarationName) {
+                    isScopeHoistedFunctionDeclaration = true;
+
+                    return estraverse.VisitorOption.Break;
+                }
+            }
+        });
+
+        return isScopeHoistedFunctionDeclaration;
+    }
+
+    /**
+     * @param {BlockStatement} blockStatementNode
+     * @returns {boolean}
+     */
+    private static isValidCollectedBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
+        if (!blockStatementNode.body.length) {
+            return false;
+        }
 
         let nestedBlockStatementsCount: number = 0;
         let isValidBlockStatementNode: boolean = true;
@@ -117,8 +165,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
                 }
 
                 if (
-                    nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
-                    isProhibitedNode(node)
+                    nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount
+                    || DeadCodeInjectionTransformer.isProhibitedNodeInsideCollectedBlockStatement(node)
+                    || DeadCodeInjectionTransformer.isScopeHoistingFunctionDeclaration(node)
                 ) {
                     isValidBlockStatementNode = false;
 
@@ -209,7 +258,10 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      * @param {NodeGuards} parentNode
      * @returns {NodeGuards | VisitorOption}
      */
-    public transformNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node | estraverse.VisitorOption {
+    public transformNode (
+        blockStatementNode: ESTree.BlockStatement,
+        parentNode: ESTree.Node
+    ): ESTree.Node | estraverse.VisitorOption {
         const canBreakTraverse: boolean = !this.collectedBlockStatements.length
             || this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount;
 
@@ -235,10 +287,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
         const maxInteger: number = this.collectedBlockStatements.length - 1;
         const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
         const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
-        const isInvalidRandomBlockStatementNode: boolean = randomBlockStatementNode === blockStatementNode
-            || !randomBlockStatementNode.body.length;
+        const isDuplicateBlockStatementNodes: boolean = randomBlockStatementNode === blockStatementNode;
 
-        if (isInvalidRandomBlockStatementNode) {
+        if (isDuplicateBlockStatementNodes) {
             return blockStatementNode;
         }
 

+ 36 - 0
test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts

@@ -517,5 +517,41 @@ describe('DeadCodeInjectionTransformer', () => {
                 assert.isAtLeast(matchesLength, expectedMatchesLength);
             });
         });
+
+        describe('variant #12 - block statement with scope-hoisting', () => {
+            const regExp: RegExp = new RegExp(
+                `${variableMatch} *\\(\\); *` +
+                `var *${variableMatch} *= *0x2; *` +
+                `function *${variableMatch} *\\(\\) *{ *} *`,
+                'g'
+            );
+            const expectedMatchesLength: number = 5;
+
+            let matchesLength: number = 0;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/block-statement-with-scope-hoisting.js');
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1,
+                        deadCodeInjection: true,
+                        deadCodeInjectionThreshold: 1
+                    }
+                );
+
+                const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
+                const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(regExp);
+
+                if (functionMatches) {
+                    matchesLength = functionMatches.length;
+                }            });
+
+            it('shouldn\'t collect block statements with scope-hoisting', () => {
+                assert.equal(matchesLength, expectedMatchesLength);
+            });
+        });
     });
 });

+ 15 - 0
test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/block-statement-wtith-scope-hoisting.js → test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/block-statement-with-scope-hoisting.js

@@ -1,22 +1,37 @@
 (function(){
     function foo () {
+        var a = 1;
         inner1();
+        var b = 2;
         function inner1 () {}
+        var c = 3;
     }
     function bar () {
+        var a = 1;
         inner2();
+        var b = 2;
         function inner2 () {}
+        var c = 3;
     }
     function baz () {
+        var a = 1;
         inner3();
+        var b = 2;
         function inner3 () {}
+        var c = 3;
     }
     function bark () {
+        var a = 1;
         inner4();
+        var b = 2;
         function inner4 () {}
+        var c = 3;
     }
     function hawk () {
+        var a = 1;
         inner5();
+        var b = 2;
         function inner5 () {}
+        var c = 3;
     }
 })();

Some files were not shown because too many files changed in this diff