Bladeren bron

`shuffleStringArray` option

sanex3339 5 jaren geleden
bovenliggende
commit
1edf96c0a0
28 gewijzigde bestanden met toevoegingen van 388 en 84 verwijderingen
  1. 2 0
      CHANGELOG.md
  2. 12 0
      README.md
  3. 0 0
      dist/index.browser.js
  4. 0 0
      dist/index.cli.js
  5. 0 0
      dist/index.js
  6. 5 6
      src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts
  7. 5 1
      src/cli/JavaScriptObfuscatorCLI.ts
  8. 2 2
      src/container/modules/storages/StoragesModule.ts
  9. 6 25
      src/custom-nodes/string-array-nodes/StringArrayNode.ts
  10. 5 5
      src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts
  11. 11 19
      src/custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup.ts
  12. 1 0
      src/interfaces/options/IOptions.d.ts
  13. 24 0
      src/interfaces/storages/string-array-storage/IStringArrayStorage.d.ts
  14. 29 9
      src/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.ts
  15. 6 0
      src/options/Options.ts
  16. 1 0
      src/options/normalizer-rules/StringArrayRule.ts
  17. 1 0
      src/options/presets/Default.ts
  18. 1 0
      src/options/presets/NoCustomNodes.ts
  19. 82 9
      src/storages/string-array/StringArrayStorage.ts
  20. 1 1
      src/templates/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate.ts
  21. 0 5
      src/types/storages/TStringArrayStorage.d.ts
  22. 5 1
      test/dev/dev.ts
  23. 2 1
      test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts
  24. 4 0
      test/functional-tests/options/OptionsNormalizer.spec.ts
  25. 3 0
      test/index.spec.ts
  26. 60 0
      test/unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/BooleanLiteralObfuscatingReplacer.spec.ts
  27. 60 0
      test/unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/NumberLiteralObfuscatingReplacer.spec.ts
  28. 60 0
      test/unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.spec.ts

+ 2 - 0
CHANGELOG.md

@@ -2,7 +2,9 @@ Change Log
 
 v0.23.0
 ---
+* **New option:** `shuffleStringArray` randomly shuffles string array items
 * **Internal change:** switched AST parser from `espree` on `acorn`
+* **Internal refactoring:** refactoring of string array storage and related things
 
 v0.22.1
 ---

+ 12 - 0
README.md

@@ -306,6 +306,7 @@ Following options are available for the JS Obfuscator:
     rotateStringArray: true,
     seed: 0,
     selfDefending: false,
+    shuffleStringArray: true,
     sourceMap: false,
     sourceMapBaseUrl: '',
     sourceMapFileName: '',
@@ -349,6 +350,7 @@ Following options are available for the JS Obfuscator:
     --rotate-string-array <boolean>
     --seed <string|number>
     --self-defending <boolean>
+    --shuffle-string-array <boolean>
     --source-map <boolean>
     --source-map-base-url <string>
     --source-map-file-name <string>
@@ -691,6 +693,13 @@ Type: `boolean` Default: `false`
 
 This option makes the output code resilient against formatting and variable renaming. If one tries to use a JavaScript beautifier on the obfuscated code, the code won't work anymore, making it harder to understand and modify it.
 
+### `shuffleStringArray`
+Type: `boolean` Default: `true`
+
+##### :warning: [`stringArray`](#stringarray) must be enabled
+
+Randomly shuffles the `stringArray` array items.
+
 ### `sourceMap`
 Type: `boolean` Default: `false`
 
@@ -867,6 +876,7 @@ Performance will 50-100% slower than without obfuscation
     renameGlobals: false,
     rotateStringArray: true,
     selfDefending: true,
+    shuffleStringArray: true,
     splitStrings: true,
     splitStringsChunkLength: '5',
     stringArray: true,
@@ -896,6 +906,7 @@ Performance will 30-35% slower than without obfuscation
     renameGlobals: false,
     rotateStringArray: true,
     selfDefending: true,
+    shuffleStringArray: true,
     splitStrings: true,
     splitStringsChunkLength: '10',
     stringArray: true,
@@ -923,6 +934,7 @@ Performance will slightly slower than without obfuscation
     renameGlobals: false,
     rotateStringArray: true,
     selfDefending: true,
+    shuffleStringArray: true,
     splitStrings: false,
     stringArray: true,
     stringArrayEncoding: false,

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


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


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


+ 5 - 6
src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts

@@ -4,10 +4,9 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TStringArrayStorage } from '../../types/storages/TStringArrayStorage';
-
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayStorage } from '../../interfaces/storages/string-array-storage/IStringArrayStorage';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
 import { IStringArrayStorageItemData } from '../../interfaces/storages/string-array-storage/IStringArrayStorageItem';
 
@@ -35,9 +34,9 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
     private readonly randomGenerator: IRandomGenerator;
 
     /**
-     * @type {TStringArrayStorage}
+     * @type {IStringArrayStorage}
      */
-    private readonly stringArrayStorage: TStringArrayStorage;
+    private readonly stringArrayStorage: IStringArrayStorage;
 
     /**
      * @type {Map<ESTree.Literal, IStringArrayStorageItemData>}
@@ -45,12 +44,12 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
     private readonly stringArrayStorageData: Map<ESTree.Literal, IStringArrayStorageItemData> = new Map();
 
     /**
-     * @param {TStringArrayStorage} stringArrayStorage
+     * @param {IStringArrayStorage} stringArrayStorage
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
-        @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: TStringArrayStorage,
+        @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: IStringArrayStorage,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions,
     ) {

+ 5 - 1
src/cli/JavaScriptObfuscatorCLI.ts

@@ -283,7 +283,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 BooleanSanitizer
             )
             .option(
-                '--rotate-string-array <boolean>', 'Disable rotation of unicode array values during obfuscation',
+                '--rotate-string-array <boolean>', 'Enable rotation of string array values during obfuscation',
                 BooleanSanitizer
             )
             .option(
@@ -296,6 +296,10 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 'Disables self-defending for obfuscated code',
                 BooleanSanitizer
             )
+            .option(
+                '--shuffle-string-array <boolean>', 'Randomly shuffles string array items',
+                BooleanSanitizer
+            )
             .option(
                 '--source-map <boolean>',
                 'Enables source map generation',

+ 2 - 2
src/container/modules/storages/StoragesModule.ts

@@ -3,10 +3,10 @@ import { ServiceIdentifiers } from '../../ServiceIdentifiers';
 
 import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
 import { TCustomNodeGroupStorage } from '../../../types/storages/TCustomNodeGroupStorage';
-import { TStringArrayStorage } from '../../../types/storages/TStringArrayStorage';
 
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
+import { IStringArrayStorage } from '../../../interfaces/storages/string-array-storage/IStringArrayStorage';
 
 import { ControlFlowStorage } from '../../../storages/control-flow/ControlFlowStorage';
 import { CustomNodeGroupStorage } from '../../../storages/custom-node-group/CustomNodeGroupStorage';
@@ -18,7 +18,7 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
         .to(CustomNodeGroupStorage)
         .inSingletonScope();
 
-    bind<TStringArrayStorage>(ServiceIdentifiers.TStringArrayStorage)
+    bind<IStringArrayStorage>(ServiceIdentifiers.TStringArrayStorage)
         .to(StringArrayStorage)
         .inSingletonScope();
 

+ 6 - 25
src/custom-nodes/string-array-nodes/StringArrayNode.ts

@@ -3,11 +3,11 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TStatement } from '../../types/node/TStatement';
-import { TStringArrayStorage } from '../../types/storages/TStringArrayStorage';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeFormatter';
+import { IStringArrayStorage } from '../../interfaces/storages/string-array-storage/IStringArrayStorage';
 
 import { initializable } from '../../decorators/Initializable';
 
@@ -15,15 +15,14 @@ import { StringArrayTemplate } from '../../templates/string-array-nodes/string-a
 
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeUtils } from '../../node/NodeUtils';
-import { StringArrayStorage } from '../../storages/string-array/StringArrayStorage';
 
 @injectable()
 export class StringArrayNode extends AbstractCustomNode {
     /**
-     * @type {TStringArrayStorage}
+     * @type {IStringArrayStorage}
      */
     @initializable()
-    private stringArrayStorage!: TStringArrayStorage;
+    private stringArrayStorage!: IStringArrayStorage;
 
     /**
      * @type {string}
@@ -31,12 +30,6 @@ export class StringArrayNode extends AbstractCustomNode {
     @initializable()
     private stringArrayName!: string;
 
-    /**
-     * @type {number}
-     */
-    @initializable()
-    private stringArrayRotateValue!: number;
-
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {ICustomNodeFormatter} customNodeFormatter
@@ -54,27 +47,15 @@ export class StringArrayNode extends AbstractCustomNode {
     }
 
     /**
-     * @param {TStringArrayStorage} stringArrayStorage
+     * @param {IStringArrayStorage} stringArrayStorage
      * @param {string} stringArrayName
-     * @param {number} stringArrayRotateValue
      */
     public initialize (
-        stringArrayStorage: TStringArrayStorage,
-        stringArrayName: string,
-        stringArrayRotateValue: number
+        stringArrayStorage: IStringArrayStorage,
+        stringArrayName: string
     ): void {
         this.stringArrayStorage = stringArrayStorage;
         this.stringArrayName = stringArrayName;
-        this.stringArrayRotateValue = stringArrayRotateValue;
-    }
-
-    /**
-     * @returns {TStatement[]}
-     */
-    public getNode (): TStatement[] {
-        (<StringArrayStorage>this.stringArrayStorage).rotateStorage(this.stringArrayRotateValue);
-
-        return super.getNode();
     }
 
     /**

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

@@ -38,7 +38,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
      * @param {number}
      */
     @initializable()
-    private stringArrayRotateValue!: number;
+    private stringArrayRotationAmount!: number;
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
@@ -62,14 +62,14 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
 
     /**
      * @param {string} stringArrayName
-     * @param {number} stringArrayRotateValue
+     * @param {number} stringArrayRotationAmount
      */
     public initialize (
         stringArrayName: string,
-        stringArrayRotateValue: number
+        stringArrayRotationAmount: number
     ): void {
         this.stringArrayName = stringArrayName;
-        this.stringArrayRotateValue = stringArrayRotateValue;
+        this.stringArrayRotationAmount = stringArrayRotationAmount;
     }
 
     /**
@@ -103,7 +103,7 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode {
                 code,
                 timesName,
                 stringArrayName: this.stringArrayName,
-                stringArrayRotateValue: NumberUtils.toHex(this.stringArrayRotateValue),
+                stringArrayRotationAmount: NumberUtils.toHex(this.stringArrayRotationAmount),
                 whileFunctionName
             }),
             {

+ 11 - 19
src/custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup.ts

@@ -5,12 +5,12 @@ import { TCustomNodeFactory } from '../../../types/container/custom-nodes/TCusto
 import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
 import { TInitialData } from '../../../types/TInitialData';
 import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
-import { TStringArrayStorage } from '../../../types/storages/TStringArrayStorage';
 
+import { ICallsGraphData } from '../../../interfaces/analyzers/calls-graph-analyzer/ICallsGraphData';
 import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
-import { ICallsGraphData } from '../../../interfaces/analyzers/calls-graph-analyzer/ICallsGraphData';
+import { IStringArrayStorage } from '../../../interfaces/storages/string-array-storage/IStringArrayStorage';
 
 import { initializable } from '../../../decorators/Initializable';
 
@@ -42,20 +42,20 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
     private readonly customNodeFactory: TCustomNodeFactory;
 
     /**
-     * @type {TStringArrayStorage}
+     * @type {IStringArrayStorage}
      */
-    private readonly stringArrayStorage: TStringArrayStorage;
+    private readonly stringArrayStorage: IStringArrayStorage;
 
     /**
      * @param {TCustomNodeFactory} customNodeFactory
-     * @param {TStringArrayStorage} stringArrayStorage
+     * @param {IStringArrayStorage} stringArrayStorage
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__ICustomNode) customNodeFactory: TCustomNodeFactory,
-        @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: TStringArrayStorage,
+        @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: IStringArrayStorage,
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
             identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@@ -106,21 +106,13 @@ export class StringArrayCustomNodeGroup extends AbstractCustomNodeGroup {
         const stringArrayRotateFunctionNode: ICustomNode<TInitialData<StringArrayRotateFunctionNode>> =
             this.customNodeFactory(CustomNode.StringArrayRotateFunctionNode);
 
-        const stringArrayStorageId: string = this.stringArrayStorage.getStorageId();
-
-        const [stringArrayName, stringArrayCallsWrapperName]: string[] = stringArrayStorageId.split('|');
-
-        let stringArrayRotateValue: number;
-
-        if (this.options.rotateStringArray) {
-            stringArrayRotateValue = this.randomGenerator.getRandomInteger(100, 500);
-        } else {
-            stringArrayRotateValue = 0;
-        }
+        const stringArrayName: string = this.stringArrayStorage.getStorageName();
+        const stringArrayCallsWrapperName: string = this.stringArrayStorage.getStorageCallsWrapperName();
+        const stringArrayRotationAmount: number = this.stringArrayStorage.getRotationAmount();
 
-        stringArrayNode.initialize(this.stringArrayStorage, stringArrayName, stringArrayRotateValue);
+        stringArrayNode.initialize(this.stringArrayStorage, stringArrayName);
         stringArrayCallsWrapper.initialize(stringArrayName, stringArrayCallsWrapperName);
-        stringArrayRotateFunctionNode.initialize(stringArrayName, stringArrayRotateValue);
+        stringArrayRotateFunctionNode.initialize(stringArrayName, stringArrayRotationAmount);
 
         this.customNodes.set(CustomNode.StringArrayNode, stringArrayNode);
         this.customNodes.set(CustomNode.StringArrayCallsWrapper, stringArrayCallsWrapper);

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

@@ -27,6 +27,7 @@ export interface IOptions {
     readonly rotateStringArray: boolean;
     readonly seed: string | number;
     readonly selfDefending: boolean;
+    readonly shuffleStringArray: boolean;
     readonly sourceMap: boolean;
     readonly sourceMapBaseUrl: string;
     readonly sourceMapFileName: string;

+ 24 - 0
src/interfaces/storages/string-array-storage/IStringArrayStorage.d.ts

@@ -0,0 +1,24 @@
+import { IStringArrayStorageItemData } from './IStringArrayStorageItem';
+
+import { IMapStorage } from '../IMapStorage';
+
+export interface IStringArrayStorage extends IMapStorage <string, IStringArrayStorageItemData> {
+    /**
+     * @returns {number}
+     */
+    getRotationAmount (): number;
+
+    /**
+     * @returns {string}
+     */
+    getStorageName (): string;
+
+    /**
+     * @returns {string}
+     */
+    getStorageCallsWrapperName (): string;
+
+    rotateStorage (): void;
+
+    shuffleStorage (): void;
+}

+ 29 - 9
src/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.ts

@@ -1,15 +1,17 @@
-import { inject, injectable, } from 'inversify';
+import { inject, injectable, postConstruct } from 'inversify';
 import { ServiceIdentifiers } from '../../../../container/ServiceIdentifiers';
 
 import * as ESTree from 'estree';
 
-import { TStringArrayStorage } from '../../../../types/storages/TStringArrayStorage';
-
 import { IEscapeSequenceEncoder } from '../../../../interfaces/utils/IEscapeSequenceEncoder';
+import { IInitializable } from '../../../../interfaces/IInitializable';
 import { IOptions } from '../../../../interfaces/options/IOptions';
+import { IStringArrayStorage } from '../../../../interfaces/storages/string-array-storage/IStringArrayStorage';
 import { IStringArrayStorageAnalyzer } from '../../../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
 import { IStringArrayStorageItemData } from '../../../../interfaces/storages/string-array-storage/IStringArrayStorageItem';
 
+import { initializable } from '../../../../decorators/Initializable';
+
 import { StringArrayEncoding } from '../../../../enums/StringArrayEncoding';
 
 import { AbstractObfuscatingReplacer } from '../AbstractObfuscatingReplacer';
@@ -19,7 +21,7 @@ import { NumberUtils } from '../../../../utils/NumberUtils';
 import { Utils } from '../../../../utils/Utils';
 
 @injectable()
-export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplacer {
+export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplacer implements IInitializable {
     /**
      * @type {IEscapeSequenceEncoder}
      */
@@ -30,6 +32,11 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
      */
     private readonly nodesCache: Map <string, ESTree.Node> = new Map();
 
+    /**
+     * @type {IStringArrayStorage}
+     */
+    private readonly stringArrayStorage: IStringArrayStorage;
+
     /**
      * @type {IStringArrayStorageAnalyzer}
      */
@@ -38,26 +45,26 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
     /**
      * @type {string}
      */
-    private readonly stringArrayStorageCallsWrapperName: string;
+    @initializable()
+    private stringArrayStorageCallsWrapperName!: string;
 
     /**
-     * @param {TStringArrayStorage} stringArrayStorage
+     * @param {IStringArrayStorage} stringArrayStorage
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
      * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
      */
     constructor (
-        @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: TStringArrayStorage,
+        @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: IStringArrayStorage,
         @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
         @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(options);
 
+        this.stringArrayStorage = stringArrayStorage;
         this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
         this.escapeSequenceEncoder = escapeSequenceEncoder;
-
-        this.stringArrayStorageCallsWrapperName = stringArrayStorage.getStorageId().split('|')[1];
     }
 
     /**
@@ -84,6 +91,19 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
         return rc4KeyLiteralNode;
     }
 
+    @postConstruct()
+    public initialize (): void {
+        this.stringArrayStorageCallsWrapperName = this.stringArrayStorage.getStorageCallsWrapperName();
+
+        if (this.options.shuffleStringArray) {
+            this.stringArrayStorage.shuffleStorage();
+        }
+
+        if (this.options.rotateStringArray) {
+            this.stringArrayStorage.rotateStorage();
+        }
+    }
+
     /**
      * @param {SimpleLiteral} literalNode
      * @returns {Node}

+ 6 - 0
src/options/Options.ts

@@ -188,6 +188,12 @@ export class Options implements IOptions {
     @IsBoolean()
     public readonly selfDefending!: boolean;
 
+    /**
+     * @type {boolean}
+     */
+    @IsBoolean()
+    public readonly shuffleStringArray!: boolean;
+
     /**
      * @type {boolean}
      */

+ 1 - 0
src/options/normalizer-rules/StringArrayRule.ts

@@ -11,6 +11,7 @@ export const StringArrayRule: TOptionsNormalizerRule = (options: IOptions): IOpt
         options = {
             ...options,
             rotateStringArray: false,
+            shuffleStringArray: false,
             stringArray: false,
             stringArrayEncoding: false,
             stringArrayThreshold: 0

+ 1 - 0
src/options/presets/Default.ts

@@ -27,6 +27,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     rotateStringArray: true,
     seed: 0,
     selfDefending: false,
+    shuffleStringArray: true,
     sourceMap: false,
     sourceMapBaseUrl: '',
     sourceMapFileName: '',

+ 1 - 0
src/options/presets/NoCustomNodes.ts

@@ -26,6 +26,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     rotateStringArray: false,
     seed: 0,
     selfDefending: false,
+    shuffleStringArray: false,
     sourceMap: false,
     sourceMapBaseUrl: '',
     sourceMapFileName: '',

+ 82 - 9
src/storages/string-array/StringArrayStorage.ts

@@ -10,14 +10,17 @@ import { IEncodedValue } from '../../interfaces/IEncodedValue';
 import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 import { IOptions } from '../../interfaces/options/IOptions';
 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';
 
 @injectable()
-export class StringArrayStorage extends MapStorage <string, IStringArrayStorageItemData> {
+export class StringArrayStorage extends MapStorage <string, IStringArrayStorageItemData> implements IStringArrayStorage {
     /**
      * @type {number}
      */
@@ -58,6 +61,23 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
      */
     private readonly rc4Keys: string[];
 
+    /**
+     * @type {number}
+     */
+    private rotationAmount: number = 0;
+
+    /**
+     * @type {string}
+     */
+    @initializable()
+    private stringArrayStorageName!: string;
+
+    /**
+     * @type {string}
+     */
+    @initializable()
+    private stringArrayStorageCallsWrapperName!: string;
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {IArrayUtils} arrayUtils
@@ -99,10 +119,13 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
             .generate(StringArrayStorage.stringArrayNameLength);
         const baseStringArrayCallsWrapperName: string = this.identifierNamesGenerator
             .generate(StringArrayStorage.stringArrayNameLength);
-        const stringArrayName: string = `${this.options.identifiersPrefix}${baseStringArrayName}`;
-        const stringArrayCallsWrapperName: string = `${this.options.identifiersPrefix}${baseStringArrayCallsWrapperName}`;
 
-        this.storageId = `${stringArrayName}|${stringArrayCallsWrapperName}`;
+        this.stringArrayStorageName = `${this.options.identifiersPrefix}${baseStringArrayName}`;
+        this.stringArrayStorageCallsWrapperName = `${this.options.identifiersPrefix}${baseStringArrayCallsWrapperName}`;
+
+        this.rotationAmount = this.options.rotateStringArray
+            ? this.randomGenerator.getRandomInteger(100, 500)
+            : 0;
     }
 
     /**
@@ -114,26 +137,76 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
     }
 
     /**
-     * @param {number} rotationAmount
+     * @returns {number}
      */
-    public rotateStorage (rotationAmount: number): void {
+    public getRotationAmount (): number {
+        return this.rotationAmount;
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getStorageId (): string {
+        return this.stringArrayStorageName;
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getStorageName (): string {
+        return this.getStorageId();
+    }
+
+    /**
+     * @returns {string}
+     */
+    public getStorageCallsWrapperName (): string {
+        return this.stringArrayStorageCallsWrapperName;
+    }
+
+    public rotateStorage (): void {
+        if (!this.getLength()) {
+            return;
+        }
+
         this.storage = new Map(
             this.arrayUtils.rotate(
                 Array.from(this.storage.entries()),
-                rotationAmount
+                this.rotationAmount
             )
         );
     }
 
+    public shuffleStorage (): void {
+        this.storage = new Map(
+            this.arrayUtils
+                .shuffle(Array.from(this.storage.entries()))
+                .map<[string, IStringArrayStorageItemData]>(
+                    (
+                        [value, stringArrayStorageItemData]: [string, IStringArrayStorageItemData],
+                        index: number
+                    ) => {
+                        stringArrayStorageItemData.index = index;
+
+                        return [value, stringArrayStorageItemData];
+                    }
+                )
+                .sort((
+                    [, stringArrayStorageItemDataA]: [string, IStringArrayStorageItemData],
+                    [, stringArrayStorageItemDataB]: [string, IStringArrayStorageItemData]
+                ) => stringArrayStorageItemDataA.index - stringArrayStorageItemDataB.index)
+        );
+    }
+
     /**
      * @returns {string}
      */
     public toString (): string {
         return Array
             .from(this.storage.values())
-            .map((item: IStringArrayStorageItemData) => {
+            .map((stringArrayStorageItemData: IStringArrayStorageItemData) => {
                 return `'${this.escapeSequenceEncoder.encode(
-                    item.encodedValue,
+                    stringArrayStorageItemData.encodedValue,
                     this.options.unicodeEscapeSequence
                 )}'`;
             }).toString();

+ 1 - 1
src/templates/string-array-nodes/string-array-rotate-function-node/StringArrayRotateFunctionTemplate.ts

@@ -11,6 +11,6 @@ export function StringArrayRotateFunctionTemplate (): string {
             };
             
             {code}
-        })({stringArrayName}, 0x{stringArrayRotateValue});
+        })({stringArrayName}, 0x{stringArrayRotationAmount});
     `;
 }

+ 0 - 5
src/types/storages/TStringArrayStorage.d.ts

@@ -1,5 +0,0 @@
-import { IStringArrayStorageItemData } from '../../interfaces/storages/string-array-storage/IStringArrayStorageItem';
-
-import { IMapStorage } from '../../interfaces/storages/IMapStorage';
-
-export type TStringArrayStorage = IMapStorage <string, IStringArrayStorageItemData>;

+ 5 - 1
test/dev/dev.ts

@@ -11,11 +11,15 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
             var bar = 'bar';
             var baz = 'baz';
             var bark = 'bark';
+            
+            console.log(foo, bar, baz, bark);
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             stringArray: true,
-            stringArrayThreshold: 1
+            stringArrayThreshold: 1,
+            rotateStringArray: true,
+            shuffleStringArray: true
         }
     ).getObfuscatedCode();
 

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

@@ -1,6 +1,8 @@
 import { assert } from 'chai';
 import { TypeFromEnum } from '@gradecam/tsenum';
 
+import { TInputOptions } from '../../../src/types/options/TInputOptions';
+
 import { IObfuscatedCode } from '../../../src/interfaces/source-code/IObfuscatedCode';
 
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
@@ -15,7 +17,6 @@ import { IdentifierNamesGenerator } from '../../../src/enums/generators/identifi
 import { buildLargeCode } from '../../helpers/buildLargeCode';
 import { getRegExpMatch } from '../../helpers/getRegExpMatch';
 import { readFileAsString } from '../../helpers/readFileAsString';
-import { TInputOptions } from '../../../src/types/options/TInputOptions';
 
 describe('JavaScriptObfuscator', () => {
     describe('obfuscate', () => {

+ 4 - 0
test/functional-tests/options/OptionsNormalizer.spec.ts

@@ -541,6 +541,7 @@ describe('OptionsNormalizer', () => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
                     ...getDefaultOptions(),
+                    shuffleStringArray: true,
                     stringArray: false,
                     stringArrayEncoding: StringArrayEncoding.Rc4,
                     stringArrayThreshold: 0.5,
@@ -549,6 +550,7 @@ describe('OptionsNormalizer', () => {
 
                 expectedOptionsPreset = {
                     ...getDefaultOptions(),
+                    shuffleStringArray: false,
                     stringArray: false,
                     stringArrayEncoding: false,
                     stringArrayThreshold: 0,
@@ -584,6 +586,7 @@ describe('OptionsNormalizer', () => {
                 optionsPreset = getNormalizedOptions({
                     ...getDefaultOptions(),
                     rotateStringArray: true,
+                    shuffleStringArray: true,
                     stringArray: true,
                     stringArrayThreshold: 0
                 });
@@ -591,6 +594,7 @@ describe('OptionsNormalizer', () => {
                 expectedOptionsPreset = {
                     ...getDefaultOptions(),
                     rotateStringArray: false,
+                    shuffleStringArray: false,
                     stringArray: false,
                     stringArrayThreshold: 0
                 };

+ 3 - 0
test/index.spec.ts

@@ -29,6 +29,9 @@ import './unit-tests/node/node-lexical-scope-utils/NodeLexicalScopeUtils.spec';
 import './unit-tests/node/node-statement-utils/NodeStatementUtils.spec';
 import './unit-tests/node/node-utils/NodeUtils.spec';
 import './unit-tests/node-transformers/preparing-transformers/ObfuscatingGuardsTransformer.spec';
+import './unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/BooleanLiteralObfuscatingReplacer.spec';
+import './unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/NumberLiteralObfuscatingReplacer.spec';
+import './unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.spec';
 import './unit-tests/options/ValidationErrorsFormatter.spec';
 import './unit-tests/source-code/ObfuscatedCode.spec';
 import './unit-tests/storages/ArrayStorage.spec';

+ 60 - 0
test/unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/BooleanLiteralObfuscatingReplacer.spec.ts

@@ -0,0 +1,60 @@
+import 'reflect-metadata';
+
+import { assert } from 'chai';
+import * as ESTree from 'estree';
+
+import { IInversifyContainerFacade } from '../../../../../../src/interfaces/container/IInversifyContainerFacade';
+import { IObfuscatingReplacer } from '../../../../../../src/interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IObfuscatingReplacer';
+
+import { LiteralObfuscatingReplacer } from '../../../../../../src/enums/node-transformers/obfuscating-transformers/obfuscating-replacers/LiteralObfuscatingReplacer';
+import { ServiceIdentifiers } from '../../../../../../src/container/ServiceIdentifiers';
+
+import { InversifyContainerFacade } from '../../../../../../src/container/InversifyContainerFacade';
+import { NodeFactory } from '../../../../../../src/node/NodeFactory';
+
+describe('BooleanLiteralObfuscatingReplacer', () => {
+    describe('replace', () => {
+        let inversifyContainerFacade: IInversifyContainerFacade,
+            obfuscatingReplacer: IObfuscatingReplacer;
+
+        before(() => {
+            inversifyContainerFacade = new InversifyContainerFacade();
+            inversifyContainerFacade.load('', '', {});
+
+            obfuscatingReplacer = inversifyContainerFacade
+                .getNamed(ServiceIdentifiers.IObfuscatingReplacer, LiteralObfuscatingReplacer.BooleanLiteralObfuscatingReplacer);
+        });
+
+        describe('Variant #1: literal value type check', () => {
+            describe('Variant #1: literal values is a `boolean` value', () => {
+                let testFunc: () => void;
+
+                before(() => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode(true);
+
+                    testFunc = () => <ESTree.Identifier>obfuscatingReplacer.replace(literalNode);
+                });
+
+                it('should not throw an error if literal values is a `boolean` value', () => {
+                    assert.doesNotThrow(testFunc,);
+                });
+            });
+
+            describe('Variant #2: literal values is not a `boolean` value', () => {
+                const expectedError: ErrorConstructor = Error;
+
+                let testFunc: () => void;
+
+                before(() => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    testFunc = () => <ESTree.Identifier>obfuscatingReplacer.replace(literalNode);
+                });
+
+                it('should throw an error if literal values is not a `boolean` value', () => {
+                    assert.throws(testFunc, expectedError);
+                });
+            });
+        });
+    });
+});

+ 60 - 0
test/unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/NumberLiteralObfuscatingReplacer.spec.ts

@@ -0,0 +1,60 @@
+import 'reflect-metadata';
+
+import { assert } from 'chai';
+import * as ESTree from 'estree';
+
+import { IInversifyContainerFacade } from '../../../../../../src/interfaces/container/IInversifyContainerFacade';
+import { IObfuscatingReplacer } from '../../../../../../src/interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IObfuscatingReplacer';
+
+import { LiteralObfuscatingReplacer } from '../../../../../../src/enums/node-transformers/obfuscating-transformers/obfuscating-replacers/LiteralObfuscatingReplacer';
+import { ServiceIdentifiers } from '../../../../../../src/container/ServiceIdentifiers';
+
+import { InversifyContainerFacade } from '../../../../../../src/container/InversifyContainerFacade';
+import { NodeFactory } from '../../../../../../src/node/NodeFactory';
+
+describe('NumberLiteralObfuscatingReplacer', () => {
+    describe('replace', () => {
+        let inversifyContainerFacade: IInversifyContainerFacade,
+            obfuscatingReplacer: IObfuscatingReplacer;
+
+        before(() => {
+            inversifyContainerFacade = new InversifyContainerFacade();
+            inversifyContainerFacade.load('', '', {});
+
+            obfuscatingReplacer = inversifyContainerFacade
+                .getNamed(ServiceIdentifiers.IObfuscatingReplacer, LiteralObfuscatingReplacer.NumberLiteralObfuscatingReplacer);
+        });
+
+        describe('Variant #1: literal value type check', () => {
+            describe('Variant #1: literal values is a `number` value', () => {
+                let testFunc: () => void;
+
+                before(() => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode(1);
+
+                    testFunc = () => <ESTree.Identifier>obfuscatingReplacer.replace(literalNode);
+                });
+
+                it('should not throw an error if literal values is a `number` value', () => {
+                    assert.doesNotThrow(testFunc,);
+                });
+            });
+
+            describe('Variant #2: literal values is not a `number` value', () => {
+                const expectedError: ErrorConstructor = Error;
+
+                let testFunc: () => void;
+
+                before(() => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    testFunc = () => <ESTree.Identifier>obfuscatingReplacer.replace(literalNode);
+                });
+
+                it('should throw an error if literal values is not a `number` value', () => {
+                    assert.throws(testFunc, expectedError);
+                });
+            });
+        });
+    });
+});

+ 60 - 0
test/unit-tests/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.spec.ts

@@ -0,0 +1,60 @@
+import 'reflect-metadata';
+
+import { assert } from 'chai';
+import * as ESTree from 'estree';
+
+import { IInversifyContainerFacade } from '../../../../../../src/interfaces/container/IInversifyContainerFacade';
+import { IObfuscatingReplacer } from '../../../../../../src/interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IObfuscatingReplacer';
+
+import { LiteralObfuscatingReplacer } from '../../../../../../src/enums/node-transformers/obfuscating-transformers/obfuscating-replacers/LiteralObfuscatingReplacer';
+import { ServiceIdentifiers } from '../../../../../../src/container/ServiceIdentifiers';
+
+import { InversifyContainerFacade } from '../../../../../../src/container/InversifyContainerFacade';
+import { NodeFactory } from '../../../../../../src/node/NodeFactory';
+
+describe('StringLiteralObfuscatingReplacer', () => {
+    describe('replace', () => {
+        let inversifyContainerFacade: IInversifyContainerFacade,
+            obfuscatingReplacer: IObfuscatingReplacer;
+
+        before(() => {
+            inversifyContainerFacade = new InversifyContainerFacade();
+            inversifyContainerFacade.load('', '', {});
+
+            obfuscatingReplacer = inversifyContainerFacade
+                .getNamed(ServiceIdentifiers.IObfuscatingReplacer, LiteralObfuscatingReplacer.StringLiteralObfuscatingReplacer);
+        });
+
+        describe('Variant #1: literal value type check', () => {
+            describe('Variant #1: literal values is a `string` value', () => {
+                let testFunc: () => void;
+
+                before(() => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    testFunc = () => <ESTree.Identifier>obfuscatingReplacer.replace(literalNode);
+                });
+
+                it('should not throw an error if literal values is a `string` value', () => {
+                    assert.doesNotThrow(testFunc,);
+                });
+            });
+
+            describe('Variant #2: literal values is not a `string` value', () => {
+                const expectedError: ErrorConstructor = Error;
+
+                let testFunc: () => void;
+
+                before(() => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode(1);
+
+                    testFunc = () => <ESTree.Identifier>obfuscatingReplacer.replace(literalNode);
+                });
+
+                it('should throw an error if literal values is not a `string` value', () => {
+                    assert.throws(testFunc, expectedError);
+                });
+            });
+        });
+    });
+});

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