Explorar o código

Merge pull request #466 from javascript-obfuscator/custom-nodes-identifier-names-collision-fix

Custom nodes identifier names collision fix
Timofey Kachalov %!s(int64=5) %!d(string=hai) anos
pai
achega
248f6348e3
Modificáronse 29 ficheiros con 357 adicións e 150 borrados
  1. 6 0
      CHANGELOG.md
  2. 2 2
      README.md
  3. 0 0
      dist/index.browser.js
  4. 0 0
      dist/index.cli.js
  5. 0 0
      dist/index.js
  6. 1 1
      package.json
  7. 1 1
      src/JavaScriptObfuscator.ts
  8. 1 1
      src/cli/JavaScriptObfuscatorCLI.ts
  9. 41 40
      src/container/modules/generators/GeneratorsModule.ts
  10. 1 1
      src/container/modules/node-transformers/PreparingTransformersModule.ts
  11. 1 1
      src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts
  12. 1 1
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  13. 1 1
      src/interfaces/options/IOptions.d.ts
  14. 7 2
      src/interfaces/utils/IRandomGenerator.d.ts
  15. 0 0
      src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts
  16. 2 3
      src/options/Options.ts
  17. 2 0
      src/options/OptionsNormalizer.ts
  18. 25 0
      src/options/normalizer-rules/SeedRule.ts
  19. 0 4
      src/storages/string-array/StringArrayStorage.ts
  20. 1 1
      src/templates/debug-protection-nodes/debug-protection-function-call-node/DebugProtectionFunctionCallTemplate.ts
  21. 22 26
      src/utils/RandomGenerator.ts
  22. 38 7
      test/dev/dev.ts
  23. 43 2
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  24. 4 0
      test/functional-tests/javascript-obfuscator/fixtures/custom-nodes-identifier-names-collision.js
  25. 2 2
      test/functional-tests/node-transformers/obfuscating-transformers/class-declaration-transformer/ClassDeclarationTransformer.spec.ts
  26. 2 2
      test/functional-tests/node-transformers/obfuscating-transformers/function-declaration-transformer/FunctionDeclarationTransformer.spec.ts
  27. 9 9
      test/functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/VariableDeclarationTransformer.spec.ts
  28. 99 36
      test/functional-tests/options/OptionsNormalizer.spec.ts
  29. 45 7
      test/functional-tests/templates/debug-protection-nodes/DebugProtectionFunctionCallTemplate.spec.ts

+ 6 - 0
CHANGELOG.md

@@ -1,5 +1,11 @@
 Change Log
 
+v0.20.1
+---
+* Fixed identifier names generations for `mangled` and `dictionary` identifier names generators
+* Fixed combination of `identifierNamesGenerator: dictionary` and `debugProtection` options
+* `seed` option now accepts `string` and `number` values
+
 v0.20.0
 ---
 * **Breaking:** dropped support of Node 8 because of end of maintenance support

+ 2 - 2
README.md

@@ -343,7 +343,7 @@ Following options are available for the JS Obfuscator:
     --reserved-names '<list>' (comma separated)
     --reserved-strings '<list>' (comma separated)
     --rotate-string-array <boolean>
-    --seed <number>
+    --seed <string|number>
     --self-defending <boolean>
     --source-map <boolean>
     --source-map-base-url <string>
@@ -673,7 +673,7 @@ Shift the `stringArray` array by a fixed and random (generated at the code obfus
 This option is recommended if your original source code isn't small, as the helper function can attract attention.
 
 ### `seed`
-Type: `number` Default: `0`
+Type: `string|number` Default: `0`
 
 This option sets seed for random generator. This is useful for creating repeatable results.
 

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
dist/index.browser.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
dist/index.cli.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

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

+ 1 - 1
src/JavaScriptObfuscator.ts

@@ -127,7 +127,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
         const timeStart: number = Date.now();
         this.logger.info(LoggingMessage.Version, process.env.VERSION);
         this.logger.info(LoggingMessage.ObfuscationStarted);
-        this.logger.info(LoggingMessage.RandomGeneratorSeed, this.randomGenerator.getSeed());
+        this.logger.info(LoggingMessage.RandomGeneratorSeed, this.randomGenerator.getInputSeed());
 
         // parse AST tree
         const astTree: ESTree.Program = this.parseCode(sourceCode);

+ 1 - 1
src/cli/JavaScriptObfuscatorCLI.ts

@@ -287,7 +287,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 BooleanSanitizer
             )
             .option(
-                '--seed <number>',
+                '--seed <string|number>',
                 'Sets seed for random generator. This is useful for creating repeatable results.',
                 parseFloat
             )

+ 41 - 40
src/container/modules/generators/GeneratorsModule.ts

@@ -28,45 +28,46 @@ export const generatorsModule: interfaces.ContainerModule = new ContainerModule(
         .whenTargetNamed(IdentifierNamesGenerator.MangledIdentifierNamesGenerator);
 
     // identifier name generator factory
+    function identifierNameGeneratorFactory (): (context: interfaces.Context) => (options: IOptions) => IIdentifierNamesGenerator {
+        let cachedIdentifierNamesGenerator: IIdentifierNamesGenerator | null = null;
+
+        return (context: interfaces.Context): (options: IOptions) => IIdentifierNamesGenerator => (options: IOptions) => {
+            if (cachedIdentifierNamesGenerator) {
+                return cachedIdentifierNamesGenerator;
+            }
+
+            let identifierNamesGenerator: IIdentifierNamesGenerator;
+
+            switch (options.identifierNamesGenerator) {
+                case IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator:
+                    identifierNamesGenerator = context.container.getNamed<IIdentifierNamesGenerator>(
+                        ServiceIdentifiers.IIdentifierNamesGenerator,
+                        IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator
+                    );
+
+                    break;
+
+                case IdentifierNamesGenerator.MangledIdentifierNamesGenerator:
+                    identifierNamesGenerator = context.container.getNamed<IIdentifierNamesGenerator>(
+                        ServiceIdentifiers.IIdentifierNamesGenerator,
+                        IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                    );
+
+                    break;
+
+                case IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator:
+                default:
+                    identifierNamesGenerator = context.container.getNamed<IIdentifierNamesGenerator>(
+                        ServiceIdentifiers.IIdentifierNamesGenerator,
+                        IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator
+                    );
+            }
+
+            cachedIdentifierNamesGenerator = identifierNamesGenerator;
+
+            return identifierNamesGenerator;
+        };
+    }
     bind<IIdentifierNamesGenerator>(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
-        .toFactory<IIdentifierNamesGenerator>((context: interfaces.Context): (options: IOptions) => IIdentifierNamesGenerator => {
-            let cachedIdentifierNamesGenerator: IIdentifierNamesGenerator | null = null;
-
-            return (options: IOptions) => {
-                if (cachedIdentifierNamesGenerator) {
-                    return cachedIdentifierNamesGenerator;
-                }
-
-                let identifierNamesGenerator: IIdentifierNamesGenerator;
-
-                switch (options.identifierNamesGenerator) {
-                    case IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator:
-                        identifierNamesGenerator = context.container.getNamed<IIdentifierNamesGenerator>(
-                            ServiceIdentifiers.IIdentifierNamesGenerator,
-                            IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator
-                        );
-
-                        break;
-
-                    case IdentifierNamesGenerator.MangledIdentifierNamesGenerator:
-                        identifierNamesGenerator = context.container.getNamed<IIdentifierNamesGenerator>(
-                            ServiceIdentifiers.IIdentifierNamesGenerator,
-                            IdentifierNamesGenerator.MangledIdentifierNamesGenerator
-                        );
-
-                        break;
-
-                    case IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator:
-                    default:
-                        identifierNamesGenerator = context.container.getNamed<IIdentifierNamesGenerator>(
-                            ServiceIdentifiers.IIdentifierNamesGenerator,
-                            IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator
-                        );
-                }
-
-                cachedIdentifierNamesGenerator = identifierNamesGenerator;
-
-                return identifierNamesGenerator;
-            };
-        });
+        .toFactory<IIdentifierNamesGenerator>(identifierNameGeneratorFactory());
 });

+ 1 - 1
src/container/modules/node-transformers/PreparingTransformersModule.ts

@@ -12,7 +12,7 @@ import { BlackListObfuscatingGuard } from '../../../node-transformers/preparing-
 import { CommentsTransformer } from '../../../node-transformers/preparing-transformers/CommentsTransformer';
 import { ConditionalCommentObfuscatingGuard } from '../../../node-transformers/preparing-transformers/obfuscating-guards/ConditionalCommentObfuscatingGuard';
 import { CustomNodesTransformer } from '../../../node-transformers/preparing-transformers/CustomNodesTransformer';
-import { EvalCallExpressionTransformer } from '../../../node-transformers/preparing-transformers/EvaCallExpressionTransformer';
+import { EvalCallExpressionTransformer } from '../../../node-transformers/preparing-transformers/EvalCallExpressionTransformer';
 import { MetadataTransformer } from '../../../node-transformers/preparing-transformers/MetadataTransformer';
 import { ObfuscatingGuardsTransformer } from '../../../node-transformers/preparing-transformers/ObfuscatingGuardsTransformer';
 import { ParentificationTransformer } from '../../../node-transformers/preparing-transformers/ParentificationTransformer';

+ 1 - 1
src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts

@@ -101,7 +101,7 @@ export class StringArrayCallsWrapper extends AbstractCustomNode {
                 ...NO_ADDITIONAL_NODES_PRESET,
                 identifierNamesGenerator: this.options.identifierNamesGenerator,
                 identifiersDictionary: this.options.identifiersDictionary,
-                seed: this.options.seed
+                seed: this.randomGenerator.getRawSeed()
             }
         ).getObfuscatedCode();
     }

+ 1 - 1
src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts

@@ -108,7 +108,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
                 ...NO_ADDITIONAL_NODES_PRESET,
                 identifierNamesGenerator: this.options.identifierNamesGenerator,
                 identifiersDictionary: this.options.identifiersDictionary,
-                seed: this.options.seed
+                seed: this.randomGenerator.getRawSeed()
             }
         ).getObfuscatedCode();
     }

+ 1 - 1
src/interfaces/options/IOptions.d.ts

@@ -23,7 +23,7 @@ export interface IOptions {
     readonly reservedNames: string[];
     readonly reservedStrings: string[];
     readonly rotateStringArray: boolean;
-    readonly seed: number;
+    readonly seed: string | number;
     readonly selfDefending: boolean;
     readonly sourceMap: boolean;
     readonly sourceMapBaseUrl: string;

+ 7 - 2
src/interfaces/utils/IRandomGenerator.d.ts

@@ -26,7 +26,12 @@ export interface IRandomGenerator {
     getRandomString (length: number, pool?: string): string;
 
     /**
-     * @returns {number}
+     * @returns {string}
+     */
+    getInputSeed (): string;
+
+    /**
+     * @returns {string}
      */
-    getSeed (): number;
+    getRawSeed (): string;
 }

+ 0 - 0
src/node-transformers/preparing-transformers/EvaCallExpressionTransformer.ts → src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts


+ 2 - 3
src/options/Options.ts

@@ -176,10 +176,9 @@ export class Options implements IOptions {
     public readonly rotateStringArray!: boolean;
 
     /**
-     * @type {number}
+     * @type {string | number}
      */
-    @IsNumber()
-    public readonly seed!: number;
+    public readonly seed!: string | number;
 
     /**
      * @type {boolean}

+ 2 - 0
src/options/OptionsNormalizer.ts

@@ -10,6 +10,7 @@ import { DeadCodeInjectionRule } from './normalizer-rules/DeadCodeInjectionRule'
 import { DeadCodeInjectionThresholdRule } from './normalizer-rules/DeadCodeInjectionThresholdRule';
 import { DomainLockRule } from './normalizer-rules/DomainLockRule';
 import { InputFileNameRule } from './normalizer-rules/InputFileNameRule';
+import { SeedRule } from './normalizer-rules/SeedRule';
 import { SelfDefendingRule } from './normalizer-rules/SelfDefendingRule';
 import { SourceMapBaseUrlRule } from './normalizer-rules/SourceMapBaseUrlRule';
 import { SourceMapFileNameRule } from './normalizer-rules/SourceMapFileNameRule';
@@ -29,6 +30,7 @@ export class OptionsNormalizer implements IOptionsNormalizer {
         DeadCodeInjectionThresholdRule,
         DomainLockRule,
         InputFileNameRule,
+        SeedRule,
         SelfDefendingRule,
         SourceMapBaseUrlRule,
         SourceMapFileNameRule,

+ 25 - 0
src/options/normalizer-rules/SeedRule.ts

@@ -0,0 +1,25 @@
+import { TOptionsNormalizerRule } from '../../types/options/TOptionsNormalizerRule';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+/**
+ * @param {IOptions} options
+ * @returns {IOptions}
+ */
+export const SeedRule: TOptionsNormalizerRule = (options: IOptions): IOptions => {
+    if (options.seed) {
+        return {
+            ...options,
+            seed: options.seed
+        };
+    }
+
+    const getRandomInteger: (min: number, max: number) => number = (min: number, max: number) => {
+        return Math.floor(Math.random() * (max - min + 1) + min);
+    };
+
+    return {
+        ...options,
+        seed: getRandomInteger(0, 999_999_999)
+    };
+};

+ 0 - 4
src/storages/string-array/StringArrayStorage.ts

@@ -50,10 +50,6 @@ export class StringArrayStorage extends ArrayStorage <string> {
     public initialize (): void {
         super.initialize();
 
-        if (!this.options.stringArray) {
-            return;
-        }
-
         const baseStringArrayName: string = this.identifierNamesGenerator
             .generate(StringArrayStorage.stringArrayNameLength);
         const baseStringArrayCallsWrapperName: string = this.identifierNamesGenerator

+ 1 - 1
src/templates/debug-protection-nodes/debug-protection-function-call-node/DebugProtectionFunctionCallTemplate.ts

@@ -6,7 +6,7 @@ export function DebugProtectionFunctionCallTemplate (): string {
         (function () {
             {singleNodeCallControllerFunctionName}(this, function () {
                 var regExp1 = new RegExp('function *\\\\( *\\\\)');
-                var regExp2 = new RegExp('\\\\+\\\\+ *\\(?:_0x(?:[a-f0-9]){4,6}|(?:\\\\b|\\\\d)[a-z0-9]{1,4}(?:\\\\b|\\\\d)\\)', 'i');
+                var regExp2 = new RegExp('\\\\+\\\\+ *\\(?:[a-zA-Z_$][0-9a-zA-Z_$]*\\)', 'i');
        
                 var result = {debugProtectionFunctionName}('init');
                 

+ 22 - 26
src/utils/RandomGenerator.ts

@@ -29,12 +29,6 @@ export class RandomGenerator implements IRandomGenerator, IInitializable {
     @initializable()
     private randomGenerator!: Chance.Chance;
 
-    /**
-     * @type {number}
-     */
-    @initializable()
-    private seed!: number;
-
     /**
      * @type {ISourceCode}
      */
@@ -54,24 +48,7 @@ export class RandomGenerator implements IRandomGenerator, IInitializable {
 
     @postConstruct()
     public initialize (): void {
-        const getRandomInteger: (min: number, max: number) => number = (min: number, max: number) => {
-            return Math.floor(Math.random() * (max - min + 1) + min);
-        };
-
-        /**
-         * We need to add numbers from md5 hash of source code to input seed to prevent same String Array name
-         * for different bundles with same seed
-         *
-         * @returns {number}
-         */
-        const getSeed: () => number = (): number => {
-            const md5Hash: string = md5(this.sourceCode.getSourceCode());
-
-            return this.seed + Number(md5Hash.replace(/\D/g, ''));
-        };
-
-        this.seed = this.options.seed !== 0 ? this.options.seed : getRandomInteger(0, 999_999_999);
-        this.randomGenerator = new Chance(getSeed());
+        this.randomGenerator = new Chance(this.getRawSeed());
     }
 
     /**
@@ -110,9 +87,28 @@ export class RandomGenerator implements IRandomGenerator, IInitializable {
     }
 
     /**
+     * @returns {string}
+     */
+    public getInputSeed (): string {
+        return this.options.seed.toString();
+    }
+
+    /**
+     * We need to add numbers from md5 hash of source code to input seed to prevent same String Array name
+     * for different bundles with same seed
+     *
      * @returns {number}
      */
-    public getSeed (): number {
-        return this.seed;
+    public getRawSeed (): string {
+        const inputSeed: string = this.getInputSeed();
+        const inputSeedParts: string[] = `${inputSeed}`.split('|');
+
+        if (inputSeedParts.length > 1) {
+            return inputSeed;
+        }
+
+        const sourceCodeMD5Hash: string = md5(this.sourceCode.getSourceCode());
+
+        return `${inputSeed}|${sourceCodeMD5Hash}`;
     }
 }

+ 38 - 7
test/dev/dev.ts

@@ -1,20 +1,51 @@
 'use strict';
-import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
+import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            var abc = 1;
-            var cde = 1;
-            var fg = 1;
-            var sss = 1;
+            // Paste your JavaScript code here
+            function hi() {
+              console.log("Hello World!");
+            }
+            hi();
         `,
         {
-            ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
-            renameGlobals: true
+            selfDefending: false,
+            disableConsoleOutput: false,
+            debugProtection: false,
+            debugProtectionInterval: false,
+            splitStrings: true,
+            splitStringsChunkLength: 5,
+            splitStringsChunkLengthEnabled: true,
+            stringArray: true,
+            rotateStringArray: false,
+            rotateStringArrayEnabled: true,
+            stringArrayThreshold: 1,
+            stringArrayThresholdEnabled: true,
+            stringArrayEncoding: false,
+            stringArrayEncodingEnabled: true,
+            sourceMap: false,
+            sourceMapBaseUrl: "",
+            sourceMapFileName: "",
+            sourceMapSeparate: false,
+            domainLock: [],
+            reservedNames: [],
+            reservedStrings: [],
+            seed: 0,
+            controlFlowFlatteningThreshold: 1,
+            controlFlowFlattening: true,
+            deadCodeInjectionThreshold: 1,
+            deadCodeInjection: true,
+            unicodeEscapeSequence: false,
+            renameGlobals: true,
+            identifierNamesGenerator: IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator,
+            identifiersDictionary: ["foo", "bar", "baz", "bark", "hawk", "fooz", "moscow", "chikago"],
+            identifiersPrefix: "",
+            transformObjectKeys: true
         }
     ).getObfuscatedCode();
 

+ 43 - 2
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -570,7 +570,7 @@ describe('JavaScriptObfuscator', () => {
         });
 
         describe('dictionary identifier names generator', () => {
-            const regExp: RegExp = /var *[ab] *= *0x1; *var *[ab] *= *0x2; *var *[AB] *= *0x3;/;
+            const regExp: RegExp = /var *[abc] *= *0x1; *var *[ABC] *= *0x2; *var *[ABC] *= *0x3;/;
 
             let obfuscatedCode: string;
 
@@ -582,7 +582,7 @@ describe('JavaScriptObfuscator', () => {
                     {
                         ...NO_ADDITIONAL_NODES_PRESET,
                         identifierNamesGenerator: IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator,
-                        identifiersDictionary: ['a', 'b']
+                        identifiersDictionary: ['a', 'b', 'c']
                     }
                 ).getObfuscatedCode();
             });
@@ -666,5 +666,46 @@ describe('JavaScriptObfuscator', () => {
                 assert.equal(result, expectedValue);
             });
         });
+
+        describe('Identifier names collision between base code and appended string array nodes', () => {
+            const samplesCount: number = 30;
+
+            let areCollisionsExists: boolean = false;
+            let obfuscateFunc: (identifierNamesGenerator: IdentifierNamesGenerator) => string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/custom-nodes-identifier-names-collision.js');
+
+                obfuscateFunc = (identifierNamesGenerator: IdentifierNamesGenerator) => JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        identifierNamesGenerator,
+                        compact: false,
+                        renameGlobals: true,
+                        identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'foozmos', 'cow', 'chikago'],
+                        stringArray: true
+                    }
+                ).getObfuscatedCode();
+
+
+                [
+                    IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator,
+                    IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                ].forEach((identifierNamesGenerator: IdentifierNamesGenerator) => {
+                    for (let i = 0; i < samplesCount; i++) {
+                        try {
+                            eval(obfuscateFunc(identifierNamesGenerator));
+                        } catch {
+                            areCollisionsExists = true;
+                            break;
+                        }
+                    }
+                });
+            });
+
+            it('It does not create identifier names collision', () => {
+                assert.equal(areCollisionsExists, false);
+            });
+        });
     });
 });

+ 4 - 0
test/functional-tests/javascript-obfuscator/fixtures/custom-nodes-identifier-names-collision.js

@@ -0,0 +1,4 @@
+function hi() {
+    console.log("Hello World!");
+}
+hi();

+ 2 - 2
test/functional-tests/node-transformers/obfuscating-transformers/class-declaration-transformer/ClassDeclarationTransformer.spec.ts

@@ -121,8 +121,8 @@ describe('ClassDeclarationTransformer', () => {
         });
 
         describe('Variant #3: already renamed identifiers shouldn\'t be renamed twice', () => {
-            const classDeclarationRegExp: RegExp = /class *b *{/;
-            const variableDeclarationsRegExp: RegExp = /let *c, *d, *e, *f;/;
+            const classDeclarationRegExp: RegExp = /class *d *{/;
+            const variableDeclarationsRegExp: RegExp = /let *e, *f, *g, *h;/;
 
             let obfuscatedCode: string;
 

+ 2 - 2
test/functional-tests/node-transformers/obfuscating-transformers/function-declaration-transformer/FunctionDeclarationTransformer.spec.ts

@@ -145,8 +145,8 @@ describe('FunctionDeclarationTransformer', () => {
 
         describe('Variant #5: already renamed identifiers shouldn\'t be renamed twice', () => {
             describe('Variant #1', () => {
-                const functionDeclarationRegExp: RegExp = /function *b\(\) *{/;
-                const variableDeclarationsRegExp: RegExp = /let *c, *d, *e, *f;/;
+                const functionDeclarationRegExp: RegExp = /function *d\(\) *{/;
+                const variableDeclarationsRegExp: RegExp = /let *e, *f, *g, *h;/;
 
                 let obfuscatedCode: string;
 

+ 9 - 9
test/functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/VariableDeclarationTransformer.spec.ts

@@ -426,11 +426,11 @@ describe('VariableDeclarationTransformer', () => {
 
     describe('Variant #13: already renamed identifiers shouldn\'t be renamed twice', () => {
         describe('Variant #1', () => {
-            const variableDeclarationRegExp: RegExp = /var *b *= *0x1;/;
-            const functionDeclarationRegExp1: RegExp = /function *c *\(\) *{}/;
-            const functionDeclarationRegExp2: RegExp = /function *d *\(\) *{}/;
-            const functionDeclarationRegExp3: RegExp = /function *e *\(\) *{}/;
-            const functionDeclarationRegExp4: RegExp = /function *f *\(\) *{}/;
+            const variableDeclarationRegExp: RegExp = /var *d *= *0x1;/;
+            const functionDeclarationRegExp1: RegExp = /function *e *\(\) *{}/;
+            const functionDeclarationRegExp2: RegExp = /function *f *\(\) *{}/;
+            const functionDeclarationRegExp3: RegExp = /function *g *\(\) *{}/;
+            const functionDeclarationRegExp4: RegExp = /function *h *\(\) *{}/;
 
             let obfuscatedCode: string;
 
@@ -468,10 +468,10 @@ describe('VariableDeclarationTransformer', () => {
         });
 
         describe('Variant #2', () => {
-            const variableDeclarationRegExp1: RegExp = /var *b *= *0x1;/;
-            const variableDeclarationRegExp2: RegExp = /var *c;/;
-            const functionDeclarationRegExp: RegExp = /function *d *\(\) *{/;
-            const variableDeclarationRegExp3: RegExp = /var *d *= *function *\(\) *{}/;
+            const variableDeclarationRegExp1: RegExp = /var *d *= *0x1;/;
+            const variableDeclarationRegExp2: RegExp = /var *e;/;
+            const functionDeclarationRegExp: RegExp = /function *f *\(\) *{/;
+            const variableDeclarationRegExp3: RegExp = /var *f *= *function *\(\) *{}/;
 
             let obfuscatedCode: string;
 

+ 99 - 36
test/functional-tests/options/OptionsNormalizer.spec.ts

@@ -33,6 +33,13 @@ function getNormalizedOptions (optionsPreset: TInputOptions): TInputOptions {
     return <TInputOptions>optionsNormalizer.normalize(options);
 }
 
+function getDefaultOptions(): TInputOptions {
+    return {
+        ...DEFAULT_PRESET,
+        seed: 1 // set `seed` to the fixed value, to prevent a new seed for the each case
+    };
+}
+
 describe('OptionsNormalizer', () => {
     describe('normalize', () => {
         let optionsPreset: TInputOptions,
@@ -41,13 +48,13 @@ describe('OptionsNormalizer', () => {
         describe('controlFlowFlatteningThresholdRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     controlFlowFlattening: true,
                     controlFlowFlatteningThreshold: 0
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     controlFlowFlattening: false,
                     controlFlowFlatteningThreshold: 0
                 };
@@ -61,7 +68,7 @@ describe('OptionsNormalizer', () => {
         describe('deadCodeInjectionRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     deadCodeInjection: true,
                     deadCodeInjectionThreshold: 0.4,
                     stringArray: false,
@@ -69,7 +76,7 @@ describe('OptionsNormalizer', () => {
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     deadCodeInjection: true,
                     deadCodeInjectionThreshold: 0.4,
                     stringArray: true,
@@ -86,7 +93,7 @@ describe('OptionsNormalizer', () => {
             describe('`stringArrayThreshold` option is empty', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         deadCodeInjection: true,
                         deadCodeInjectionThreshold: 0.4,
                         stringArray: false,
@@ -94,7 +101,7 @@ describe('OptionsNormalizer', () => {
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         deadCodeInjection: true,
                         deadCodeInjectionThreshold: 0.4,
                         stringArray: true,
@@ -110,7 +117,7 @@ describe('OptionsNormalizer', () => {
             describe('`stringArrayThreshold` option is not empty', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         deadCodeInjection: true,
                         deadCodeInjectionThreshold: 0.4,
                         stringArray: false,
@@ -118,7 +125,7 @@ describe('OptionsNormalizer', () => {
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         deadCodeInjection: true,
                         deadCodeInjectionThreshold: 0.4,
                         stringArray: true,
@@ -135,13 +142,13 @@ describe('OptionsNormalizer', () => {
         describe('deadCodeInjectionThresholdRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     deadCodeInjection: true,
                     deadCodeInjectionThreshold: 0
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     deadCodeInjection: false,
                     deadCodeInjectionThreshold: 0
                 };
@@ -155,7 +162,7 @@ describe('OptionsNormalizer', () => {
         describe('domainLockRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     domainLock: [
                         '//localhost:9000',
                         'https://google.ru/abc?cde=fgh'
@@ -163,7 +170,7 @@ describe('OptionsNormalizer', () => {
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     domainLock: [
                         'localhost',
                         'google.ru'
@@ -180,12 +187,12 @@ describe('OptionsNormalizer', () => {
             describe('Variant #1: extension isn\'t set', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: 'foo'
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: 'foo.js'
                     };
                 });
@@ -198,12 +205,12 @@ describe('OptionsNormalizer', () => {
             describe('Variant #2: extension is set', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: 'foo.js'
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: 'foo.js'
                     };
                 });
@@ -216,12 +223,12 @@ describe('OptionsNormalizer', () => {
             describe('Variant #3: extension in set with `.map` postfix', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: 'foo.map.js'
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: 'foo.map.js'
                     };
                 });
@@ -234,12 +241,12 @@ describe('OptionsNormalizer', () => {
             describe('Variant #4: no file name', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: ''
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         inputFileName: ''
                     };
                 });
@@ -250,16 +257,72 @@ describe('OptionsNormalizer', () => {
             });
         });
 
+        describe('seedRule', () => {
+            describe('Variant #1: seed value is string', () => {
+                before(() => {
+                    optionsPreset = getNormalizedOptions({
+                        ...getDefaultOptions(),
+                        seed: 'abc'
+                    });
+
+                    expectedOptionsPreset = {
+                        ...getDefaultOptions(),
+                        seed: 'abc'
+                    };
+                });
+
+                it('should not normalize options preset', () => {
+                    assert.deepEqual(optionsPreset, expectedOptionsPreset);
+                });
+            });
+
+            describe('Variant #2: seed value is number', () => {
+                before(() => {
+                    optionsPreset = getNormalizedOptions({
+                        ...getDefaultOptions(),
+                        seed: 123
+                    });
+
+                    expectedOptionsPreset = {
+                        ...getDefaultOptions(),
+                        seed: 123
+                    };
+                });
+
+                it('should normalize options preset', () => {
+                    assert.deepEqual(optionsPreset, expectedOptionsPreset);
+                });
+            });
+
+            describe('Variant #3: seed value is `0``', () => {
+                let seedValue: number;
+
+                before(() => {
+                    optionsPreset = getNormalizedOptions({
+                        ...getDefaultOptions(),
+                        seed: 0
+                    });
+
+                    seedValue = Number(optionsPreset.seed);
+                });
+
+                it('should normalize seed value', () => {
+                    assert.isAtLeast(seedValue, 0);
+                    assert.isBelow(seedValue, 999_999_999);
+                });
+            });
+        });
+
         describe('selfDefendingRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     selfDefending: true,
                     compact: false
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     selfDefending: true,
                     compact: true
                 };
@@ -274,12 +337,12 @@ describe('OptionsNormalizer', () => {
             describe('Variant #1: only source map base url', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         sourceMapBaseUrl: 'http://localhost:9000',
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         sourceMapBaseUrl: ''
                     };
                 });
@@ -292,13 +355,13 @@ describe('OptionsNormalizer', () => {
             describe('Variant #2: source map base url with source map file name', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         sourceMapBaseUrl: 'http://localhost:9000',
                         sourceMapFileName: '/outputSourceMapName.map'
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         sourceMapBaseUrl: 'http://localhost:9000/',
                         sourceMapFileName: 'outputSourceMapName.js.map'
                     };
@@ -313,13 +376,13 @@ describe('OptionsNormalizer', () => {
         describe('sourceMapFileNameRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     sourceMapBaseUrl: 'http://localhost:9000',
                     sourceMapFileName: '//outputSourceMapName'
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     sourceMapBaseUrl: 'http://localhost:9000/',
                     sourceMapFileName: 'outputSourceMapName.js.map'
                 };
@@ -334,13 +397,13 @@ describe('OptionsNormalizer', () => {
             describe('`splitStringsChunkLengthRule` value is float number', () => {
                 before(() => {
                     optionsPreset = getNormalizedOptions({
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         splitStrings: true,
                         splitStringsChunkLength: 5.6
                     });
 
                     expectedOptionsPreset = {
-                        ...DEFAULT_PRESET,
+                        ...getDefaultOptions(),
                         splitStrings: true,
                         splitStringsChunkLength: 5
                     };
@@ -355,7 +418,7 @@ describe('OptionsNormalizer', () => {
         describe('stringArrayRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     stringArray: false,
                     stringArrayEncoding: StringArrayEncoding.Rc4,
                     stringArrayThreshold: 0.5,
@@ -363,7 +426,7 @@ describe('OptionsNormalizer', () => {
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     stringArray: false,
                     stringArrayEncoding: false,
                     stringArrayThreshold: 0,
@@ -379,12 +442,12 @@ describe('OptionsNormalizer', () => {
         describe('stringArrayEncodingRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     stringArrayEncoding: true
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     stringArrayEncoding: StringArrayEncoding.Base64
                 };
             });
@@ -397,14 +460,14 @@ describe('OptionsNormalizer', () => {
         describe('stringArrayThresholdRule', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     rotateStringArray: true,
                     stringArray: true,
                     stringArrayThreshold: 0
                 });
 
                 expectedOptionsPreset = {
-                    ...DEFAULT_PRESET,
+                    ...getDefaultOptions(),
                     rotateStringArray: false,
                     stringArray: false,
                     stringArrayThreshold: 0

+ 45 - 7
test/functional-tests/templates/debug-protection-nodes/DebugProtectionFunctionCallTemplate.spec.ts

@@ -5,6 +5,7 @@ import { readFileAsString } from '../../../helpers/readFileAsString';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
 
+import { IdentifierNamesGenerator } from '../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { ObfuscationTarget } from '../../../../src/enums/ObfuscationTarget';
 
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
@@ -29,7 +30,7 @@ function spawnThread(inputCallback: Function, threadCallback: Function, timeoutC
 }
 
 describe('DebugProtectionFunctionCallTemplate', () => {
-    describe('Variant #1: correctly obfuscated code`', () => {
+    describe('Variant #1: correctly obfuscate code with `HexadecimalIdentifierNamesGenerator``', () => {
         const expectedEvaluationResult: number = 1;
 
         let obfuscatedCode: string,
@@ -42,7 +43,43 @@ describe('DebugProtectionFunctionCallTemplate', () => {
                 code,
                 {
                     ...NO_ADDITIONAL_NODES_PRESET,
-                    debugProtection: true
+                    debugProtection: true,
+                    identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator
+                }
+            ).getObfuscatedCode();
+
+            spawnThread(
+                () => obfuscatedCode,
+                (response: number) => {
+                    evaluationResult = response;
+                    done();
+                },
+                () => {
+                    done();
+                }
+            );
+        });
+
+        it('should correctly evaluate code with enabled debug protection', () => {
+            assert.equal(evaluationResult, expectedEvaluationResult);
+        });
+    });
+
+    describe('Variant #2: correctly obfuscate code with `MangledIdentifierNamesGenerator` option', () => {
+        const expectedEvaluationResult: number = 1;
+
+        let obfuscatedCode: string,
+            evaluationResult: number = 0;
+
+        beforeEach((done) => {
+            const code: string = readFileAsString(__dirname + '/fixtures/input.js');
+
+            obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    debugProtection: true,
+                    identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
                 }
             ).getObfuscatedCode();
 
@@ -63,7 +100,7 @@ describe('DebugProtectionFunctionCallTemplate', () => {
         });
     });
 
-    describe('Variant #2: correctly obfuscated code with enabled `mangle` option', () => {
+    describe('Variant #3: correctly obfuscate code with `DictionaryIdentifierNamesGenerator` option', () => {
         const expectedEvaluationResult: number = 1;
 
         let obfuscatedCode: string,
@@ -77,7 +114,8 @@ describe('DebugProtectionFunctionCallTemplate', () => {
                 {
                     ...NO_ADDITIONAL_NODES_PRESET,
                     debugProtection: true,
-                    mangle: true
+                    identifierNamesGenerator: IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator,
+                    identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'eagle']
                 }
             ).getObfuscatedCode();
 
@@ -98,7 +136,7 @@ describe('DebugProtectionFunctionCallTemplate', () => {
         });
     });
 
-    describe('Variant #3: correctly obfuscated code with target `extension`', () => {
+    describe('Variant #4: correctly obfuscated code with target `BrowserNoEval`', () => {
         const expectedEvaluationResult: number = 1;
 
         let obfuscatedCode: string,
@@ -133,7 +171,7 @@ describe('DebugProtectionFunctionCallTemplate', () => {
         });
     });
 
-    describe('Variant #4: obfuscated code with removed debug protection code', () => {
+    describe('Variant #5: obfuscated code with removed debug protection code', () => {
         const expectedEvaluationResult: number = 0;
 
         let obfuscatedCode: string,
@@ -168,7 +206,7 @@ describe('DebugProtectionFunctionCallTemplate', () => {
         });
     });
 
-    describe('Variant #5: single call of debug protection code', () => {
+    describe('Variant #6: single call of debug protection code', () => {
         const expectedEvaluationResult: number = 1;
 
         let obfuscatedCode: string,

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio