Browse Source

Merge pull request #120 from javascript-obfuscator/0.12.2

0.12.2 release
Timofey Kachalov 7 years ago
parent
commit
bea270e093

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 ===
+v0.12.2
+---
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/119
+
 v0.12.1
 ---
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/117

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


+ 5 - 5
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.12.1",
+  "version": "0.12.2",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",
@@ -23,11 +23,11 @@
     "chance": "1.0.12",
     "class-validator": "0.7.3",
     "commander": "2.11.0",
-    "escodegen-wallaby": "1.6.12",
+    "escodegen-wallaby": "1.6.14",
     "esmangle": "1.0.1",
     "esprima": "4.0.0",
     "estraverse": "4.2.0",
-    "inversify": "4.5.0",
+    "inversify": "4.5.1",
     "md5": "2.2.1",
     "mkdirp": "0.5.1",
     "opencollective": "1.0.3",
@@ -47,7 +47,7 @@
     "@types/md5": "2.1.32",
     "@types/mkdirp": "0.5.1",
     "@types/mocha": "2.2.44",
-    "@types/node": "8.0.47",
+    "@types/node": "8.0.50",
     "@types/sinon": "2.3.7",
     "@types/string-template": "1.0.2",
     "@types/webpack-env": "1.13.2",
@@ -61,7 +61,7 @@
     "istanbul": "1.1.0-alpha.1",
     "mocha": "4.0.1",
     "pre-commit": "1.2.2",
-    "sinon": "4.1.1",
+    "sinon": "4.1.2",
     "ts-node": "3.3.0",
     "tslint": "5.8.0",
     "tslint-eslint-rules": "4.1.1",

+ 2 - 2
src/cli/utils/CLIUtils.ts

@@ -82,9 +82,9 @@ export class CLIUtils {
 
     /**
      * @param {string} outputPath
-     * @param {any} data
+     * @param {string} data
      */
-    public static writeFile (outputPath: string, data: any): void {
+    public static writeFile (outputPath: string, data: string): void {
         mkdirp.sync(path.dirname(outputPath));
 
         fs.writeFileSync(outputPath, data, {

+ 34 - 16
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -8,8 +8,10 @@ import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatemen
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { ITransformersRunner } from '../../interfaces/node-transformers/ITransformersRunner';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
+import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
 import { NodeType } from '../../enums/node/NodeType';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
@@ -29,6 +31,18 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      */
     private static readonly minCollectedBlockStatementsCount: number = 5;
 
+    /**
+     * @type {NodeTransformer[]}
+     */
+    private static readonly transformersToRenameBlockScopeIdentifiers: NodeTransformer[] = [
+        NodeTransformer.CatchClauseTransformer,
+        NodeTransformer.ClassDeclarationTransformer,
+        NodeTransformer.FunctionDeclarationTransformer,
+        NodeTransformer.FunctionTransformer,
+        NodeTransformer.LabeledStatementTransformer,
+        NodeTransformer.VariableDeclarationTransformer
+    ];
+
     /**
      * @type {ESTree.BlockStatement[]}
      */
@@ -40,14 +54,23 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     private collectedBlockStatementsLength: number;
 
     /**
+     * @type {ITransformersRunner}
+     */
+    private readonly transformersRunner: ITransformersRunner;
+
+    /**
+     * @param {ITransformersRunner} transformersRunner
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
+        @inject(ServiceIdentifiers.ITransformersRunner) transformersRunner: ITransformersRunner,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(randomGenerator, options);
+
+        this.transformersRunner = transformersRunner;
     }
 
     /**
@@ -131,15 +154,14 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
         blockStatementNode: ESTree.BlockStatement,
         collectedBlockStatements: ESTree.BlockStatement[]
     ): void {
-        const clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode);
-
-        let nestedBlockStatementsCount: number = 0,
+        let clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode),
+            nestedBlockStatementsCount: number = 0,
             isValidBlockStatementNode: boolean = true;
 
         estraverse.replace(clonedBlockStatementNode, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node | null): any => {
                 /**
-                 * First step: count nested block statements in current block statement
+                 * Count nested block statements in current block statement
                  */
                 if (NodeGuards.isBlockStatementNode(node)) {
                     nestedBlockStatementsCount++;
@@ -159,18 +181,6 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
                     return estraverse.VisitorOption.Break;
                 }
 
-                /**
-                 * Second step: rename all identifiers (except identifiers in member expressions)
-                 * in current block statement
-                 */
-                if (
-                    NodeGuards.isIdentifierNode(node) &&
-                    parentNode &&
-                    !NodeGuards.isMemberExpressionNode(parentNode)
-                ) {
-                    node.name = this.randomGenerator.getRandomVariableName(6);
-                }
-
                 return node;
             }
         });
@@ -179,6 +189,14 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
             return;
         }
 
+        /**
+         * We should transform identifiers in the dead code block statement to avoid conflicts with original code
+         */
+        clonedBlockStatementNode = this.transformersRunner.transform(
+            clonedBlockStatementNode,
+            DeadCodeInjectionTransformer.transformersToRenameBlockScopeIdentifiers
+        );
+
         collectedBlockStatements.push(clonedBlockStatementNode);
     }
 

