فهرست منبع

Merge pull request #879 from javascript-obfuscator/rename-properties-auto-exclude

Added `renamePropertiesMode` option
Timofey Kachalov 4 سال پیش
والد
کامیت
05cd3bed61
27فایلهای تغییر یافته به همراه1158 افزوده شده و 387 حذف شده
  1. 5 0
      CHANGELOG.md
  2. 15 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. 6 4
      src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts
  8. 9 2
      src/cli/JavaScriptObfuscatorCLI.ts
  9. 9 0
      src/enums/node-transformers/rename-properties-transformers/RenamePropertiesMode.ts
  10. 4 2
      src/interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer.ts
  11. 5 0
      src/interfaces/node-transformers/rename-properties-transformers/replacer/IRenamePropertiesReplacer.ts
  12. 2 0
      src/interfaces/options/IOptions.ts
  13. 48 1
      src/node-transformers/rename-properties-transformers/RenamePropertiesTransformer.ts
  14. 22 1
      src/node-transformers/rename-properties-transformers/replacer/RenamePropertiesReplacer.ts
  15. 556 0
      src/node-transformers/rename-properties-transformers/replacer/ReservedDomProperties.json
  16. 3 2
      src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer.ts
  17. 1 1
      src/node/NodeGuards.ts
  18. 4 2
      src/node/NodeLiteralUtils.ts
  19. 8 0
      src/options/Options.ts
  20. 2 0
      src/options/presets/Default.ts
  21. 2 0
      src/options/presets/NoCustomNodes.ts
  22. 3 0
      src/types/node/TStringLiteralNode.ts
  23. 5 0
      src/types/options/TRenamePropertiesMode.ts
  24. 13 9
      test/dev/dev.ts
  25. 348 289
      test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts
  26. 13 0
      test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/safe-mode.js
  27. 74 71
      test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts

+ 5 - 0
CHANGELOG.md

@@ -1,5 +1,10 @@
 Change Log
 
+v2.11.0
+---
+* Added option `renamePropertiesMode` to switch between new `safe` and old `unsafe` modes of `renameProperties` option. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/878
+* `renameProperties` option now works in `safe` way by default
+
 v2.10.7
 ---
 * Fixed CVE-2019-18413. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/880

+ 15 - 2
README.md

@@ -371,6 +371,7 @@ Following options are available for the JS Obfuscator:
     optionsPreset: 'default',
     renameGlobals: false,
     renameProperties: false,
+    renamePropertiesMode: 'safe',
     reservedNames: [],
     reservedStrings: [],
     rotateStringArray: true,
@@ -429,6 +430,7 @@ Following options are available for the JS Obfuscator:
     --options-preset <string> [default, low-obfuscation, medium-obfuscation, high-obfuscation]
     --rename-globals <boolean>
     --rename-properties <boolean>
+    --rename-properties-mode <string> [safe, unsafe]
     --reserved-names '<list>' (comma separated)
     --reserved-strings '<list>' (comma separated)
     --rotate-string-array <boolean>
@@ -785,10 +787,12 @@ Enables obfuscation of global variable and function names **with declaration**.
 ### `renameProperties`
 Type: `boolean` Default: `false`
 
-##### :warning: this option **WILL** break your code in most cases. Enable it only if you know what it does!
+##### :warning: this option **MAY** break your code. Enable it only if you know what it does!
 
 Enables renaming of property names. All built-in DOM properties and properties in core JavaScript classes will be ignored.
 
+To switch between `safe` and `unsafe` modes of this option use [`renamePropertiesMode`](#renamepropertiesmode) option.
+
 To set format of renamed property names use [`identifierNamesGenerator`](#identifiernamesgenerator) option.
 
 To control which properties will be renamed use [`reservedNames`](#reservednames) option.
@@ -821,6 +825,15 @@ Example:
 }());
 ```
 
+### `renamePropertiesMode`
+Type: `string` Default: `safe`
+
+##### :warning: Even in `safe` mode, [`renameProperties`](#renameproperties) option **MAY** break your code.
+
+Specifies `renameProperties` option mode:
+* `safe` - default behaviour after `2.11.0` release. Trying to rename properties in a more safe way to prevent runtime errors. With this mode some properties will be excluded from renaming.
+* `unsafe` - default behaviour before `2.11.0` release. Renames properties in an unsafe way without any restrictions.
+
 ### `reservedNames`
 Type: `string[]` Default: `[]`
 
@@ -1447,7 +1460,7 @@ See: [`Kind of variables`](#kind-of-variables)
 
 ### I enabled `renameProperties` option, and my code broke! What to do?
 
-Just disable this option.
+Try `renamePropertiesMode: 'safe'` option, if it still doesn't work, just disable this option.
 
 ## Backers
 

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 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": "2.10.7",
+  "version": "2.11.0",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

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

@@ -4,6 +4,8 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
+import { TStringLiteralNode } from '../../types/node/TStringLiteralNode';
+
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
@@ -107,9 +109,9 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
     }
 
     /**
-     * @param {(SimpleLiteral & {value: string}) | (RegExpLiteral & {value: string})} literalNode
+     * @param {TStringLiteralNode} literalNode
      */
-    public addItemDataForLiteralNode (literalNode: ESTree.Literal & {value: string}): void {
+    public addItemDataForLiteralNode (literalNode: TStringLiteralNode): void {
         this.stringArrayStorageData.set(
             literalNode,
             this.stringArrayStorage.getOrThrow(literalNode.value)
@@ -125,10 +127,10 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
     }
 
     /**
-     * @param {(SimpleLiteral & {value: string})} literalNode
+     * @param {TStringLiteralNode} literalNode
      * @returns {boolean}
      */
-    private shouldAddValueToStringArray (literalNode: ESTree.Literal & {value: string}): boolean {
+    private shouldAddValueToStringArray (literalNode: TStringLiteralNode): boolean {
         const isForceTransformNode: boolean = NodeMetadata.isForceTransformNode(literalNode);
 
         if (isForceTransformNode) {

+ 9 - 2
src/cli/JavaScriptObfuscatorCLI.ts

@@ -15,8 +15,10 @@ import { IdentifierNamesGenerator } from '../enums/generators/identifier-names-g
 import { LoggingPrefix } from '../enums/logger/LoggingPrefix';
 import { ObfuscationTarget } from '../enums/ObfuscationTarget';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
+import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
 import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
+import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
 import { DEFAULT_PRESET } from '../options/presets/Default';
@@ -30,7 +32,6 @@ import { Logger } from '../logger/Logger';
 import { ObfuscatedCodeWriter } from './utils/ObfuscatedCodeWriter';
 import { SourceCodeReader } from './utils/SourceCodeReader';
 import { Utils } from '../utils/Utils';
-import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 
 export class JavaScriptObfuscatorCLI implements IInitializable {
     /**
@@ -285,9 +286,15 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 BooleanSanitizer
             )
             .option(
-                '--rename-properties <boolean>', 'UNSAFE: Enables renaming of property names. This probably WILL break your code',
+                '--rename-properties <boolean>', 'UNSAFE: Enables renaming of property names. This probably MAY break your code',
                 BooleanSanitizer
             )
+            .option(
+                '--rename-properties-mode <boolean>',
+                'Specify `--rename-properties` option mode. ' +
+                `Values: ${CLIUtils.stringifyOptionAvailableValues(RenamePropertiesMode)}. ` +
+                `Default: ${RenamePropertiesMode.Safe}`
+            )
             .option(
                 '--rotate-string-array <boolean>', 'Enable rotation of string array values during obfuscation',
                 BooleanSanitizer

+ 9 - 0
src/enums/node-transformers/rename-properties-transformers/RenamePropertiesMode.ts

@@ -0,0 +1,9 @@
+import { Utils } from '../../../utils/Utils';
+
+export const RenamePropertiesMode: Readonly<{
+    Safe: 'safe';
+    Unsafe: 'unsafe';
+}> = Utils.makeEnum({
+    Safe: 'safe',
+    Unsafe: 'unsafe'
+});

+ 4 - 2
src/interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer.ts

@@ -1,5 +1,7 @@
 import * as ESTree from 'estree';
 
+import { TStringLiteralNode } from '../../../types/node/TStringLiteralNode';
+
 import { IAnalyzer } from '../IAnalyzer';
 import { IStringArrayStorageItemData } from '../../storages/string-array-transformers/IStringArrayStorageItem';
 
@@ -16,9 +18,9 @@ export interface IStringArrayStorageAnalyzer extends IAnalyzer<[ESTree.Program],
     analyzeLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void;
 
     /**
-     * @param {(SimpleLiteral & {value: string}) | (RegExpLiteral & {value: string})} literalNode
+     * @param {TStringLiteralNode} stringLiteralNode
      */
-    addItemDataForLiteralNode (literalNode: ESTree.Literal & {value: string}): void;
+    addItemDataForLiteralNode (stringLiteralNode: TStringLiteralNode): void;
 
     /**
      * @param {Literal} literalNode

+ 5 - 0
src/interfaces/node-transformers/rename-properties-transformers/replacer/IRenamePropertiesReplacer.ts

@@ -1,6 +1,11 @@
 import * as ESTree from 'estree';
 
 export interface IRenamePropertiesReplacer {
+    /**
+     * @param {string} propertyName
+     */
+    excludePropertyName (propertyName: string): void;
+
     /**
      * @param {ESTree.Identifier | ESTree.Literal} node
      * @returns {ESTree.Identifier | ESTree.Literal}

+ 2 - 0
src/interfaces/options/IOptions.ts

@@ -2,6 +2,7 @@ import { TOptionsPreset } from '../../types/options/TOptionsPreset';
 import { TStringArrayIndexesType } from '../../types/options/TStringArrayIndexesType';
 import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';
 import { TStringArrayWrappersType } from '../../types/options/TStringArrayWrappersType';
+import { TRenamePropertiesMode } from '../../types/options/TRenamePropertiesMode';
 import { TTypeFromEnum } from '../../types/utils/TTypeFromEnum';
 
 import { IdentifierNamesGenerator } from '../../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
@@ -29,6 +30,7 @@ export interface IOptions {
     readonly optionsPreset: TOptionsPreset;
     readonly renameGlobals: boolean;
     readonly renameProperties: boolean;
+    readonly renamePropertiesMode: TRenamePropertiesMode;
     readonly reservedNames: string[];
     readonly reservedStrings: string[];
     readonly rotateStringArray: boolean;

+ 48 - 1
src/node-transformers/rename-properties-transformers/RenamePropertiesTransformer.ts

@@ -1,4 +1,4 @@
-import { inject, injectable, } from 'inversify';
+import { inject, injectable} from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 import * as ESTree from 'estree';
@@ -12,6 +12,8 @@ import { NodeTransformationStage } from '../../enums/node-transformers/NodeTrans
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
+import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
+import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 
 @injectable()
 export class RenamePropertiesTransformer extends AbstractNodeTransformer {
@@ -59,6 +61,15 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer {
      */
     public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null {
         switch (nodeTransformationStage) {
+            case NodeTransformationStage.Preparing:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null): void => {
+                        if (parentNode) {
+                            this.prepareNode(node, parentNode);
+                        }
+                    }
+                };
+
             case NodeTransformationStage.RenameProperties:
                 return {
                     enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
@@ -73,6 +84,19 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer {
         }
     }
 
+    /**
+     * @param {Node} node
+     * @param {Node} parentNode
+     */
+    public prepareNode (
+        node: ESTree.Node,
+        parentNode: ESTree.Node
+    ): void {
+        if (this.options.renamePropertiesMode === RenamePropertiesMode.Safe) {
+            this.analyzeAutoExcludedPropertyNames(node, parentNode);
+        }
+    }
+
     /**
      * @param {Node} node
      * @param {NodeGuards} parentNode
@@ -136,4 +160,27 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer {
 
         return methodDefinitionNode;
     }
+
+    /**
+     * @param {Node} node
+     * @param {Node} parentNode
+     */
+    private analyzeAutoExcludedPropertyNames (
+        node: ESTree.Node,
+        parentNode: ESTree.Node
+    ): void {
+        if (!NodeGuards.isLiteralNode(node) || !NodeLiteralUtils.isStringLiteralNode(node)) {
+            return;
+        }
+
+        if (
+            (NodeGuards.isPropertyNode(parentNode) && parentNode.key === node)
+            || NodeGuards.isMemberExpressionNode(parentNode) && parentNode.property === node
+            || NodeGuards.isMethodDefinitionNode(parentNode) && parentNode.key === node
+        ) {
+            return;
+        }
+
+        this.renamePropertiesReplacer.excludePropertyName(node.value);
+    }
 }

+ 22 - 1
src/node-transformers/rename-properties-transformers/replacer/RenamePropertiesReplacer.ts

@@ -33,6 +33,11 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
      */
     private readonly identifierNamesGenerator: IIdentifierNamesGenerator;
 
+    /**
+     * @type {Set<string>}
+     */
+    private readonly excludedPropertyNames: Set<string> = new Set();
+
     /**
      * @type {Map<string, string>}
      * @private
@@ -57,6 +62,13 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
         this.options = options;
     }
 
+    /**
+     * @param {string} propertyName
+     */
+    public excludePropertyName (propertyName: string): void {
+       this.excludedPropertyNames.add(propertyName);
+    }
+
     /**
      * @param {ESTree.Identifier | ESTree.Literal} node
      * @returns {ESTree.Identifier | ESTree.Literal}
@@ -104,10 +116,19 @@ export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
      * @returns {boolean}
      */
     private isReservedName (name: string): boolean {
-        return this.isReservedOptionName(name)
+        return this.isExcludedName(name)
+            || this.isReservedOptionName(name)
             || this.isReservedDomPropertyName(name);
     }
 
+    /**
+     * @param {string} name
+     * @returns {boolean}
+     */
+    private isExcludedName (name: string): boolean {
+        return this.excludedPropertyNames.has(name);
+    }
+
     /**
      * @param {string} name
      * @returns {boolean}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 556 - 0
src/node-transformers/rename-properties-transformers/replacer/ReservedDomProperties.json


+ 3 - 2
src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer.ts

@@ -8,6 +8,7 @@ import { TCustomCodeHelperFactory } from '../../types/container/custom-code-help
 import { TInitialData } from '../../types/TInitialData';
 import { TNumberNumericalExpressionData } from '../../types/analyzers/number-numerical-expression-analyzer/TNumberNumericalExpressionData';
 import { TStatement } from '../../types/node/TStatement';
+import { TStringLiteralNode } from '../../types/node/TStringLiteralNode';
 
 import { ICustomCodeHelper } from '../../interfaces/custom-code-helpers/ICustomCodeHelper';
 import { INodeTransformersRunner } from '../../interfaces/node-transformers/INodeTransformersRunner';
@@ -258,10 +259,10 @@ export class StringArrayRotateFunctionTransformer extends AbstractNodeTransforme
     }
 
     /**
-     * @param {Literal} stringLiteralNode
+     * @param {TStringLiteralNode} stringLiteralNode
      * @returns {boolean}
      */
-    private isComparisonExpressionStringLiteralNode (stringLiteralNode: ESTree.Literal & {value: string}): boolean {
+    private isComparisonExpressionStringLiteralNode (stringLiteralNode: TStringLiteralNode): boolean {
         return /\d/.test(stringLiteralNode.value);
     }
 }

+ 1 - 1
src/node/NodeGuards.ts

@@ -3,10 +3,10 @@ import * as ESTree from 'estree';
 
 import { TNodeWithLexicalScope } from '../types/node/TNodeWithLexicalScope';
 import { TNodeWithLexicalScopeStatements } from '../types/node/TNodeWithLexicalScopeStatements';
+import { TNodeWithSingleStatementBody } from '../types/node/TNodeWithSingleStatementBody';
 import { TNodeWithStatements } from '../types/node/TNodeWithStatements';
 
 import { NodeType } from '../enums/node/NodeType';
-import { TNodeWithSingleStatementBody } from '../types/node/TNodeWithSingleStatementBody';
 
 export class NodeGuards {
     /**

+ 4 - 2
src/node/NodeLiteralUtils.ts

@@ -1,13 +1,15 @@
 import * as ESTree from 'estree';
 
+import { TStringLiteralNode } from '../types/node/TStringLiteralNode';
+
 import { NodeGuards } from './NodeGuards';
 
 export class NodeLiteralUtils {
     /**
      * @param {Literal} literalNode
-     * @returns {literalNode is (SimpleLiteral & {value: string})}
+     * @returns {literalNode is TStringLiteralNode}
      */
-    public static isStringLiteralNode (literalNode: ESTree.Literal): literalNode is ESTree.Literal & {value: string} {
+    public static isStringLiteralNode (literalNode: ESTree.Literal): literalNode is TStringLiteralNode {
         return typeof literalNode.value === 'string';
     }
 

+ 8 - 0
src/options/Options.ts

@@ -20,6 +20,7 @@ import {
 
 import { TInputOptions } from '../types/options/TInputOptions';
 import { TOptionsPreset } from '../types/options/TOptionsPreset';
+import { TRenamePropertiesMode } from '../types/options/TRenamePropertiesMode';
 import { TStringArrayIndexesType } from '../types/options/TStringArrayIndexesType';
 import { TStringArrayEncoding } from '../types/options/TStringArrayEncoding';
 import { TStringArrayWrappersType } from '../types/options/TStringArrayWrappersType';
@@ -31,6 +32,7 @@ import { IOptionsNormalizer } from '../interfaces/options/IOptionsNormalizer';
 import { IdentifierNamesGenerator } from '../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { ObfuscationTarget } from '../enums/ObfuscationTarget';
 import { OptionsPreset } from '../enums/options/presets/OptionsPreset';
+import { RenamePropertiesMode } from '../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../enums/source-map/SourceMapMode';
 import { StringArrayIndexesType } from '../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayEncoding } from '../enums/node-transformers/string-array-transformers/StringArrayEncoding';
@@ -215,6 +217,12 @@ export class Options implements IOptions {
     @IsBoolean()
     public readonly renameProperties!: boolean;
 
+    /**
+     * @type {RenamePropertiesMode}
+     */
+    @IsIn([RenamePropertiesMode.Safe, RenamePropertiesMode.Unsafe])
+    public readonly renamePropertiesMode!: TRenamePropertiesMode;
+
     /**
      * @type {string[]}
      */

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

@@ -3,6 +3,7 @@ import { TInputOptions } from '../../types/options/TInputOptions';
 import { IdentifierNamesGenerator } from '../../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
 import { OptionsPreset } from '../../enums/options/presets/OptionsPreset';
+import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
 import { StringArrayIndexesType } from '../../enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
@@ -31,6 +32,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     optionsPreset: OptionsPreset.Default,
     renameGlobals: false,
     renameProperties: false,
+    renamePropertiesMode: RenamePropertiesMode.Safe,
     reservedNames: [],
     reservedStrings: [],
     rotateStringArray: true,

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

@@ -2,6 +2,7 @@ import { TInputOptions } from '../../types/options/TInputOptions';
 
 import { IdentifierNamesGenerator } from '../../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
+import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { SourceMapMode } from '../../enums/source-map/SourceMapMode';
 import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayWrappersType } from '../../enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -28,6 +29,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     numbersToExpressions: false,
     renameGlobals: false,
     renameProperties: false,
+    renamePropertiesMode: RenamePropertiesMode.Safe,
     reservedNames: [],
     reservedStrings: [],
     rotateStringArray: false,

+ 3 - 0
src/types/node/TStringLiteralNode.ts

@@ -0,0 +1,3 @@
+import * as ESTree from 'estree';
+
+export type TStringLiteralNode = ESTree.Literal & {value: string};

+ 5 - 0
src/types/options/TRenamePropertiesMode.ts

@@ -0,0 +1,5 @@
+import { TTypeFromEnum } from '../utils/TTypeFromEnum';
+
+import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
+
+export type TRenamePropertiesMode = TTypeFromEnum<typeof RenamePropertiesMode>;

+ 13 - 9
test/dev/dev.ts

@@ -1,25 +1,29 @@
 'use strict';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
+import { RenamePropertiesMode } from '../../src/enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            (function () {
-                const log = console.log;
-                const first = 'foo';
-                const rest = ['bar', 'baz', 'bark'];
-                log(first, ...rest);            
-            })();
-
+            const object = {
+                foo: 1,
+                bar: 2,
+                baz: 3
+            };
+            
+            var excluded1 = 'bar';
+            var excluded2 = 'baz';
+            
+            console.log(object.foo, object['bar'], object.baz);
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
-            controlFlowFlattening: true,
-            controlFlowFlatteningThreshold: 1
+            renameProperties: true,
+            renamePropertiesMode: RenamePropertiesMode.Safe
         }
     ).getObfuscatedCode();
 

+ 348 - 289
test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts

@@ -3,6 +3,7 @@ import { assert } from 'chai';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
 import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
+import { RenamePropertiesMode } from '../../../../../src/enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 
@@ -10,341 +11,399 @@ import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFac
 
 describe('RenamePropertiesTransformer', () => {
     describe('transformNode', () => {
-        describe('Hexadecimal identifier names generator', () => {
-            describe('Variant #1: base properties rename', () => {
-                const property1RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x1/;
-                const property2RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x2/;
-                const property3RegExp: RegExp = /\['(_0x[a-f0-9]{4,6})']: *0x3/;
-                const property4RegExp: RegExp = /\[hawk]: *0x4/;
-
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/base.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator
-                        }
-                    ).getObfuscatedCode();
-                });
-
-                it('Match #1: should rename property', () => {
-                    assert.match(obfuscatedCode, property1RegExp);
-                });
-
-                it('Match #2: should rename property', () => {
-                    assert.match(obfuscatedCode, property2RegExp);
-                });
-
-                it('Match #3: should rename property', () => {
-                    assert.match(obfuscatedCode, property3RegExp);
-                });
-
-                it('Match #4: should rename property', () => {
-                    assert.match(obfuscatedCode, property4RegExp);
-                });
-            });
-        });
-
-        describe('Mangled identifier names generator', () => {
-            describe('Variant #1: base properties mangle', () => {
-                const property1RegExp: RegExp = /'a': *0x1/;
-                const property2RegExp: RegExp = /'b': *0x2/;
-                const property3RegExp: RegExp = /\['c']: *0x3/;
-                const property4RegExp: RegExp = /\[hawk]: *0x4/;
-
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/base.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
-                        }
-                    ).getObfuscatedCode();
-                });
-
-                it('Match #1: should rename property', () => {
-                    assert.match(obfuscatedCode, property1RegExp);
-                });
-
-                it('Match #2: should rename property', () => {
-                    assert.match(obfuscatedCode, property2RegExp);
-                });
-
-                it('Match #3: should rename property', () => {
-                    assert.match(obfuscatedCode, property3RegExp);
-                });
-
-                it('Match #4: should rename property', () => {
-                    assert.match(obfuscatedCode, property4RegExp);
+        describe('Mode: `unsafe`', () => {
+            describe('Hexadecimal identifier names generator', () => {
+                describe('Variant #1: base properties rename', () => {
+                    const property1RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x1/;
+                    const property2RegExp: RegExp = /'(_0x[a-f0-9]{4,6})': *0x2/;
+                    const property3RegExp: RegExp = /\['(_0x[a-f0-9]{4,6})']: *0x3/;
+                    const property4RegExp: RegExp = /\[hawk]: *0x4/;
+
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/base.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Match #1: should rename property', () => {
+                        assert.match(obfuscatedCode, property1RegExp);
+                    });
+
+                    it('Match #2: should rename property', () => {
+                        assert.match(obfuscatedCode, property2RegExp);
+                    });
+
+                    it('Match #3: should rename property', () => {
+                        assert.match(obfuscatedCode, property3RegExp);
+                    });
+
+                    it('Match #4: should rename property', () => {
+                        assert.match(obfuscatedCode, property4RegExp);
+                    });
                 });
             });
 
-            describe('Variant #2: base properties rename with rename globals', () => {
-                const variable1RegExp: RegExp = /const d *= *'hawk'/;
-                const variable2RegExp: RegExp = /const e *= *{/;
-                const property1RegExp: RegExp = /'a': *0x1/;
-                const property2RegExp: RegExp = /'b': *0x2/;
-                const property3RegExp: RegExp = /\['c']: *0x3/;
-                const property4RegExp: RegExp = /\[d]: *0x4/;
-
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/base.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
-                            renameGlobals: true
-                        }
-                    ).getObfuscatedCode();
+            describe('Mangled identifier names generator', () => {
+                describe('Variant #1: base properties mangle', () => {
+                    const property1RegExp: RegExp = /'a': *0x1/;
+                    const property2RegExp: RegExp = /'b': *0x2/;
+                    const property3RegExp: RegExp = /\['c']: *0x3/;
+                    const property4RegExp: RegExp = /\[hawk]: *0x4/;
+
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/base.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Match #1: should rename property', () => {
+                        assert.match(obfuscatedCode, property1RegExp);
+                    });
+
+                    it('Match #2: should rename property', () => {
+                        assert.match(obfuscatedCode, property2RegExp);
+                    });
+
+                    it('Match #3: should rename property', () => {
+                        assert.match(obfuscatedCode, property3RegExp);
+                    });
+
+                    it('Match #4: should rename property', () => {
+                        assert.match(obfuscatedCode, property4RegExp);
+                    });
                 });
 
-                it('Match #1: should rename variable name', () => {
-                    assert.match(obfuscatedCode, variable1RegExp);
+                describe('Variant #2: base properties rename with rename globals', () => {
+                    const variable1RegExp: RegExp = /const d *= *'hawk'/;
+                    const variable2RegExp: RegExp = /const e *= *{/;
+                    const property1RegExp: RegExp = /'a': *0x1/;
+                    const property2RegExp: RegExp = /'b': *0x2/;
+                    const property3RegExp: RegExp = /\['c']: *0x3/;
+                    const property4RegExp: RegExp = /\[d]: *0x4/;
+
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/base.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                renameGlobals: true
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Match #1: should rename variable name', () => {
+                        assert.match(obfuscatedCode, variable1RegExp);
+                    });
+
+                    it('Match #2: should rename variable name', () => {
+                        assert.match(obfuscatedCode, variable2RegExp);
+                    });
+
+                    it('Match #3: should rename property', () => {
+                        assert.match(obfuscatedCode, property1RegExp);
+                    });
+
+                    it('Match #4: should rename property', () => {
+                        assert.match(obfuscatedCode, property2RegExp);
+                    });
+
+                    it('Match #5: should rename property', () => {
+                        assert.match(obfuscatedCode, property3RegExp);
+                    });
+
+                    it('Match #6: should rename property', () => {
+                        assert.match(obfuscatedCode, property4RegExp);
+                    });
                 });
 
-                it('Match #2: should rename variable name', () => {
-                    assert.match(obfuscatedCode, variable2RegExp);
+                describe('Variant #3: properties rename of nested objects', () => {
+                    const regExp: RegExp = new RegExp('' +
+                        'const foo *= *{' +
+                            '\'a\': *{' +
+                                '\'b\': *0x1' +
+                            '}' +
+                        '};' +
+                        'const bar *= *foo\\[\'a\']\\[\'b\'];' +
+                    '');
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/nested-objects.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Should rename property', () => {
+                        assert.match(obfuscatedCode, regExp);
+                    });
                 });
 
-                it('Match #3: should rename property', () => {
-                    assert.match(obfuscatedCode, property1RegExp);
+                describe('Variant #4: properties rename of rest element', () => {
+                    const regExp: RegExp = new RegExp('' +
+                        'const foo *= *{' +
+                            '\'a\': *0x1' +
+                        '};' +
+                        'const \\{a: *bar} *= *foo;' +
+                        'const baz *= *bar;' +
+                    '');
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/rest-element.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Should rename property', () => {
+                        assert.match(obfuscatedCode, regExp);
+                    });
                 });
 
-                it('Match #4: should rename property', () => {
-                    assert.match(obfuscatedCode, property2RegExp);
+                describe('Variant #5: reserved dom properties', () => {
+                    const regExp: RegExp = new RegExp('' +
+                        'const foo *= *{' +
+                            '\'a\': *0x1,' +
+                            '\'join\': *0x2,' +
+                            '\'b\': *0x3,' +
+                            '\'c\': *0x4' +
+                        '};' +
+                        'const baz *= *foo\\[\'a\'] *\\+ *foo\\[\'join\'] *\\+ *foo\\[\'b\'] *\\+ *foo\\[\'c\'];' +
+                    '');
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/reserved-properties.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Should rename non-reserved properties', () => {
+                        assert.match(obfuscatedCode, regExp);
+                    });
                 });
 
-                it('Match #5: should rename property', () => {
-                    assert.match(obfuscatedCode, property3RegExp);
+                describe('Variant #6: reserved names properties', () => {
+                    const regExp: RegExp = new RegExp('' +
+                        'const foo *= *{' +
+                            '\'a\': *0x1,' +
+                            '\'join\': *0x2,' +
+                            '\'reserved\': *0x3,' +
+                            '\'private_\': *0x4' +
+                        '};' +
+                        'const baz *= *foo\\[\'a\'] *\\+ *foo\\[\'join\'] *\\+ *foo\\[\'reserved\'] *\\+ *foo\\[\'private_\'];' +
+                    '');
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/reserved-properties.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                reservedNames: ['^reserved$', '_$']
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Should rename non-reserved properties', () => {
+                        assert.match(obfuscatedCode, regExp);
+                    });
                 });
 
-                it('Match #6: should rename property', () => {
-                    assert.match(obfuscatedCode, property4RegExp);
-                });
-            });
-
-            describe('Variant #3: properties rename of nested objects', () => {
-                const regExp: RegExp = new RegExp('' +
-                    'const foo *= *{' +
-                        '\'a\': *{' +
-                            '\'b\': *0x1' +
+                describe('Variant #7: class methods', () => {
+                    const regExp: RegExp = new RegExp('' +
+                        'class Foo *{' +
+                            '\\[\'a\'] *\\(\\) *{}' +
                         '}' +
-                    '};' +
-                    'const bar *= *foo\\[\'a\']\\[\'b\'];' +
-                '');
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/nested-objects.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
-                        }
-                    ).getObfuscatedCode();
-                });
-
-                it('Should rename property', () => {
-                    assert.match(obfuscatedCode, regExp);
-                });
-            });
-
-            describe('Variant #4: properties rename of rest element', () => {
-                const regExp: RegExp = new RegExp('' +
-                    'const foo *= *{' +
-                        '\'a\': *0x1' +
-                    '};' +
-                    'const \\{a: *bar} *= *foo;' +
-                    'const baz *= *bar;' +
-                '');
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/rest-element.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
-                        }
-                    ).getObfuscatedCode();
+                        'const foo *= *new Foo\\(\\);' +
+                        'foo\\[\'a\']\\(\\);' +
+                    '');
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/class-methods.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                reservedNames: ['^reserved$', '_$']
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Should rename class method name', () => {
+                        assert.match(obfuscatedCode, regExp);
+                    });
                 });
 
-                it('Should rename property', () => {
-                    assert.match(obfuscatedCode, regExp);
+                describe('Variant #8: integration with `splitStrings` option', () => {
+                    const propertyRegExp: RegExp = new RegExp(
+                        'const foo *= *{' +
+                            '\'a\': *\'long\' *\\+ *\'Prop\' *\\+ *\'erty\' *\\+ *\'Valu\' *\\+ *\'e\'' +
+                        '};' +
+                        'foo\\[\'a\'];'
+                    );
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/split-strings-integration.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                splitStrings: true,
+                                splitStringsChunkLength: 4
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('Should rename property before `splitStrings` option will applied', () => {
+                        assert.match(obfuscatedCode, propertyRegExp);
+                    });
                 });
             });
 
-            describe('Variant #5: reserved dom properties', () => {
-                const regExp: RegExp = new RegExp('' +
-                    'const foo *= *{' +
-                        '\'a\': *0x1,' +
-                        '\'join\': *0x2,' +
-                        '\'b\': *0x3,' +
-                        '\'c\': *0x4' +
-                    '};' +
-                    'const baz *= *foo\\[\'a\'] *\\+ *foo\\[\'join\'] *\\+ *foo\\[\'b\'] *\\+ *foo\\[\'c\'];' +
-                '');
+            describe('Ignored literal node type', () => {
+                describe('Variant #1: boolean literal node', () => {
+                    const regExp: RegExp = /var obj *= *{}; *obj\[!!\[]] *= *0x1;/;
 
-                let obfuscatedCode: string;
+                    let obfuscatedCode: string;
 
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/reserved-properties.js');
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/boolean-literal-node.js');
 
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
-                        }
-                    ).getObfuscatedCode();
-                });
-
-                it('Should rename non-reserved properties', () => {
-                    assert.match(obfuscatedCode, regExp);
-                });
-            });
-
-            describe('Variant #6: reserved names properties', () => {
-                const regExp: RegExp = new RegExp('' +
-                    'const foo *= *{' +
-                        '\'a\': *0x1,' +
-                        '\'join\': *0x2,' +
-                        '\'reserved\': *0x3,' +
-                        '\'private_\': *0x4' +
-                    '};' +
-                    'const baz *= *foo\\[\'a\'] *\\+ *foo\\[\'join\'] *\\+ *foo\\[\'reserved\'] *\\+ *foo\\[\'private_\'];' +
-                '');
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/reserved-properties.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
-                            reservedNames: ['^reserved$', '_$']
-                        }
-                    ).getObfuscatedCode();
-                });
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameProperties: true,
+                                renamePropertiesMode: RenamePropertiesMode.Unsafe
+                            }
+                        ).getObfuscatedCode();
+                    });
 
-                it('Should rename non-reserved properties', () => {
-                    assert.match(obfuscatedCode, regExp);
+                    it('Match #1: should skip literal property with invalid type', () => {
+                        assert.match(obfuscatedCode, regExp);
+                    });
                 });
             });
+        });
 
-            describe('Variant #7: class methods', () => {
-                const regExp: RegExp = new RegExp('' +
-                    'class Foo *{' +
-                        '\\[\'a\'] *\\(\\) *{}' +
+        describe('Mode: `safe`', () => {
+            describe('Variant #1: base properties rename', () => {
+                const declarationsRegExp: RegExp = new RegExp('' +
+                    'const object *= *{' +
+                        '\'foo\': *0x1, *' +
+                        '\'a\': *0x2 *' +
+                    '}; *' +
+                    'class Class *{ *' +
+                        'static\\[\'baz\'] *\\(\\) *{} *' +
+                        'static\\[\'b\'] *\\(\\) *{} *' +
                     '}' +
-                    'const foo *= *new Foo\\(\\);' +
-                    'foo\\[\'a\']\\(\\);' +
+                '');
+                const referencesRegExp: RegExp = new RegExp('' +
+                    'console\\[\'log\']\\(' +
+                        'object\\[\'foo\'], *' +
+                        'object\\[\'a\'], *' +
+                        'Class\\[\'baz\'], *' +
+                        'Class\\[\'b\'] *' +
+                    '\\);' +
                 '');
 
                 let obfuscatedCode: string;
 
                 before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/class-methods.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
-                            reservedNames: ['^reserved$', '_$']
-                        }
-                    ).getObfuscatedCode();
-                });
-
-                it('Should rename class method name', () => {
-                    assert.match(obfuscatedCode, regExp);
-                });
-            });
-
-            describe('Variant #8: integration with `splitStrings` option', () => {
-                const propertyRegExp: RegExp = new RegExp(
-                    'const foo *= *{' +
-                        '\'a\': *\'long\' *\\+ *\'Prop\' *\\+ *\'erty\' *\\+ *\'Valu\' *\\+ *\'e\'' +
-                    '};' +
-                    'foo\\[\'a\'];'
-                );
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/split-strings-integration.js');
+                    const code: string = readFileAsString(__dirname + '/fixtures/safe-mode.js');
 
                     obfuscatedCode = JavaScriptObfuscator.obfuscate(
                         code,
                         {
                             ...NO_ADDITIONAL_NODES_PRESET,
                             renameProperties: true,
-                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
-                            splitStrings: true,
-                            splitStringsChunkLength: 4
+                            renamePropertiesMode: RenamePropertiesMode.Safe,
+                            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
                         }
                     ).getObfuscatedCode();
                 });
 
-                it('Should rename property before `splitStrings` option will applied', () => {
-                    assert.match(obfuscatedCode, propertyRegExp);
-                });
-            });
-        });
-
-        describe('Ignored literal node type', () => {
-            describe('Variant #1: boolean literal node', () => {
-                const regExp: RegExp = /var obj *= *{}; *obj\[!!\[]] *= *0x1;/;
-
-
-                let obfuscatedCode: string;
-
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/boolean-literal-node.js');
-
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET,
-                            renameProperties: true
-                        }
-                    ).getObfuscatedCode();
+                it('Should rename property declarations', () => {
+                    assert.match(obfuscatedCode, declarationsRegExp);
                 });
 
-                it('Match #1: should skip literal property with invalid type', () => {
-                    assert.match(obfuscatedCode, regExp);
+                it('Should rename property references', () => {
+                    assert.match(obfuscatedCode, referencesRegExp);
                 });
             });
         });

+ 13 - 0
test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/safe-mode.js

@@ -0,0 +1,13 @@
+const object = {
+    foo: 1,
+    bar: 2
+};
+class Class {
+    static baz () {}
+    static bark () {}
+}
+
+var excluded1 = 'foo';
+var excluded2 = 'baz';
+
+console.log(object.foo, object['bar'], Class.baz, Class['bark']);

+ 74 - 71
test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts

@@ -3,6 +3,7 @@ import { assert } from 'chai';
 import { TInputOptions } from '../../src/types/options/TInputOptions';
 
 import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
+import { RenamePropertiesMode } from '../../src/enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
 import { StringArrayEncoding } from '../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
 import { StringArrayIndexesType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType';
 import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
@@ -30,7 +31,7 @@ describe('JavaScriptObfuscator runtime eval', function () {
         numbersToExpressions: true,
         simplify: true,
         renameProperties: true,
-        reservedNames: ['generate', 'sha256'],
+        renamePropertiesMode: RenamePropertiesMode.Unsafe,
         rotateStringArray: true,
         selfDefending: true,
         splitStrings: true,
@@ -92,78 +93,79 @@ describe('JavaScriptObfuscator runtime eval', function () {
                 const code: string = readFileAsString(__dirname + '/fixtures/astring.js');
 
                 const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
-                    code,
+                    `
+                    ${getEnvironmentCode()}
+                    ${code}
+                    const code = generate({
+                        "type": "Program",
+                        "body": [
+                            {
+                                "type": "FunctionDeclaration",
+                                "id": {
+                                    "type": "Identifier",
+                                    "name": "test",
+                                    "range": [
+                                        9,
+                                        13
+                                    ]
+                                },
+                                "params": [],
+                                "body": {
+                                    "type": "BlockStatement",
+                                    "body": [
+                                        {
+                                            "type": "ReturnStatement",
+                                            "argument": {
+                                                "type": "Literal",
+                                                "value": "foo",
+                                                "raw": "'foo'",
+                                                "range": [
+                                                    30,
+                                                    35
+                                                ]
+                                            },
+                                            "range": [
+                                                23,
+                                                36
+                                            ]
+                                        }
+                                    ],
+                                    "range": [
+                                        17,
+                                        38
+                                    ]
+                                },
+                                "generator": false,
+                                "expression": false,
+                                "async": false,
+                                "range": [
+                                    0,
+                                    38
+                                ]
+                            }
+                        ],
+                        "sourceType": "module",
+                        "range": [
+                            0,
+                            38
+                        ],
+                        "comments": []
+                    });
+                    
+                    eval(\`\${code} test();\`);
+                    `,
                     {
                         ...baseOptions,
                         ...options,
-                        renameProperties: false
+                        renamePropertiesMode: RenamePropertiesMode.Safe,
+                        reservedNames: ['generate']
                     }
                 ).getObfuscatedCode();
 
                 let evaluationResult: string;
 
                 try {
-                    evaluationResult = eval(`
-                        ${getEnvironmentCode()}
-                        ${obfuscatedCode}
-                        const code = generate({
-                            "type": "Program",
-                            "body": [
-                                {
-                                    "type": "FunctionDeclaration",
-                                    "id": {
-                                        "type": "Identifier",
-                                        "name": "test",
-                                        "range": [
-                                            9,
-                                            13
-                                        ]
-                                    },
-                                    "params": [],
-                                    "body": {
-                                        "type": "BlockStatement",
-                                        "body": [
-                                            {
-                                                "type": "ReturnStatement",
-                                                "argument": {
-                                                    "type": "Literal",
-                                                    "value": "foo",
-                                                    "raw": "'foo'",
-                                                    "range": [
-                                                        30,
-                                                        35
-                                                    ]
-                                                },
-                                                "range": [
-                                                    23,
-                                                    36
-                                                ]
-                                            }
-                                        ],
-                                        "range": [
-                                            17,
-                                            38
-                                        ]
-                                    },
-                                    "generator": false,
-                                    "expression": false,
-                                    "async": false,
-                                    "range": [
-                                        0,
-                                        38
-                                    ]
-                                }
-                            ],
-                            "sourceType": "module",
-                            "range": [
-                                0,
-                                38
-                            ],
-                            "comments": []
-                        });
-                        
-                        eval(\`\${code} test();\`);
-                    `)
+                    evaluationResult = eval(obfuscatedCode)
                 } catch (e) {
                     throw new Error(`Evaluation error: ${e.message}. Code: ${obfuscatedCode}`);
                 }
@@ -177,19 +179,20 @@ describe('JavaScriptObfuscator runtime eval', function () {
                 const code: string = readFileAsString(__dirname + '/fixtures/sha256.js');
 
                 const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
-                    code,
+                    `
+                    ${getEnvironmentCode()}
+                    ${code}
+                    sha256('test');
+                    `,
                     {
                         ...baseOptions,
-                        ...options
+                        ...options,
+                        reservedNames: ['sha256']
                     }
                 ).getObfuscatedCode();
 
                 assert.equal(
-                    eval(`
-                        ${getEnvironmentCode()}
-                        ${obfuscatedCode}
-                        sha256('test');
-                    `),
+                    eval(obfuscatedCode),
                     '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
                 );
             });

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است