Преглед на файлове

Fixed conflicts between generated names and names from untouched identifiers from source code.
Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/550
Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/549

sanex3339 преди 5 години
родител
ревизия
db908f7bce
променени са 25 файла, в които са добавени 364 реда и са изтрити 70 реда
  1. 5 0
      CHANGELOG.md
  2. 0 0
      dist/index.browser.js
  3. 0 0
      dist/index.cli.js
  4. 0 0
      dist/index.js
  5. 1 1
      package.json
  6. 43 2
      src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts
  7. 21 0
      src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts
  8. 11 0
      src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts
  9. 19 0
      src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts
  10. 20 2
      src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts
  11. 3 2
      src/interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IIdentifierObfuscatingReplacer.ts
  12. 6 5
      src/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.ts
  13. 43 12
      src/node-transformers/preparing-transformers/VariablePreserveTransformer.ts
  14. 14 12
      src/storages/string-array/StringArrayStorage.ts
  15. 6 6
      test/dev/dev.ts
  16. 22 13
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  17. 3 0
      test/functional-tests/javascript-obfuscator/fixtures/dictionary-identifiers.js
  18. 1 1
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec.ts
  19. 3 3
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/ClassDeclaration.spec.ts
  20. 2 2
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/function-declaration/FunctionDeclaration.spec.ts
  21. 9 9
      test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/variable-declaration/VariableDeclaration.spec.ts
  22. 129 0
      test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/VariablePreserveTransformer.spec.ts
  23. 1 0
      test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/fixtures/string-array-storage-identifier-name-1.js
  24. 1 0
      test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/fixtures/transform-object-keys-identifier-name-1.js
  25. 1 0
      test/index.spec.ts

+ 5 - 0
CHANGELOG.md

@@ -1,5 +1,10 @@
 Change Log
 
+v0.25.0
+---
+* Fixed conflicts between generated names and names from untouched identifiers from source code. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/550. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/549
+
+
 v0.24.6
 ---
 * Fixed support of exponentiation operator. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/534

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
dist/index.browser.js


Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
dist/index.cli.js


Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

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

+ 43 - 2
src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts

@@ -1,6 +1,8 @@
 import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
+import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
+
 import { IIdentifierNamesGenerator } from '../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
@@ -22,6 +24,11 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
      */
     protected readonly preservedNamesSet: Set<string> = new Set();
 
+    /**
+     * @type {Map<TNodeWithLexicalScope, Set<string>>}
+     */
+    protected readonly lexicalScopesPreservedNamesMap: Map<TNodeWithLexicalScope, Set<string>> = new Map();
+
     /**
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
@@ -36,10 +43,17 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
 
     /**
      * @param {string} name
-     * @returns {void}
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
      */
-    public preserveName (name: string): void {
+    public preserveName (name: string, lexicalScopeNode: TNodeWithLexicalScope): void {
         this.preservedNamesSet.add(name);
+
+        const preservedNamesForLexicalScopeSet: Set<string> =
+            this.lexicalScopesPreservedNamesMap.get(lexicalScopeNode) ?? new Set();
+
+        preservedNamesForLexicalScopeSet.add(name);
+
+        this.lexicalScopesPreservedNamesMap.set(lexicalScopeNode, preservedNamesForLexicalScopeSet);
     }
 
     /**
@@ -50,6 +64,26 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
         return this.notReservedName(name) && !this.preservedNamesSet.has(name);
     }
 
+    /**
+     * @param {string} name
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @returns {boolean}
+     */
+    public isValidIdentifierNameInLexicalScope (name: string, lexicalScopeNode: TNodeWithLexicalScope): boolean {
+        if (!this.notReservedName(name)) {
+            return false;
+        }
+
+        const preservedNamesForLexicalScopeSet: Set<string> | null =
+            this.lexicalScopesPreservedNamesMap.get(lexicalScopeNode) ?? null;
+
+        if (!preservedNamesForLexicalScopeSet) {
+            return true;
+        }
+
+        return !preservedNamesForLexicalScopeSet.has(name);
+    }
+
     /**
      * @param {string} name
      * @returns {boolean}
@@ -69,6 +103,13 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
      */
     public abstract generate (nameLength?: number): string;
 
+    /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @param {number} nameLength
+     * @returns {string}
+     */
+    public abstract generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string;
+
     /**
      * @param {number} nameLength
      * @returns {string}

+ 21 - 0
src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts

@@ -6,6 +6,7 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
 import { AbstractIdentifierNamesGenerator } from './AbstractIdentifierNamesGenerator';
+import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
 
 @injectable()
 export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesGenerator {
@@ -75,6 +76,12 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG
         const iteratorResult: IteratorResult<string> = this.identifiersIterator.next();
 
         if (!iteratorResult.done) {
+            const identifierName: string =iteratorResult.value;
+
+            if (!this.isValidIdentifierName(identifierName)) {
+                return this.generate();
+            }
+
             return iteratorResult.value;
         }
 
@@ -84,6 +91,20 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG
         return this.generate();
     }
 
+    /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @returns {string}
+     */
+    public generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope): string {
+        const identifierName: string = this.generate();
+
+        if (!this.isValidIdentifierNameInLexicalScope(identifierName, lexicalScopeNode)) {
+            return this.generateForLexicalScope(lexicalScopeNode);
+        }
+
+        return identifierName;
+    }
+
     /**
      * @returns {string}
      */