+ 28 - 1
src/node-transformers/obfuscating-transformers/FunctionTransformer.ts

@@ -109,14 +109,41 @@ export class FunctionTransformer extends AbstractNodeTransformer {
             });
     }
 
+    /**
+     * @param {Property[]} properties
+     * @param {Set<string>} ignoredIdentifierNamesSet
+     */
+    private addIdentifiersToIgnoredIdentifierNamesSet (
+        properties: ESTree.Property[],
+        ignoredIdentifierNamesSet: Set<string>
+    ): void {
+        properties.forEach((property: ESTree.Property) => {
+            if (!NodeGuards.isIdentifierNode(property.key)) {
+                return;
+            }
+
+            ignoredIdentifierNamesSet.add(property.key.name);
+        });
+    }
+
     /**
      * @param {Function} functionNode
      * @param {number} nodeIdentifier
      */
     private replaceFunctionParams (functionNode: ESTree.Function, nodeIdentifier: number): void {
+        const ignoredIdentifierNamesSet: Set<string> = new Set();
+
         const replaceVisitor: estraverse.Visitor = {
             enter: (node: ESTree.Node, parentNode: ESTree.Node | null): any => {
-                if (parentNode && NodeGuards.isReplaceableIdentifierNode(node, parentNode)) {
+                if (NodeGuards.isObjectPatternNode(node)) {
+                    this.addIdentifiersToIgnoredIdentifierNamesSet(node.properties, ignoredIdentifierNamesSet);
+                }
+
+                if (
+                    parentNode &&
+                    NodeGuards.isReplaceableIdentifierNode(node, parentNode) &&
+                    !ignoredIdentifierNamesSet.has(node.name)
+                ) {
                     const newIdentifier: ESTree.Identifier = this.identifierObfuscatingReplacer.replace(node.name, nodeIdentifier);
                     const newIdentifierName: string = newIdentifier.name;
 

+ 3 - 1
src/node/NodeGuards.ts

@@ -221,7 +221,9 @@ export class NodeGuards {
             parentNode.property === node
         );
 
-        return !parentNodeIsPropertyNode && !parentNodeIsMemberExpressionNode && !NodeGuards.isLabelIdentifierNode(node, parentNode);
+        return !parentNodeIsPropertyNode &&
+            !parentNodeIsMemberExpressionNode &&
+            !NodeGuards.isLabelIdentifierNode(node, parentNode);
     }
 
     /**

+ 29 - 4
test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts

@@ -190,26 +190,26 @@ describe('DeadCodeInjectionTransformer', () => {
                 `if *\\(${variableMatch}\\('${hexMatch}'\\) *=== *${variableMatch}\\('${hexMatch}'\\)\\) *\\{` +
                     `console.*` +
                 `\\} *else *\\{` +
-                    `${variableMatch}.*` +
+                    `alert.*` +
                 `\\}` +
             ``;
             const match2: string = `` +
                 `if *\\(${variableMatch}\\('${hexMatch}'\\) *!== *${variableMatch}\\('${hexMatch}'\\)\\) *\\{` +
                     `console.*` +
                 `\\} *else *\\{` +
-                    `${variableMatch}.*` +
+                    `alert.*` +
                 `\\}` +
             ``;
             const match3: string = `` +
                 `if *\\(${variableMatch}\\('${hexMatch}'\\) *=== *${variableMatch}\\('${hexMatch}'\\)\\) *\\{` +
-                    `${variableMatch}.*` +
+                    `alert.*` +
                 `\\} *else *\\{` +
                     `console.*` +
                 `\\}` +
             ``;
             const match4: string = `` +
                 `if *\\(${variableMatch}\\('${hexMatch}'\\) *!== *${variableMatch}\\('${hexMatch}'\\)\\) *\\{` +
-                    `${variableMatch}.*` +
+                    `alert.*` +
                 `\\} *else *\\{` +
                     `console.*` +
                 `\\}` +
@@ -309,5 +309,30 @@ describe('DeadCodeInjectionTransformer', () => {
                 assert.match(obfuscatedCode, regExp);
             });
         });
+
+        describe('variant #7 - correct obfuscation of dead-code block statements', () => {
+            const variableName: string = 'importantVariableName';
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/obfuscation-of-dead-code-block-statements.js');
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_CUSTOM_NODES_PRESET,
+                        deadCodeInjection: true,
+                        deadCodeInjectionThreshold: 1,
+                        debugProtection: true
+                    }
+                );
+
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
+            });
+
+            it('should correctly obfuscate dead-code block statements and prevent any exposing of internal variable names', () => {
+                assert.notInclude(obfuscatedCode, variableName);
+            });
+        });
     });
 });

+ 5 - 0
test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/obfuscation-of-dead-code-block-statements.js

@@ -0,0 +1,5 @@
+function test() {
+    var importantVariableName = 'test';
+
+    return importantVariableName.indexOf('test');
+}

+ 46 - 17
test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/FunctionTransformer.spec.ts

@@ -49,29 +49,58 @@ describe('FunctionTransformer', () => {
     });
 
     describe('object pattern as parameter', () => {
-        const functionParameterRegExp: RegExp = /function *\(\{ *bar *\}\) *\{/;
-        const functionBodyRegExp: RegExp = /return *bar;/;
+        describe('variant #1: simple', () => {
+            const functionParameterRegExp: RegExp = /function *\(\{ *bar *\}\) *\{/;
+            const functionBodyRegExp: RegExp = /return *bar;/;
 
-        let obfuscatedCode: string;
+            let obfuscatedCode: string;
 
-        before(() => {
-            const code: string = readFileAsString(__dirname + '/fixtures/object-pattern-as-parameter.js');
-            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
-                code,
-                {
-                    ...NO_CUSTOM_NODES_PRESET
-                }
-            );
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/object-pattern-as-parameter-1.js');
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_CUSTOM_NODES_PRESET
+                    }
+                );
 
-            obfuscatedCode = obfuscationResult.getObfuscatedCode();
-        });
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
+            });
 
-        it('match #1: shouldn\'t transform function parameter object pattern identifier', () => {
-            assert.match(obfuscatedCode, functionParameterRegExp);
+            it('match #1: shouldn\'t transform function parameter object pattern identifier', () => {
+                assert.match(obfuscatedCode, functionParameterRegExp);
+            });
+
+            it('match #2: shouldn\'t transform function parameter object pattern identifier', () => {
+                assert.match(obfuscatedCode, functionBodyRegExp);
+            });
         });
 
-        it('match #2: shouldn\'t transform function parameter object pattern identifier', () => {
-            assert.match(obfuscatedCode, functionBodyRegExp);
+        describe('variant #2: correct transformation when identifier with same name in parent scope exist', () => {
+            const callbackParameterRegExp: RegExp = /\['then'] *\(\({ *data *}\)/;
+            const callbackBodyRegExp: RegExp = /console\['log']\(data\)/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/object-pattern-as-parameter-2.js');
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_CUSTOM_NODES_PRESET
+                    }
+                );
+
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
+            });
+
+            it('match #1: shouldn\'t transform callback parameter object pattern identifier', () => {
+                assert.match(obfuscatedCode, callbackParameterRegExp);
+            });
+
+            it('match #2: shouldn\'t transform callback parameter object pattern identifier', () => {
+                assert.match(obfuscatedCode, callbackBodyRegExp);
+            });
         });
     });
 

+ 0 - 0
test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/fixtures/object-pattern-as-parameter.js → test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/fixtures/object-pattern-as-parameter-1.js


+ 8 - 0
test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/fixtures/object-pattern-as-parameter-2.js

@@ -0,0 +1,8 @@
+(function(){
+    function foo (data) {
+        new Promise((resolve) => resolve({data: data}))
+            .then(({data}) => console.log(data));
+    }
+
+    foo(1);
+})();

+ 20 - 16
yarn.lock

@@ -52,10 +52,14 @@
   version "2.2.44"
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e"
 
-"@types/node@*", "@types/[email protected]":
+"@types/node@*":
   version "8.0.47"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.47.tgz#968e596f91acd59069054558a00708c445ca30c2"
 
+"@types/[email protected]":
+  version "8.0.50"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.50.tgz#dc545448e128c88c4eec7cd64025fcc3b7604541"
+
 "@types/[email protected]":
   version "2.3.7"
   resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-2.3.7.tgz#e92c2fed3297eae078d78d1da032b26788b4af86"
@@ -1413,9 +1417,9 @@ [email protected], escape-string-regexp@^1.0.2, escape-string-regexp@^1
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
 
[email protected]2:
-  version "1.6.12"
-  resolved "https://registry.yarnpkg.com/escodegen-wallaby/-/escodegen-wallaby-1.6.12.tgz#dac2ed380daca6c7a2c9e8496d50c091239fe26f"
[email protected]4:
+  version "1.6.14"
+  resolved "https://registry.yarnpkg.com/escodegen-wallaby/-/escodegen-wallaby-1.6.14.tgz#41a0a7a2a2bd776207d25b4d2f373f12853de23f"
   dependencies:
     esprima "^2.7.1"
     estraverse "^1.9.1"
@@ -2037,9 +2041,9 @@ invariant@^2.2.2:
   dependencies:
     loose-envify "^1.0.0"
 
[email protected].0:
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/inversify/-/inversify-4.5.0.tgz#1a575ddf1db216ed3292d9b0f70f497de275874e"
[email protected].1:
+  version "4.5.1"
+  resolved "https://registry.yarnpkg.com/inversify/-/inversify-4.5.1.tgz#2f8a249e1fc5346e4f28b4b86dfae5af1b673178"
 
 invert-kv@^1.0.0:
   version "1.0.0"
@@ -2300,14 +2304,14 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
 
[email protected]:
[email protected], [email protected]:
   version "3.6.1"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30"
   dependencies:
     argparse "^1.0.7"
     esprima "^2.6.0"
 
-[email protected], js-yaml@^3.7.0:
+js-yaml@^3.7.0:
   version "3.10.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"
   dependencies:
@@ -2475,9 +2479,9 @@ lolex@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6"
 
-lolex@^2.1.3:
-  version "2.1.3"
-  resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.1.3.tgz#53f893bbe88c80378156240e127126b905c83087"
+lolex@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.0.tgz#d6bad0f0aa5caebffcfebb09fb2caa89baaff51c"
 
 longest@^1.0.1:
   version "1.0.1"
@@ -3431,14 +3435,14 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
 
[email protected].1:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.1.tgz#bd657be815df608887fe1f0b75f9590ec563748a"
[email protected].2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.2.tgz#65610521d926fb53742dd84cd599f0b89a82f440"
   dependencies:
     diff "^3.1.0"
     formatio "1.2.0"
     lodash.get "^4.4.2"
-    lolex "^2.1.3"
+    lolex "^2.2.0"
     nise "^1.2.0"
     supports-color "^4.4.0"
     type-detect "^4.0.0"

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