Ver Fonte

Merge branch 'master' into minification-transformers

# Conflicts:
#	CHANGELOG.md
#	dist/index.browser.js
#	dist/index.cli.js
#	dist/index.js
#	src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts
#	src/utils/CryptUtils.ts
#	test/dev/dev.ts
sanex3339 há 5 anos atrás
pai
commit
2e198d1190
19 ficheiros alterados com 311 adições e 165 exclusões
  1. 7 1
      CHANGELOG.md
  2. 0 0
      dist/index.browser.js
  3. 0 0
      dist/index.cli.js
  4. 0 0
      dist/index.js
  5. 18 1
      src/cli/utils/CLIUtils.ts
  6. 18 8
      src/custom-code-helpers/string-array/StringArrayCallsWrapperCodeHelper.ts
  7. 2 1
      src/custom-code-helpers/string-array/group/StringArrayCodeHelperGroup.ts
  8. 15 20
      src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts
  9. 1 1
      src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/Rc4Template.ts
  10. 1 1
      src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/StringArrayBase64DecodeTemplate.ts
  11. 2 1
      src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts
  12. 2 1
      src/utils/CryptUtils.ts
  13. 8 4
      test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperNodeTemplate.spec.ts
  14. 175 122
      test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts
  15. 25 0
      test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/function-declaration-inside-block-statement.js
  16. 2 1
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec.ts
  17. 14 0
      test/helpers/swapLettersCase.ts
  18. 18 2
      test/unit-tests/cli/utils/CLIUtils.spec.ts
  19. 3 1
      test/unit-tests/utils/CryptUtils.spec.ts

+ 7 - 1
CHANGELOG.md

@@ -1,9 +1,15 @@
 Change Log
 
-v1.3.0
+v1.4.0
 ---
 * **New option:** `minify` enables code minification.
 
+v1.3.0
+---
+* Improvements of `stringArrayEncoding`: `base64` and `rc4`
+* **CLI**: added config file extension validation (it still supports `.js` and `.json` extensions)
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/499
+
 v1.2.2
 ---
 * Fixed performance regression of `Initializing` stage after `1.2.0`

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/index.browser.js


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/index.cli.js


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/index.js


+ 18 - 1
src/cli/utils/CLIUtils.ts