+ 11 - 0
src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts

@@ -1,6 +1,8 @@
 import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
+import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
+
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
@@ -56,6 +58,15 @@ export class HexadecimalIdentifierNamesGenerator extends AbstractIdentifierNames
         return identifierName;
     }
 
+    /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @param {number} nameLength
+     * @returns {string}
+     */
+    public generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string {
+        return this.generate(nameLength);
+    }
+
     /**
      * @param {number} nameLength
      * @returns {string}

+ 19 - 0
src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts

@@ -1,6 +1,8 @@
 import { inject, injectable } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
+import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
+
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
@@ -58,6 +60,23 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
         return identifierName;
     }
 
+    /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @param {number} nameLength
+     * @returns {string}
+     */
+    public generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string {
+        const identifierName: string = this.generateNewMangledName(this.previousMangledName);
+
+        if (!this.isValidIdentifierNameInLexicalScope(identifierName, lexicalScopeNode)) {
+            return this.generateForLexicalScope(lexicalScopeNode, nameLength);
+        }
+
+        this.previousMangledName = identifierName;
+
+        return identifierName;
+    }
+
     /**
      * @param {number} nameLength
      * @returns {string}

+ 20 - 2
src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts

@@ -1,3 +1,5 @@
+import { TNodeWithLexicalScope } from '../../../types/node/TNodeWithLexicalScope';
+
 export interface IIdentifierNamesGenerator {
     /**
      * @param {number} nameLength
@@ -5,6 +7,14 @@ export interface IIdentifierNamesGenerator {
      */
     generate (nameLength?: number): string;
 
+    /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @param {number} nameLength
+     * @returns {string}
+     */
+    generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string;
+
+
     /**
      * @param {number} nameLength
      * @returns {string}
@@ -18,7 +28,15 @@ export interface IIdentifierNamesGenerator {
     isValidIdentifierName (identifierName: string): boolean;
 
     /**
-     * @param {string} name
+     * @param {string} identifierName
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     * @returns {boolean}
+     */
+    isValidIdentifierNameInLexicalScope (identifierName: string, lexicalScopeNode: TNodeWithLexicalScope): boolean;
+
+    /**
+     * @param {string} identifierName
+     * @param {TNodeWithLexicalScope} lexicalScope
      */
-    preserveName (name: string): void;
+    preserveName (identifierName: string, lexicalScope: TNodeWithLexicalScope): void;
 }

+ 3 - 2
src/interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IIdentifierObfuscatingReplacer.ts

@@ -18,7 +18,8 @@ export interface IIdentifierObfuscatingReplacer extends IObfuscatingReplacer <ES
     storeLocalName (identifierNode: ESTree.Identifier, lexicalScopeNode: TNodeWithLexicalScope): void;
 
     /**
-     * @param {string} name
+     * @param {Identifier} identifierNode
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
      */
-    preserveName (name: string): void;
+    preserveName (identifierNode: ESTree.Identifier, lexicalScopeNode: TNodeWithLexicalScope): void;
 }

+ 6 - 5
src/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.ts

@@ -97,7 +97,7 @@ export class BaseIdentifierObfuscatingReplacer extends AbstractObfuscatingReplac
             return;
         }
 
-        const newIdentifierName: string = this.identifierNamesGenerator.generate();
+        const newIdentifierName: string = this.identifierNamesGenerator.generateForLexicalScope(lexicalScopeNode);
 
         if (!this.blockScopesMap.has(lexicalScopeNode)) {
             this.blockScopesMap.set(lexicalScopeNode, new Map());
@@ -109,12 +109,13 @@ export class BaseIdentifierObfuscatingReplacer extends AbstractObfuscatingReplac
     }
 
     /**
-     * Preserve `name` to protect it from further using.
+     * Preserve `name` to protect it from further using
      *
-     * @param {string} name
+     * @param {Identifier} identifierNode
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
      */
-    public preserveName (name: string): void {
-        this.identifierNamesGenerator.preserveName(name);
+    public preserveName (identifierNode: ESTree.Identifier, lexicalScopeNode: TNodeWithLexicalScope): void {
+        this.identifierNamesGenerator.preserveName(identifierNode.name, lexicalScopeNode);
     }
 
     /**

+ 43 - 12
src/node-transformers/preparing-transformers/VariablePreserveTransformer.ts

@@ -2,6 +2,7 @@ import { inject, injectable, } from 'inversify';
 import * as ESTree from 'estree';
 
 import { TIdentifierObfuscatingReplacerFactory } from '../../types/container/node-transformers/TIdentifierObfuscatingReplacerFactory';
+import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
 
 import { IIdentifierObfuscatingReplacer } from '../../interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IIdentifierObfuscatingReplacer';
 import { IOptions } from '../../interfaces/options/IOptions';
@@ -20,6 +21,11 @@ import { NodeGuards } from '../../node/NodeGuards';
  */
 @injectable()
 export class VariablePreserveTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {TNodeWithLexicalScope[]}
+     */
+    private readonly enteredLexicalScopesStack: TNodeWithLexicalScope[] = [];
+
     /**
      * @type {IIdentifierObfuscatingReplacer}
      */
@@ -52,18 +58,23 @@ export class VariablePreserveTransformer extends AbstractNodeTransformer {
             case TransformationStage.Preparing:
                 return {
                     enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
+                        if (NodeGuards.isNodeWithLexicalScope(node)) {
+                            this.addLexicalScopeToEnteredLexicalScopesStack(node);
+                        }
+
                         if (
                             NodeGuards.isIdentifierNode(node)
                             && parentNode
-                            && (
-                                NodeGuards.parentNodeIsPropertyNode(node, parentNode)
-                                || NodeGuards.parentNodeIsMemberExpressionNode(node, parentNode)
-                                || NodeGuards.parentNodeIsMethodDefinitionNode(node, parentNode)
-                                || NodeGuards.isLabelIdentifierNode(node, parentNode)
-                            )
                         ) {
+                            this.preserveIdentifierNameForLexicalScope(node);
+
                             return this.transformNode(node, parentNode);
                         }
+                    },
+                    leave: (node: ESTree.Node): void => {
+                        if (NodeGuards.isNodeWithLexicalScope(node)) {
+                            this.removeLexicalScopeFromEnteredLexicalScopesStack(node);
+                        }
                     }
                 };
 
@@ -73,14 +84,34 @@ export class VariablePreserveTransformer extends AbstractNodeTransformer {
     }
 
     /**
-     * @param {Identifier} node
-     * @param {NodeGuards} parentNode
-     * @returns {NodeGuards}
+     * @param {Identifier} identifierNode
+     * @param {Node} parentNode
+     * @returns {Node}
+     */
+    public transformNode (identifierNode: ESTree.Identifier, parentNode: ESTree.Node): ESTree.Node {
+        return identifierNode;
+    }
+
+    /**
+     * @param {Identifier} identifierNode
      */
-    public transformNode (node: ESTree.Identifier, parentNode: ESTree.Node): ESTree.Node {
-        this.identifierObfuscatingReplacer.preserveName(node.name);
+    private preserveIdentifierNameForLexicalScope (identifierNode: ESTree.Identifier): void {
+        for (const lexicalScope of this.enteredLexicalScopesStack) {
+            this.identifierObfuscatingReplacer.preserveName(identifierNode, lexicalScope);
+        }
+    }
 
-        return node;
+    /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     */
+    private addLexicalScopeToEnteredLexicalScopesStack (lexicalScopeNode: TNodeWithLexicalScope): void {
+        this.enteredLexicalScopesStack.push(lexicalScopeNode);
     }
 
+    /**
+     * @param {TNodeWithLexicalScope} lexicalScopeNode
+     */
+    private removeLexicalScopeFromEnteredLexicalScopesStack (lexicalScopeNode: TNodeWithLexicalScope): void {
+        this.enteredLexicalScopesStack.pop();
+    }
 }

+ 14 - 12
src/storages/string-array/StringArrayStorage.ts

@@ -13,8 +13,6 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-storage/IStringArrayStorage';
 import { IStringArrayStorageItemData } from '../../interfaces/storages/string-array-storage/IStringArrayStorageItem';
 
-import { initializable } from '../../decorators/Initializable';
-
 import { StringArrayEncoding } from '../../enums/StringArrayEncoding';
 
 import { MapStorage } from '../MapStorage';
@@ -84,13 +82,11 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
     /**
      * @type {string}
      */
-    @initializable()
     private stringArrayStorageName!: string;
 
     /**
      * @type {string}
      */
-    @initializable()
     private stringArrayStorageCallsWrapperName!: string;
 
     /**
@@ -130,14 +126,6 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
     public initialize (): void {
         super.initialize();
 
-        const baseStringArrayName: string = this.identifierNamesGenerator
-            .generate(StringArrayStorage.stringArrayNameLength);
-        const baseStringArrayCallsWrapperName: string = this.identifierNamesGenerator
-            .generate(StringArrayStorage.stringArrayNameLength);
-
-        this.stringArrayStorageName = `${this.options.identifiersPrefix}${baseStringArrayName}`;
-        this.stringArrayStorageCallsWrapperName = `${this.options.identifiersPrefix}${baseStringArrayCallsWrapperName}`;
-
         this.rotationAmount = this.options.rotateStringArray
             ? this.randomGenerator.getRandomInteger(
                 StringArrayStorage.minimumRotationAmount,
@@ -165,6 +153,13 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
      * @returns {string}
      */
     public getStorageId (): string {
+        if (!this.stringArrayStorageName) {
+            const baseStringArrayName: string = this.identifierNamesGenerator
+                .generate(StringArrayStorage.stringArrayNameLength);
+
+            this.stringArrayStorageName = `${this.options.identifiersPrefix}${baseStringArrayName}`;
+        }
+
         return this.stringArrayStorageName;
     }
 
@@ -179,6 +174,13 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
      * @returns {string}
      */
     public getStorageCallsWrapperName (): string {
+        if (!this.stringArrayStorageCallsWrapperName) {
+            const baseStringArrayCallsWrapperName: string = this.identifierNamesGenerator
+                .generate(StringArrayStorage.stringArrayNameLength);
+
+            this.stringArrayStorageCallsWrapperName = `${this.options.identifiersPrefix}${baseStringArrayCallsWrapperName}`;
+        }
+
         return this.stringArrayStorageCallsWrapperName;
     }
 

+ 6 - 6
test/dev/dev.ts

@@ -7,15 +7,15 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            function foo({bar = ''}) {
-                return bar;
-            }
-            
-            console.log(foo({bar: 1}));
+            const sdasdas = '123';
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            compact: false
+            compact: false,
+            identifierNamesGenerator: 'dictionary',
+            identifiersDictionary: ['a', 'b', 'c', 'd', 'e'],
+            stringArray: true,
+            stringArrayThreshold: 1
         }
     ).getObfuscatedCode();
 

+ 22 - 13
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -618,7 +618,8 @@ describe('JavaScriptObfuscator', () => {
         });
 
         describe('dictionary identifier names generator', () => {
-            const regExp: RegExp = /var [abc] *= *0x1; *var [ABC] *= *0x2; *var [ABC] *= *0x3;/;
+            const regExp1: RegExp = /var [abc] *= *0x1; *var [abc] *= *0x2; *var [abc] *= *0x3;/;
+            const regExp2: RegExp = /var [ABC] *= *0x4; *var [ABC] *= *0x5; *var [ABC] *= *0x6;/;
 
             let obfuscatedCode: string;
 
@@ -635,8 +636,12 @@ describe('JavaScriptObfuscator', () => {
                 ).getObfuscatedCode();
             });
 
-            it('should generate identifier based on the dictionary', () => {
-                assert.match(obfuscatedCode, regExp);
+            it('Match #1: should generate identifier based on the dictionary', () => {
+                assert.match(obfuscatedCode, regExp1);
+            });
+
+            it('Match #2: should generate identifier based on the dictionary', () => {
+                assert.match(obfuscatedCode, regExp2);
             });
         });
 
@@ -726,16 +731,20 @@ describe('JavaScriptObfuscator', () => {
             before(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/custom-nodes-identifier-names-collision.js');
 
-                obfuscateFunc = (identifierNamesGenerator: TypeFromEnum<typeof IdentifierNamesGenerator>) => JavaScriptObfuscator.obfuscate(
-                    code,
-                    {
-                        identifierNamesGenerator,
-                        compact: false,
-                        renameGlobals: true,
-                        identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'foozmos', 'cow', 'chikago'],
-                        stringArray: true
-                    }
-                ).getObfuscatedCode();
+                obfuscateFunc = (identifierNamesGenerator: TypeFromEnum<typeof IdentifierNamesGenerator>) => {
+                    const obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            identifierNamesGenerator,
+                            compact: false,
+                            renameGlobals: true,
+                            identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'foozmos', 'cow', 'chikago'],
+                            stringArray: true
+                        }
+                    ).getObfuscatedCode();
+
+                    return obfuscatedCode;
+                };
 
 
                 [

+ 3 - 0
test/functional-tests/javascript-obfuscator/fixtures/dictionary-identifiers.js

@@ -2,4 +2,7 @@
     var test1 = 1;
     var test2 = 2;
     var test3 = 3;
+    var test4 = 4;
+    var test5 = 5;
+    var test6 = 6;
 })();

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

@@ -324,7 +324,7 @@ describe('LiteralTransformer', () => {
         });
 
         describe('Variant #11: string array calls wrapper name', () => {
-            const regExp: RegExp = /console\[b\('0x0'\)]\('a'\);/;
+            const regExp: RegExp = /console\[c\('0x0'\)]\('a'\);/;
 
             let obfuscatedCode: string;
 

+ 3 - 3
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/ClassDeclaration.spec.ts

@@ -483,9 +483,9 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
         });
 
         describe('Variant #3: already renamed identifiers shouldn\'t be renamed twice', () => {
-            const classDeclarationRegExp: RegExp = /class *d *{/;
-            const variableDeclarationsRegExp: RegExp = /let e, *f, *g, *h;/;
-            const classReferenceRegExp: RegExp = /new d\(\);/;
+            const classDeclarationRegExp: RegExp = /class *f *{/;
+            const variableDeclarationsRegExp: RegExp = /let g, *h, *i, *j;/;
+            const classReferenceRegExp: RegExp = /new f\(\);/;
 
             let obfuscatedCode: string;
 

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

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

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

@@ -456,11 +456,11 @@ describe('ScopeIdentifiersTransformer VariableDeclaration identifiers', () => {
 
     describe('Variant #13: already renamed identifiers shouldn\'t be renamed twice', () => {
         describe('Variant #1', () => {
-            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 *\(\) *{}/;
+            const variableDeclarationRegExp: RegExp = /var f *= *0x1;/;
+            const functionDeclarationRegExp1: RegExp = /function *g *\(\) *{}/;
+            const functionDeclarationRegExp2: RegExp = /function *h *\(\) *{}/;
+            const functionDeclarationRegExp3: RegExp = /function *i *\(\) *{}/;
+            const functionDeclarationRegExp4: RegExp = /function *j *\(\) *{}/;
 
             let obfuscatedCode: string;
 
@@ -498,10 +498,10 @@ describe('ScopeIdentifiersTransformer VariableDeclaration identifiers', () => {
         });
 
         describe('Variant #2', () => {
-            const variableDeclarationRegExp1: RegExp = /var d *= *0x1;/;
-            const variableDeclarationRegExp2: RegExp = /var e;/;
-            const functionDeclarationRegExp: RegExp = /function *f *\(\) *{/;
-            const variableDeclarationRegExp3: RegExp = /var g *= *function *\(\) *{}/;
+            const variableDeclarationRegExp1: RegExp = /var b *= *0x1;/;
+            const variableDeclarationRegExp2: RegExp = /var c;/;
+            const functionDeclarationRegExp: RegExp = /function *d *\(\) *{/;
+            const variableDeclarationRegExp3: RegExp = /var f *= *function *\(\) *{}/;
 
             let obfuscatedCode: string;
 

+ 129 - 0
test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/VariablePreserveTransformer.spec.ts

@@ -0,0 +1,129 @@
+import { assert } from 'chai';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
+
+import { readFileAsString } from '../../../../helpers/readFileAsString';
+
+import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+
+describe('VariablePreserveTransformer', () => {
+    describe('Variant #1: string array storage name conflicts with identifier name', () => {
+        describe('Variant #1: `renameGlobals` option is disabled', () => {
+            const stringArrayStorageNameRegExp: RegExp = /const b *= *\['abc'];/;
+            const identifierNameRegExp: RegExp = /const a *= *c\('0x0'\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/string-array-storage-identifier-name-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        identifierNamesGenerator: 'mangled',
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should generate non-preserved name for string array storage', () => {
+                assert.match(obfuscatedCode, stringArrayStorageNameRegExp);
+            });
+
+            it('should keep the original name for global identifier', () => {
+                assert.match(obfuscatedCode, identifierNameRegExp);
+            });
+        });
+
+        describe('Variant #2: `renameGlobals` option is enabled', () => {
+            const stringArrayStorageNameRegExp: RegExp = /const b *= *\['abc'];/;
+            const identifierNameRegExp: RegExp = /const d *= *c\('0x0'\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/string-array-storage-identifier-name-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        identifierNamesGenerator: 'mangled',
+                        renameGlobals: true,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should generate non-preserved name for string array storage', () => {
+                assert.match(obfuscatedCode, stringArrayStorageNameRegExp);
+            });
+
+            it('should rename global identifier', () => {
+                assert.match(obfuscatedCode, identifierNameRegExp);
+            });
+        });
+    });
+
+    describe('Variant #2: `transformObjectKeys` identifier name conflicts with identifier name', () => {
+        describe('Variant #1: `renameGlobals` option is disabled', () => {
+            const transformObjectKeysNameRegExp: RegExp = /const b *= *{};/;
+            const identifierNameRegExp: RegExp = /const a *= *b;/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/transform-object-keys-identifier-name-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        identifierNamesGenerator: 'mangled',
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should generate non-preserved name for `transformObjectKeys` identifier', () => {
+                assert.match(obfuscatedCode, transformObjectKeysNameRegExp);
+            });
+
+            it('should keep the original name for global identifier', () => {
+                assert.match(obfuscatedCode, identifierNameRegExp);
+            });
+        });
+
+        describe('Variant #2: `renameGlobals` option is enabled', () => {
+            const transformObjectKeysNameRegExp: RegExp = /const c *= *{};/;
+            const identifierNameRegExp: RegExp = /const d *= *c;/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/transform-object-keys-identifier-name-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        identifierNamesGenerator: 'mangled',
+                        renameGlobals: true,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should generate non-preserved name for `transformObjectKeys` identifier', () => {
+                assert.match(obfuscatedCode, transformObjectKeysNameRegExp);
+            });
+
+            it('should keep the original name for global identifier', () => {
+                assert.match(obfuscatedCode, identifierNameRegExp);
+            });
+        });
+    });
+});

+ 1 - 0
test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/fixtures/string-array-storage-identifier-name-1.js

@@ -0,0 +1 @@
+const a = 'abc';

+ 1 - 0
test/functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/fixtures/transform-object-keys-identifier-name-1.js

@@ -0,0 +1 @@
+const a = { x: 1 };

+ 1 - 0
test/index.spec.ts

@@ -92,6 +92,7 @@ import './functional-tests/node-transformers/preparing-transformers/comments-tra
 import './functional-tests/node-transformers/preparing-transformers/obfuscating-guards/black-list-obfuscating-guard/BlackListObfuscatingGuard.spec';
 import './functional-tests/node-transformers/preparing-transformers/obfuscating-guards/conditional-comment-obfuscating-guard/ConditionalCommentObfuscatingGuard.spec';
 import './functional-tests/node-transformers/preparing-transformers/obfuscating-guards/reserved-string-obfuscating-guard/ReservedStringObfuscatingGuard.spec';
+import './functional-tests/node-transformers/preparing-transformers/variable-preserve-transformer/VariablePreserveTransformer.spec';
 import './functional-tests/options/OptionsNormalizer.spec';
 import './functional-tests/options/domain-lock/Validation.spec';
 import './functional-tests/storages/string-array-storage/StringArrayStorage.spec';

Някои файлове не бяха показани, защото твърде много файлове са промени