@@ -1,6 +1,16 @@
+import * as path from 'path';
+
 import { TDictionary } from '../../types/TDictionary';
 
 export class CLIUtils {
+    /**
+     * @type {string[]}
+     */
+    public static readonly allowedConfigFileExtensions: string[] = [
+        '.js',
+        '.json'
+    ];
+
     /**
      * @param {string} configPath
      * @returns {TDictionary}
@@ -8,13 +18,20 @@ export class CLIUtils {
     public static getUserConfig (configPath: string): TDictionary {
         let config: TDictionary;
 
+        const configFileExtension: string = path.extname(configPath);
+        const isValidExtension: boolean = CLIUtils.allowedConfigFileExtensions.includes(configFileExtension);
+
+        if (!isValidExtension) {
+            throw new ReferenceError('Given config path must be a valid `.js` or `.json` file path');
+        }
+
         try {
             config = require(configPath);
         } catch {
             try {
                 config = __non_webpack_require__(configPath);
             } catch {
-                throw new ReferenceError('Given config path must be a valid `.js` or `.json` file path');
+                throw new ReferenceError(`Cannot open config file with path: ${configPath}`);
             }
         }
 

+ 18 - 8
src/custom-code-helpers/string-array/StringArrayCallsWrapperCodeHelper.ts

@@ -10,13 +10,11 @@ import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEn
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
-import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
 
 import { initializable } from '../../decorators/Initializable';
 
 import { AtobTemplate } from './templates/string-array-calls-wrapper/AtobTemplate';
-import { GlobalVariableNoEvalTemplate } from '../common/templates/GlobalVariableNoEvalTemplate';
 import { Rc4Template } from './templates/string-array-calls-wrapper/Rc4Template';
 import { SelfDefendingTemplate } from './templates/string-array-calls-wrapper/SelfDefendingTemplate';
 import { StringArrayBase64DecodeTemplate } from './templates/string-array-calls-wrapper/StringArrayBase64DecodeTemplate';
@@ -28,6 +26,12 @@ import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
 export class StringArrayCallsWrapperCodeHelper extends AbstractCustomCodeHelper {
+    /**
+     * @type {string}
+     */
+    @initializable()
+    private atobFunctionName!: string;
+
     /**
      * @type {string}
      */
@@ -76,13 +80,16 @@ export class StringArrayCallsWrapperCodeHelper extends AbstractCustomCodeHelper
     /**
      * @param {string} stringArrayName
      * @param {string} stringArrayCallsWrapperName
+     * @param {string} atobFunctionName
      */
     public initialize (
         stringArrayName: string,
-        stringArrayCallsWrapperName: string
+        stringArrayCallsWrapperName: string,
+        atobFunctionName: string
     ): void {
         this.stringArrayName = stringArrayName;
         this.stringArrayCallsWrapperName = stringArrayCallsWrapperName;
+        this.atobFunctionName = atobFunctionName;
     }
 
     /**
@@ -117,10 +124,12 @@ export class StringArrayCallsWrapperCodeHelper extends AbstractCustomCodeHelper
      * @returns {string}
      */
     private getDecodeStringArrayTemplate (): string {
-        const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
-            ? this.getGlobalVariableTemplate()
-            : GlobalVariableNoEvalTemplate();
-        const atobPolyfill: string = this.customCodeHelperFormatter.formatTemplate(AtobTemplate(), { globalVariableTemplate });
+        const atobPolyfill: string = this.customCodeHelperFormatter.formatTemplate(AtobTemplate(), {
+            atobFunctionName: this.atobFunctionName
+        });
+        const rc4Polyfill: string = this.customCodeHelperFormatter.formatTemplate(Rc4Template(), {
+            atobFunctionName: this.atobFunctionName
+        });
 
         let decodeStringArrayTemplate: string = '';
         let selfDefendingCode: string = '';
@@ -144,8 +153,8 @@ export class StringArrayCallsWrapperCodeHelper extends AbstractCustomCodeHelper
                     StringArrayRC4DecodeTemplate(this.randomGenerator),
                     {
                         atobPolyfill,
+                        rc4Polyfill,
                         selfDefendingCode,
-                        rc4Polyfill: Rc4Template(),
                         stringArrayCallsWrapperName: this.stringArrayCallsWrapperName
                     }
                 );
@@ -157,6 +166,7 @@ export class StringArrayCallsWrapperCodeHelper extends AbstractCustomCodeHelper
                     StringArrayBase64DecodeTemplate(this.randomGenerator),
                     {
                         atobPolyfill,
+                        atobFunctionName: this.atobFunctionName,
                         selfDefendingCode,
                         stringArrayCallsWrapperName: this.stringArrayCallsWrapperName
                     }

+ 2 - 1
src/custom-code-helpers/string-array/group/StringArrayCodeHelperGroup.ts

@@ -118,9 +118,10 @@ export class StringArrayCodeHelperGroup extends AbstractCustomCodeHelperGroup {
         const stringArrayName: string = this.stringArrayStorage.getStorageName();
         const stringArrayCallsWrapperName: string = this.stringArrayStorage.getStorageCallsWrapperName();
         const stringArrayRotationAmount: number = this.stringArrayStorage.getRotationAmount();
+        const atobFunctionName: string = this.randomGenerator.getRandomString(6);
 
         stringArrayCodeHelper.initialize(this.stringArrayStorage, stringArrayName);
-        stringArrayCallsWrapperCodeHelper.initialize(stringArrayName, stringArrayCallsWrapperName);
+        stringArrayCallsWrapperCodeHelper.initialize(stringArrayName, stringArrayCallsWrapperName, atobFunctionName);
         stringArrayRotateFunctionCodeHelper.initialize(stringArrayName, stringArrayRotationAmount);
 
         this.customCodeHelpers.set(CustomCodeHelper.StringArray, stringArrayCodeHelper);

+ 15 - 20
src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts

@@ -6,27 +6,22 @@ import { numbersString } from '../../../../constants/NumbersString';
  * @returns {string}
  */
 export function AtobTemplate (): string {
+    // swapped lowercase and uppercase groups of alphabet to prevent easy decode!!!!
     return `
-        (function () {
-            {globalVariableTemplate}
-            
-            const chars = '${alphabetStringUppercase}${alphabetString}${numbersString}+/=';
+        var {atobFunctionName} = function (input) {
+            const chars = '${alphabetString}${alphabetStringUppercase}${numbersString}+/=';
 
-            that.atob || (
-                that.atob = function(input) {
-                    const str = String(input).replace(/=+$/, '');
-                    let output = '';
-                    for (
-                        let bc = 0, bs, buffer, idx = 0;
-                        buffer = str.charAt(idx++);
-                        ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
-                            bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
-                    ) {
-                        buffer = chars.indexOf(buffer);
-                    }
-                    return output;
-                }
-            );
-        })();
+            const str = String(input).replace(/=+$/, '');
+            let output = '';
+            for (
+                let bc = 0, bs, buffer, idx = 0;
+                buffer = str.charAt(idx++);
+                ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
+                    bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
+            ) {
+                buffer = chars.indexOf(buffer);
+            }
+            return output;
+        };
     `;
 }

+ 1 - 1
src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/Rc4Template.ts

@@ -6,7 +6,7 @@ export function Rc4Template (): string {
         const rc4 = function (str, key) {
             let s = [], j = 0, x, res = '', newStr = '';
            
-            str = atob(str);
+            str = {atobFunctionName}(str);
                 
             for (let k = 0, length = str.length; k < length; k++) {
                 newStr += '%' + ('00' + str.charCodeAt(k).toString(16)).slice(-2);

+ 1 - 1
src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/StringArrayBase64DecodeTemplate.ts

@@ -18,7 +18,7 @@ export function StringArrayBase64DecodeTemplate (
             {atobPolyfill}
             
             {stringArrayCallsWrapperName}.${base64DecodeFunctionIdentifier} = function (str) {
-                const string = atob(str);
+                const string = {atobFunctionName}(str);
                 let newStringChars = [];
                 
                 for (let i = 0, length = string.length; i < length; i++) {

+ 2 - 1
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -100,7 +100,8 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      * @returns {boolean}
      */
     private static isProhibitedNodeInsideCollectedBlockStatement (targetNode: ESTree.Node): boolean {
-        return NodeGuards.isBreakStatementNode(targetNode)
+        return NodeGuards.isFunctionDeclarationNode(targetNode) // can break code on strict mode
+            || NodeGuards.isBreakStatementNode(targetNode)
             || NodeGuards.isContinueStatementNode(targetNode)
             || NodeGuards.isAwaitExpressionNode(targetNode)
             || NodeGuards.isSuperNode(targetNode);

+ 2 - 1
src/utils/CryptUtils.ts

@@ -32,7 +32,8 @@ export class CryptUtils implements ICryptUtils {
      * @returns {string}
      */
     public btoa (string: string): string {
-        const chars: string = `${alphabetStringUppercase}${alphabetString}${numbersString}+/=`;
+        // swapped lowercase and uppercase groups of alphabet to prevent easy decode!!!!
+        const chars: string = `${alphabetString}${alphabetStringUppercase}${numbersString}+/=`;
 
         let output: string = '';
 

+ 8 - 4
test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperNodeTemplate.spec.ts

@@ -12,7 +12,6 @@ import { IObfuscatedCode } from '../../../../../../src/interfaces/source-code/IO
 import { IRandomGenerator } from '../../../../../../src/interfaces/utils/IRandomGenerator';
 
 import { AtobTemplate } from '../../../../../../src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate';
-import { GlobalVariableTemplate1 } from '../../../../../../src/custom-code-helpers/common/templates/GlobalVariableTemplate1';
 import { Rc4Template } from '../../../../../../src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/Rc4Template';
 import { StringArrayBase64DecodeTemplate } from '../../../../../../src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/StringArrayBase64DecodeTemplate';
 import { StringArrayCallsWrapperTemplate } from '../../../../../../src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/StringArrayCallsWrapperTemplate';
@@ -27,6 +26,7 @@ import { readFileAsString } from '../../../../../helpers/readFileAsString';
 describe('StringArrayCallsWrapperTemplate', () => {
     const stringArrayName: string = 'stringArrayName';
     const stringArrayCallsWrapperName: string = 'stringArrayCallsWrapperName';
+    const atobFunctionName: string = 'atob';
 
     let cryptUtils: ICryptUtils,
         randomGenerator: IRandomGenerator;
@@ -47,12 +47,13 @@ describe('StringArrayCallsWrapperTemplate', () => {
 
         before(() => {
             const atobPolyfill = format(AtobTemplate(), {
-                globalVariableTemplate: GlobalVariableTemplate1()
+                atobFunctionName
             });
             const atobDecodeTemplate: string = format(
                 StringArrayBase64DecodeTemplate(randomGenerator),
                 {
                     atobPolyfill,
+                    atobFunctionName,
                     selfDefendingCode: '',
                     stringArrayCallsWrapperName
                 }
@@ -86,13 +87,16 @@ describe('StringArrayCallsWrapperTemplate', () => {
 
         before(() => {
             const atobPolyfill = format(AtobTemplate(), {
-                globalVariableTemplate: GlobalVariableTemplate1()
+                atobFunctionName
+            });
+            const rc4Polyfill = format(Rc4Template(), {
+                atobFunctionName
             });
             const rc4decodeCodeHelperTemplate: string = format(
                 StringArrayRC4DecodeTemplate(randomGenerator),
                 {
                     atobPolyfill,
-                    rc4Polyfill: Rc4Template(),
+                    rc4Polyfill,
                     selfDefendingCode: '',
                     stringArrayCallsWrapperName
                 }

+ 175 - 122
test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts

@@ -126,28 +126,26 @@ describe('DeadCodeInjectionTransformer', () => {
             });
         });
 
-        describe('Variant #4 - break or continue statement in block statement', () => {
-            describe('Variant #1', () => {
+        describe('Variant #4 - prohibited node inside collected block statement', () => {
+            describe('Variant #1 - function declaration in block statement', () => {
                 const functionRegExp: RegExp = new RegExp(
                     `var ${variableMatch} *= *function *\\(\\) *\\{` +
                         `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
                     `\\};`,
                     'g'
                 );
-                const loopRegExp: RegExp = new RegExp(
-                    `for *\\(var ${variableMatch} *= *${hexMatch}; *${variableMatch} *< *${hexMatch}; *${variableMatch}\\+\\+\\) *\\{` +
-                        `(?:continue|break);` +
-                    `\\}`,
+                const functionDeclarationRegExp: RegExp = new RegExp(
+                    `function *${variableMatch} *\\(${variableMatch}\\) *{}`,
                     'g'
                 );
                 const expectedFunctionMatchesLength: number = 4;
-                const expectedLoopMatchesLength: number = 2;
+                const expectedFunctionDeclarationMatchesLength: number = 1;
 
                 let functionMatchesLength: number = 0,
-                    loopMatchesLength: number = 0;
+                    functionDeclarationMatchesLength: number = 0;
 
                 before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/break-continue-statement-1.js');
+                    const code: string = readFileAsString(__dirname + '/fixtures/function-declaration-inside-block-statement.js');
 
                     const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
                         code,
@@ -160,14 +158,14 @@ describe('DeadCodeInjectionTransformer', () => {
                         }
                     ).getObfuscatedCode();
                     const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
-                    const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(loopRegExp);
+                    const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionDeclarationRegExp);
 
                     if (functionMatches) {
                         functionMatchesLength = functionMatches.length;
                     }
 
                     if (loopMatches) {
-                        loopMatchesLength = loopMatches.length;
+                        functionDeclarationMatchesLength = loopMatches.length;
                     }
                 });
 
@@ -176,30 +174,136 @@ describe('DeadCodeInjectionTransformer', () => {
                 });
 
                 it('match #2: shouldn\'t add dead code', () => {
-                    assert.equal(loopMatchesLength, expectedLoopMatchesLength);
+                    assert.equal(functionDeclarationMatchesLength, expectedFunctionDeclarationMatchesLength);
                 });
             });
 
-            describe('Variant #2', () => {
+            describe('Variant #2 - break or continue statement in block statement', () => {
+                describe('Variant #1', () => {
+                    const functionRegExp: RegExp = new RegExp(
+                        `var ${variableMatch} *= *function *\\(\\) *\\{` +
+                            `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                        `\\};`,
+                        'g'
+                    );
+                    const loopRegExp: RegExp = new RegExp(
+                        `for *\\(var ${variableMatch} *= *${hexMatch}; *${variableMatch} *< *${hexMatch}; *${variableMatch}\\+\\+\\) *\\{` +
+                        `(?:continue|break);` +
+                        `\\}`,
+                        'g'
+                    );
+                    const expectedFunctionMatchesLength: number = 4;
+                    const expectedLoopMatchesLength: number = 2;
+
+                    let functionMatchesLength: number = 0,
+                        loopMatchesLength: number = 0;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/break-continue-statement-1.js');
+
+                        const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                deadCodeInjection: true,
+                                deadCodeInjectionThreshold: 1,
+                                stringArray: true,
+                                stringArrayThreshold: 1
+                            }
+                        ).getObfuscatedCode();
+                        const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
+                        const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(loopRegExp);
+
+                        if (functionMatches) {
+                            functionMatchesLength = functionMatches.length;
+                        }
+
+                        if (loopMatches) {
+                            loopMatchesLength = loopMatches.length;
+                        }
+                    });
+
+                    it('match #1: shouldn\'t add dead code', () => {
+                        assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
+                    });
+
+                    it('match #2: shouldn\'t add dead code', () => {
+                        assert.equal(loopMatchesLength, expectedLoopMatchesLength);
+                    });
+                });
+
+                describe('Variant #2', () => {
+                    const functionRegExp: RegExp = new RegExp(
+                        `var ${variableMatch} *= *function *\\(\\) *\\{` +
+                            `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                        `\\};`,
+                        'g'
+                    );
+                    const loopRegExp: RegExp = new RegExp(
+                        `for *\\(var ${variableMatch} *= *${hexMatch}; *${variableMatch} *< *${hexMatch}; *${variableMatch}\\+\\+\\) *` +
+                            `(?:continue|break);`,
+                        'g'
+                    );
+                    const expectedFunctionMatchesLength: number = 4;
+                    const expectedLoopMatchesLength: number = 2;
+
+                    let functionMatchesLength: number = 0,
+                        loopMatchesLength: number = 0;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/break-continue-statement-2.js');
+
+                        const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                deadCodeInjection: true,
+                                deadCodeInjectionThreshold: 1,
+                                stringArray: true,
+                                stringArrayThreshold: 1
+                            }
+                        ).getObfuscatedCode();
+                        const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
+                        const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(loopRegExp);
+
+                        if (functionMatches) {
+                            functionMatchesLength = functionMatches.length;
+                        }
+
+                        if (loopMatches) {
+                            loopMatchesLength = loopMatches.length;
+                        }
+                    });
+
+                    it('match #1: shouldn\'t add dead code', () => {
+                        assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
+                    });
+
+                    it('match #2: shouldn\'t add dead code', () => {
+                        assert.equal(loopMatchesLength, expectedLoopMatchesLength);
+                    });
+                });
+            });
+
+            describe('Variant #3 - await expression in block statement', () => {
                 const functionRegExp: RegExp = new RegExp(
                     `var ${variableMatch} *= *function *\\(\\) *\\{` +
                         `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
                     `\\};`,
                     'g'
                 );
-                const loopRegExp: RegExp = new RegExp(
-                    `for *\\(var ${variableMatch} *= *${hexMatch}; *${variableMatch} *< *${hexMatch}; *${variableMatch}\\+\\+\\) *` +
-                        `(?:continue|break);`,
+                const awaitExpressionRegExp: RegExp = new RegExp(
+                    `await *${variableMatch}\\(\\)`,
                     'g'
                 );
                 const expectedFunctionMatchesLength: number = 4;
-                const expectedLoopMatchesLength: number = 2;
+                const expectedAwaitExpressionMatchesLength: number = 1;
 
                 let functionMatchesLength: number = 0,
-                    loopMatchesLength: number = 0;
+                    awaitExpressionMatchesLength: number = 0;
 
                 before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/break-continue-statement-2.js');
+                    const code: string = readFileAsString(__dirname + '/fixtures/await-expression.js');
 
                     const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
                         code,
@@ -212,14 +316,14 @@ describe('DeadCodeInjectionTransformer', () => {
                         }
                     ).getObfuscatedCode();
                     const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
-                    const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(loopRegExp);
+                    const awaitExpressionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(awaitExpressionRegExp);
 
                     if (functionMatches) {
                         functionMatchesLength = functionMatches.length;
                     }
 
-                    if (loopMatches) {
-                        loopMatchesLength = loopMatches.length;
+                    if (awaitExpressionMatches) {
+                        awaitExpressionMatchesLength = awaitExpressionMatches.length;
                     }
                 });
 
@@ -228,114 +332,63 @@ describe('DeadCodeInjectionTransformer', () => {
                 });
 
                 it('match #2: shouldn\'t add dead code', () => {
-                    assert.equal(loopMatchesLength, expectedLoopMatchesLength);
+                    assert.equal(awaitExpressionMatchesLength, expectedAwaitExpressionMatchesLength);
                 });
             });
-        });
-
-        describe('Variant #5 - await expression in block statement', () => {
-            const functionRegExp: RegExp = new RegExp(
-                `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                    `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
-                `\\};`,
-                'g'
-            );
-            const awaitExpressionRegExp: RegExp = new RegExp(
-                `await *${variableMatch}\\(\\)`,
-                'g'
-            );
-            const expectedFunctionMatchesLength: number = 4;
-            const expectedAwaitExpressionMatchesLength: number = 1;
-
-            let functionMatchesLength: number = 0,
-                awaitExpressionMatchesLength: number = 0;
-
-            before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/await-expression.js');
-
-                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
-                    code,
-                    {
-                        ...NO_ADDITIONAL_NODES_PRESET,
-                        deadCodeInjection: true,
-                        deadCodeInjectionThreshold: 1,
-                        stringArray: true,
-                        stringArrayThreshold: 1
-                    }
-                ).getObfuscatedCode();
-                const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
-                const awaitExpressionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(awaitExpressionRegExp);
-
-                if (functionMatches) {
-                    functionMatchesLength = functionMatches.length;
-                }
-
-                if (awaitExpressionMatches) {
-                    awaitExpressionMatchesLength = awaitExpressionMatches.length;
-                }
-            });
-
-            it('match #1: shouldn\'t add dead code', () => {
-                assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
-            });
 
-            it('match #2: shouldn\'t add dead code', () => {
-                assert.equal(awaitExpressionMatchesLength, expectedAwaitExpressionMatchesLength);
-            });
-        });
+            describe('Variant #4 - super expression in block statement', () => {
+                const functionRegExp: RegExp = new RegExp(
+                    `var ${variableMatch} *= *function *\\(\\) *\\{` +
+                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                    `\\};`,
+                    'g'
+                );
+                const superExpressionRegExp: RegExp = new RegExp(
+                    `super *\\(\\);`,
+                    'g'
+                );
+                const expectedFunctionMatchesLength: number = 4;
+                const expectedSuperExpressionMatchesLength: number = 1;
 
-        describe('Variant #6 - super expression in block statement', () => {
-            const functionRegExp: RegExp = new RegExp(
-                `var ${variableMatch} *= *function *\\(\\) *\\{` +
-                    `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
-                `\\};`,
-                'g'
-            );
-            const superExpressionRegExp: RegExp = new RegExp(
-                `super *\\(\\);`,
-                'g'
-            );
-            const expectedFunctionMatchesLength: number = 4;
-            const expectedSuperExpressionMatchesLength: number = 1;
+                let functionMatchesLength: number = 0,
+                    superExpressionMatchesLength: number = 0;
 
-            let functionMatchesLength: number = 0,
-                superExpressionMatchesLength: number = 0;
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/super-expression.js');
 
-            before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/super-expression.js');
+                    const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            deadCodeInjection: true,
+                            deadCodeInjectionThreshold: 1,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                    const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
+                    const superExpressionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(superExpressionRegExp);
 
-                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
-                    code,
-                    {
-                        ...NO_ADDITIONAL_NODES_PRESET,
-                        deadCodeInjection: true,
-                        deadCodeInjectionThreshold: 1,
-                        stringArray: true,
-                        stringArrayThreshold: 1
+                    if (functionMatches) {
+                        functionMatchesLength = functionMatches.length;
                     }
-                ).getObfuscatedCode();
-                const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
-                const superExpressionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(superExpressionRegExp);
 
-                if (functionMatches) {
-                    functionMatchesLength = functionMatches.length;
-                }
-
-                if (superExpressionMatches) {
-                    superExpressionMatchesLength = superExpressionMatches.length;
-                }
-            });
+                    if (superExpressionMatches) {
+                        superExpressionMatchesLength = superExpressionMatches.length;
+                    }
+                });
 
-            it('match #1: shouldn\'t add dead code', () => {
-                assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
-            });
+                it('match #1: shouldn\'t add dead code', () => {
+                    assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
+                });
 
-            it('match #2: shouldn\'t add dead code', () => {
-                assert.equal(superExpressionMatchesLength, expectedSuperExpressionMatchesLength);
+                it('match #2: shouldn\'t add dead code', () => {
+                    assert.equal(superExpressionMatchesLength, expectedSuperExpressionMatchesLength);
+                });
             });
         });
 
-        describe('Variant #7 - chance of `IfStatement` variant', () => {
+        describe('Variant #5 - chance of `IfStatement` variant', () => {
             const samplesCount: number = 1000;
             const delta: number = 0.1;
             const expectedDistribution: number = 0.25;
@@ -437,7 +490,7 @@ describe('DeadCodeInjectionTransformer', () => {
             });
         });
 
-        describe('Variant #8 - block scope of block statement is `ProgramNode`', () => {
+        describe('Variant #6 - block scope of block statement is `ProgramNode`', () => {
             const regExp: RegExp = new RegExp(
                 `if *\\(!!\\[\\]\\) *{` +
                     `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
@@ -466,7 +519,7 @@ describe('DeadCodeInjectionTransformer', () => {
             });
         });
 
-        describe('Variant #9 - correct obfuscation of dead-code block statements', () => {
+        describe('Variant #7 - correct obfuscation of dead-code block statements', () => {
             const variableName: string = 'importantVariableName';
 
             let obfuscatedCode: string;
@@ -490,7 +543,7 @@ describe('DeadCodeInjectionTransformer', () => {
             });
         });
 
-        describe('Variant #10 - unique names for dead code identifiers', () => {
+        describe('Variant #8 - unique names for dead code identifiers', () => {
             /**
              * Code:
              *
@@ -679,7 +732,7 @@ describe('DeadCodeInjectionTransformer', () => {
             });
         });
 
-        describe('Variant #11 - block statements with empty body', () => {
+        describe('Variant #9 - block statements with empty body', () => {
             const regExp: RegExp = new RegExp(
                 `function *${variableMatch} *\\(\\) *{ *} *` +
                 `${variableMatch} *\\(\\); *`,
@@ -715,7 +768,7 @@ describe('DeadCodeInjectionTransformer', () => {
             });
         });
 
-        describe('Variant #12 - block statement with scope-hoisting', () => {
+        describe('Variant #10 - block statement with scope-hoisting', () => {
             describe('Variant #1: collecting of block statements', () => {
                 const regExp: RegExp = new RegExp(
                     `${variableMatch} *\\(\\); *` +
@@ -788,7 +841,7 @@ describe('DeadCodeInjectionTransformer', () => {
             });
         });
 
-        describe('Variant #13 - prevailing kind of variables of inserted code', () => {
+        describe('Variant #11 - prevailing kind of variables of inserted code', () => {
             describe('Variant #1: base', () => {
                 const variableDeclarationsRegExp: RegExp = new RegExp(
                     `const ${variableMatch} *= *\\[\\]; *` +

+ 25 - 0
test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/function-declaration-inside-block-statement.js

@@ -0,0 +1,25 @@
+(function(){
+    if (true) {
+        var foo = function () {
+            console.log('abc');
+        };
+        var bar = function () {
+            console.log('def');
+        };
+        var baz = function () {
+            console.log('ghi');
+        };
+        var bark = function () {
+            console.log('jkl');
+        };
+
+        if (true) {
+            function hawk (param) {}
+        }
+
+        foo();
+        bar();
+        baz();
+        bark();
+    }
+})();

+ 2 - 1
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec.ts

@@ -7,6 +7,7 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/N
 
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 import { getRegExpMatch } from '../../../../helpers/getRegExpMatch';
+import { swapLettersCase } from '../../../../helpers/swapLettersCase';
 
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 
@@ -186,7 +187,7 @@ describe('LiteralTransformer', () => {
         });
 
         describe('Variant #8: base64 encoding', () => {
-            const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['dGVzdA=='\];/;
+            const stringArrayRegExp: RegExp = new RegExp(`^var _0x([a-f0-9]){4} *= *\\['${swapLettersCase('dGVzdA==')}'];`);
             const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
 
             let obfuscatedCode: string;

+ 14 - 0
test/helpers/swapLettersCase.ts

@@ -0,0 +1,14 @@
+/**
+ * @param {string} value
+ * @returns {string}
+ */
+export function swapLettersCase (value: string): string {
+    return value
+        .split('')
+        .map((letter: string) =>
+            letter === letter.toUpperCase()
+                ? letter.toLowerCase()
+                : letter.toUpperCase()
+        )
+        .join('');
+}

+ 18 - 2
test/unit-tests/cli/utils/CLIUtils.spec.ts

@@ -51,7 +51,23 @@ describe('CLIUtils', () => {
             });
         });
 
-        describe('Variant #2: invalid config file path', () => {
+        describe('Variant #2: invalid config file extension', () => {
+            const configDirName: string = 'test/fixtures';
+            const configFileName: string = 'configs.config';
+            const configFilePath: string = `../../../${configDirName}/${configFileName}`;
+
+            let testFunc: () => void;
+
+            before(() => {
+                testFunc = () => CLIUtils.getUserConfig(configFilePath);
+            });
+
+            it('should throw an error if `configFilePath` is not a valid path', () => {
+                assert.throws(testFunc, /Given config path must be a valid/);
+            });
+        });
+
+        describe('Variant #3: invalid config file path', () => {
             const configDirName: string = 'test/fixtures';
             const configFileName: string = 'configs.js';
             const configFilePath: string = `../../../${configDirName}/${configFileName}`;
@@ -63,7 +79,7 @@ describe('CLIUtils', () => {
             });
 
             it('should throw an error if `configFilePath` is not a valid path', () => {
-                assert.throws(testFunc, ReferenceError);
+                assert.throws(testFunc, /Cannot open config file/);
             });
         });
     });

+ 3 - 1
test/unit-tests/utils/CryptUtils.spec.ts

@@ -9,6 +9,8 @@ import { IInversifyContainerFacade } from '../../../src/interfaces/container/IIn
 
 import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
 
+import { swapLettersCase } from '../../helpers/swapLettersCase';
+
 describe('CryptUtils', () => {
     let cryptUtils: ICryptUtils;
 
@@ -20,7 +22,7 @@ describe('CryptUtils', () => {
     });
 
     describe('btoa', () => {
-        const expectedString: string = 'c3RyaW5n';
+        const expectedString: string = swapLettersCase('c3RyaW5n');
 
         let string: string;
 

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff