浏览代码

More complex object keys transformation + some string literal transformations refactoring

sanex3339 5 年之前
父节点
当前提交
fc2cf933d9
共有 20 个文件被更改,包括 382 次插入93 次删除
  1. 0 0
      dist/index.browser.js
  2. 0 0
      dist/index.cli.js
  3. 0 0
      dist/index.js
  4. 13 3
      src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts
  5. 1 1
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  6. 2 22
      src/node-transformers/converting-transformers/ObjectExpressionTransformer.ts
  7. 32 11
      src/node-transformers/obfuscating-transformers/LiteralTransformer.ts
  8. 2 15
      src/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.ts
  9. 17 0
      src/node/NodeFactory.ts
  10. 22 0
      src/node/NodeLiteralUtils.ts
  11. 1 0
      src/storages/string-array/StringArrayStorage.ts
  12. 5 3
      test/dev/dev.ts
  13. 22 14
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts
  14. 7 9
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-1.js
  15. 11 13
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-2.js
  16. 60 2
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec.ts
  17. 1 0
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-computed-key-literal.js
  18. 1 0
      test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-key-literal.js
  19. 49 0
      test/unit-tests/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.spec.ts
  20. 136 0
      test/unit-tests/node/node-literal-utils/NodeLiteralUtils.spec.ts

文件差异内容过多而无法显示
+ 0 - 0
dist/index.browser.js


文件差异内容过多而无法显示
+ 0 - 0
dist/index.cli.js


文件差异内容过多而无法显示
+ 0 - 0
dist/index.js


+ 13 - 3
src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts

@@ -12,6 +12,7 @@ import { IStringArrayStorageItemData } from '../../interfaces/storages/string-ar
 
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeMetadata } from '../../node/NodeMetadata';
+import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
 
 /**
  * Adds values of literal nodes to the string array storage
@@ -67,7 +68,11 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
         }
 
         estraverse.traverse(astTree, {
-            enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+            enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | void => {
+                if (!parentNode) {
+                    return;
+                }
+
                 if (NodeMetadata.isIgnoredNode(node)) {
                     return estraverse.VisitorOption.Skip;
                 }
@@ -76,7 +81,7 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
                     return;
                 }
 
-                this.analyzeLiteralNode(node);
+                this.analyzeLiteralNode(node, parentNode);
             }
         });
     }
@@ -91,12 +96,17 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
 
     /**
      * @param {Literal} literalNode
+     * @param {Node} parentNode
      */
-    private analyzeLiteralNode (literalNode: ESTree.Literal): void {
+    private analyzeLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void {
         if (typeof literalNode.value !== 'string') {
             return;
         }
 
+        if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
+            return;
+        }
+
         if (!this.shouldAddValueToStringArray(literalNode.value)) {
             return;
         }

+ 1 - 1
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -165,7 +165,7 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
         switch (transformationStage) {
             case TransformationStage.Converting:
                 return {
-                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
                         if (
                             parentNode
                             && NodeGuards.isObjectExpressionNode(node)

+ 2 - 22
src/node-transformers/converting-transformers/ObjectExpressionTransformer.ts

@@ -12,7 +12,6 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeGuards } from '../../node/NodeGuards';
-import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 
 /**
  * replaces:
@@ -24,23 +23,14 @@ import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEn
 @injectable()
 export class ObjectExpressionTransformer extends AbstractNodeTransformer {
     /**
-     * @type {IEscapeSequenceEncoder}
-     */
-    private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
-
-    /**
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
-        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(randomGenerator, options);
-
-        this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
 
     /**
@@ -93,7 +83,7 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
             return;
         }
 
-        property.key = NodeFactory.literalNode(this.getPropertyKeyValue(property.key.value));
+        property.key = NodeFactory.literalNode(property.key.value);
     }
 
     /**
@@ -108,16 +98,6 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
             return;
         }
 
-        property.key = NodeFactory.literalNode(this.getPropertyKeyValue(property.key.name));
-    }
-
-    /**
-     * @param {string} inputValue
-     * @returns {string}
-     */
-    private getPropertyKeyValue (inputValue: string): string {
-        return this.options.unicodeEscapeSequence
-            ? this.escapeSequenceEncoder.encode(inputValue, true)
-            : inputValue;
+        property.key = NodeFactory.literalNode(property.key.name);
     }
 }

+ 32 - 11
src/node-transformers/obfuscating-transformers/LiteralTransformer.ts

@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
 
 import { TLiteralObfuscatingReplacerFactory } from '../../types/container/node-transformers/TLiteralObfuscatingReplacerFactory';
 
+import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
@@ -14,12 +15,19 @@ import { LiteralObfuscatingReplacer } from '../../enums/node-transformers/obfusc
 import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { NodeFactory } from '../../node/NodeFactory';
 import { NodeGuards } from '../../node/NodeGuards';
+import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
 import { NodeMetadata } from '../../node/NodeMetadata';
 import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
 export class LiteralTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {IEscapeSequenceEncoder}
+     */
+    private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
+
     /**
      * @type {TLiteralObfuscatingReplacerFactory}
      */
@@ -35,18 +43,21 @@ export class LiteralTransformer extends AbstractNodeTransformer {
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
+     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IObfuscatingReplacer)
             literalObfuscatingReplacerFactory: TLiteralObfuscatingReplacerFactory,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions,
-        @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer
+        @inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
+        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder
     ) {
         super(randomGenerator, options);
 
         this.literalObfuscatingReplacerFactory = literalObfuscatingReplacerFactory;
         this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
+        this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
 
     /**
@@ -68,6 +79,15 @@ export class LiteralTransformer extends AbstractNodeTransformer {
                     }
                 };
 
+            case TransformationStage.Finalizing:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && NodeGuards.isLiteralNode(node)) {
+                            return this.encodeLiteralNodeToEscapeSequence(node, parentNode);
+                        }
+                    }
+                };
+
             default:
                 return null;
         }
@@ -83,7 +103,7 @@ export class LiteralTransformer extends AbstractNodeTransformer {
      * @returns {NodeGuards}
      */
     public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Node {
-        if (this.isProhibitedNode(literalNode, parentNode)) {
+        if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
             return literalNode;
         }
 
@@ -123,17 +143,18 @@ export class LiteralTransformer extends AbstractNodeTransformer {
     /**
      * @param {Literal} literalNode
      * @param {Node} parentNode
-     * @returns {boolean}
+     * @returns {Literal}
      */
-    private isProhibitedNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): boolean {
-        if (NodeGuards.isPropertyNode(parentNode) && parentNode.key === literalNode) {
-            return true;
-        }
-
-        if (NodeGuards.isImportDeclarationNode(parentNode)) {
-            return true;
+    private encodeLiteralNodeToEscapeSequence (
+        literalNode: ESTree.Literal,
+        parentNode: ESTree.Node
+    ): ESTree.Literal {
+        if (typeof literalNode.value !== 'string') {
+            return literalNode;
         }
 
-        return false;
+        return NodeFactory.literalNode(
+            this.escapeSequenceEncoder.encode(literalNode.value, this.options.unicodeEscapeSequence)
+        );
     }
 }

+ 2 - 15
src/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.ts

@@ -3,7 +3,6 @@ import { ServiceIdentifiers } from '../../../../container/ServiceIdentifiers';
 
 import * as ESTree from 'estree';
 
-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';
@@ -22,11 +21,6 @@ import { Utils } from '../../../../utils/Utils';
 
 @injectable()
 export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplacer implements IInitializable {
-    /**
-     * @type {IEscapeSequenceEncoder}
-     */
-    private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
-
     /**
      * @type {Map<string, ESTree.Node>}
      */
@@ -51,20 +45,17 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
     /**
      * @param {IStringArrayStorage} stringArrayStorage
      * @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      * @param {IOptions} options
      */
     constructor (
         @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;
     }
 
     /**
@@ -138,9 +129,7 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
      * @returns {Node}
      */
     private replaceWithLiteralNode (value: string): ESTree.Node {
-        return NodeFactory.literalNode(
-            this.escapeSequenceEncoder.encode(value, this.options.unicodeEscapeSequence)
-        );
+        return NodeFactory.literalNode(value);
     }
 
     /**
@@ -156,9 +145,7 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
         ];
 
         if (decodeKey) {
-            callExpressionArgs.push(StringLiteralObfuscatingReplacer.getRc4KeyLiteralNode(
-                this.escapeSequenceEncoder.encode(decodeKey, this.options.unicodeEscapeSequence)
-            ));
+            callExpressionArgs.push(StringLiteralObfuscatingReplacer.getRc4KeyLiteralNode(decodeKey));
         }
 
         const stringArrayIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.stringArrayStorageCallsWrapperName);

+ 17 - 0
src/node/NodeFactory.ts

@@ -228,6 +228,23 @@ export class NodeFactory {
         };
     }
 
+    /**
+     * @param {(ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier)[]} specifiers
+     * @param {Literal} source
+     * @returns {ImportDeclaration}
+     */
+    public static importDeclarationNode (
+        specifiers: (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[],
+        source: ESTree.Literal
+    ): ESTree.ImportDeclaration {
+        return {
+            type: NodeType.ImportDeclaration,
+            specifiers,
+            source,
+            metadata: { ignoredNode: false }
+        };
+    }
+
     /**
      * @param {boolean | number | string} value
      * @param {string} raw

+ 22 - 0
src/node/NodeLiteralUtils.ts

@@ -0,0 +1,22 @@
+import * as ESTree from 'estree';
+
+import { NodeGuards } from './NodeGuards';
+
+export class NodeLiteralUtils {
+    /**
+     * @param {Literal} literalNode
+     * @param {Node} parentNode
+     * @returns {boolean}
+     */
+    public static isProhibitedLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): boolean {
+        if (NodeGuards.isPropertyNode(parentNode) && !parentNode.computed && parentNode.key === literalNode) {
+            return true;
+        }
+
+        if (NodeGuards.isImportDeclarationNode(parentNode)) {
+            return true;
+        }
+
+        return false;
+    }
+}

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

@@ -218,6 +218,7 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
         return Array
             .from(this.storage.values())
             .map((stringArrayStorageItemData: IStringArrayStorageItemData) => {
+                // we have to encode here, because of possible errors during `parse` of StringArrayCustomNode
                 return `'${this.escapeSequenceEncoder.encode(
                     stringArrayStorageItemData.encodedValue,
                     this.options.unicodeEscapeSequence

+ 5 - 3
test/dev/dev.ts

@@ -7,12 +7,14 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            var foo = {foo: 1};
+            var foo = {['foo']: 1};
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            transformObjectKeys: true,
-            compact: false
+            compact: false,
+            unicodeEscapeSequence: true,
+            stringArray: true,
+            stringArrayThreshold: 1
         }
     ).getObfuscatedCode();
 

+ 22 - 14
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts

@@ -152,11 +152,14 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         describe('Variant #6: nested objects #1', () => {
             const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['inner1'] *= *${variableMatch};` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
-                `${variableMatch}\\['inner'] *= *{};` +
-                `${variableMatch}\\['inner']\\['inner1'] *= *{};` +
-                `${variableMatch}\\['inner']\\['inner1']\\['baz'] *= *'bark';` +
+                `${variableMatch}\\['inner'] *= *${variableMatch};` +
+                `var object *= *${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
@@ -181,14 +184,17 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         describe('Variant #7: nested objects #2', () => {
             const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['hawk'] *= *'geek';` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `${variableMatch}\\['inner1'] *= *${variableMatch};` +
+                `${variableMatch}\\['cow'] *= *'bear';` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
-                `${variableMatch}\\['inner'] *= *{};` +
+                `${variableMatch}\\['inner'] *= *${variableMatch};` +
                 `${variableMatch}\\['ball'] *= *'door';` +
-                `${variableMatch}\\['inner']\\['baz'] *= *'bark';` +
-                `${variableMatch}\\['inner']\\['inner1'] *= *{};` +
-                `${variableMatch}\\['inner']\\['cow'] *= *'bear';` +
-                `${variableMatch}\\['inner']\\['inner1']\\['hawk'] *= *'geek';` +
+                `var *object *= *${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
 
@@ -213,14 +219,16 @@ describe('ObjectExpressionKeysTransformer', () => {
 
         describe('Variant #8: nested objects #3', () => {
             const match: string = `` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['hawk'] *= *'geek';` +
+                `var *${variableMatch} *= *{};` +
+                `${variableMatch}\\['baz'] *= *'bark';` +
+                `${variableMatch}\\['inner1'] *= *${variableMatch};` +
+                `${variableMatch}\\['cow'] *= *'bear';` +
                 `var *${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'bar';` +
-                `${variableMatch}\\['inner'] *= *{};` +
+                `${variableMatch}\\['inner'] *= *${variableMatch};` +
                 `${variableMatch}\\['ball'] *= *'door';` +
-                `${variableMatch}\\['inner']\\['baz'] *= *'bark';` +
-                `${variableMatch}\\['inner']\\['inner1'] *= *{};` +
-                `${variableMatch}\\['inner']\\['cow'] *= *'bear';` +
-                `${variableMatch}\\['inner']\\['inner1']\\['hawk'] *= *'geek';` +
                 `return ${variableMatch};` +
             ``;
             const regExp: RegExp = new RegExp(match);
@@ -1394,7 +1402,7 @@ describe('ObjectExpressionKeysTransformer', () => {
                 `var ${variableMatch} *= *{};` +
                 `${variableMatch}\\['foo'] *= *'test';` +
                 `var foo *= *${variableMatch};` +
-                `var ${variableMatch} *= *{\\[foo.foo] *: *'1'};` +
+                `var ${variableMatch} *= *{\\[foo\\['foo']] *: *'1'};` +
                 `${variableMatch}\\['bar'] *= *'2';` +
                 `var bar *= *${variableMatch};` +
             ``;

+ 7 - 9
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-1.js

@@ -1,10 +1,8 @@
-(function(){
-    var object = {
-        foo: 'bar',
-        inner: {
-            inner1: {
-                baz: 'bark'
-            }
+var object = {
+    foo: 'bar',
+    inner: {
+        inner1: {
+            baz: 'bark'
         }
-    };
-})();
+    }
+};

+ 11 - 13
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/nested-objects-2.js

@@ -1,14 +1,12 @@
-(function(){
-    var object = {
-        foo: 'bar',
-        inner: {
-            baz: 'bark',
-            inner1: {
-                hawk: 'geek'
-            },
-            cow: 'bear',
-
+var object = {
+    foo: 'bar',
+    inner: {
+        baz: 'bark',
+        inner1: {
+            hawk: 'geek'
         },
-        ball: 'door',
-    };
-})();
+        cow: 'bear',
+
+    },
+    ball: 'door',
+};

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

@@ -135,7 +135,7 @@ describe('LiteralTransformer', () => {
 
         describe('Variant #6: `unicodeEscapeSequence` and `stringArray` options are enabled', () => {
             const stringArrayRegExp: RegExp = /^var *_0x([a-f0-9]){4} *= *\['\\x74\\x65\\x73\\x74'\];/;
-            const stringArrayCallRegExp: RegExp = /var *test *= *_0x([a-f0-9]){4}\('0x0'\);/;
+            const stringArrayCallRegExp: RegExp = /var *test *= *_0x([a-f0-9]){4}\('\\x30\\x78\\x30'\);/;
 
             let obfuscatedCode: string;
 
@@ -157,7 +157,7 @@ describe('LiteralTransformer', () => {
                 assert.match(obfuscatedCode, stringArrayRegExp);
             });
 
-            it('match #1: should replace literal node value with unicode escape sequence from string array', () => {
+            it('match #2: should replace literal node value with unicode escape sequence from string array', () => {
                 assert.match(obfuscatedCode, stringArrayCallRegExp);
             });
         });
@@ -518,6 +518,64 @@ describe('LiteralTransformer', () => {
                 });
             });
         });
+
+        describe('Variant #13: object expression key literal', () => {
+            describe('Variant #1: base key literal', () => {
+                const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['bar'];/;
+                const objectExpressionRegExp: RegExp = /var test *= *{'foo' *: *_0x([a-f0-9]){4}\('0x0'\)};/;
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/object-expression-key-literal.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('match #1: should not add object expression key literal to the string array', () => {
+                    assert.match(obfuscatedCode, stringArrayRegExp);
+                });
+
+                it('match #2: should keep object expression key literal', () => {
+                    assert.match(obfuscatedCode, objectExpressionRegExp);
+                });
+            });
+
+            describe('Variant #2: computed key literal', () => {
+                const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['foo', *'bar'];/;
+                const objectExpressionRegExp: RegExp = /var test *= *{\[_0x([a-f0-9]){4}\('0x0'\)] *: *_0x([a-f0-9]){4}\('0x1'\)};/;
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/object-expression-computed-key-literal.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('match #1: should add object expression computed key literal to the string array', () => {
+                    assert.match(obfuscatedCode, stringArrayRegExp);
+                });
+
+                it('match #2: should replace object expression computed key literal on call to the string array', () => {
+                    assert.match(obfuscatedCode, objectExpressionRegExp);
+                });
+            });
+        });
     });
 
     describe('transformation of literal node with boolean value', () => {

+ 1 - 0
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-computed-key-literal.js

@@ -0,0 +1 @@
+var test = {['foo']: 'bar'};

+ 1 - 0
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/object-expression-key-literal.js

@@ -0,0 +1 @@
+var test = {'foo': 'bar'};

+ 49 - 0
test/unit-tests/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.spec.ts

@@ -157,6 +157,55 @@ describe('StringArrayStorageAnalyzer', () => {
             });
         });
 
+        describe('Analyzes of the AST tree with prohibited string literal nodes', () => {
+            const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
+            const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');
+
+            const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
+                encodedValue: 'foo',
+                decodeKey: null,
+                index: 0,
+                value: 'foo'
+            };
+            const expectedStringArrayStorageItemData2: undefined = undefined;
+
+            let stringArrayStorageItemData1: IStringArrayStorageItemData | undefined;
+            let stringArrayStorageItemData2: IStringArrayStorageItemData | undefined;
+
+            before(() => {
+                stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
+                    stringArrayThreshold: 1
+                });
+
+                const astTree: ESTree.Program = NodeFactory.programNode([
+                    NodeFactory.expressionStatementNode(literalNode1),
+                    NodeFactory.variableDeclarationNode([
+                        NodeFactory.variableDeclaratorNode(
+                            NodeFactory.identifierNode('bar'),
+                            NodeFactory.objectExpressionNode([
+                                NodeFactory.propertyNode(
+                                    literalNode2,
+                                    NodeFactory.literalNode(1)
+                                )
+                            ])
+                        )
+                    ])
+                ]);
+
+                stringArrayStorageAnalyzer.analyze(astTree);
+                stringArrayStorageItemData1 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode1);
+                stringArrayStorageItemData2 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode2);
+            });
+
+            it('Variant #1: should return correct string array storage item data for literal node #1', () => {
+                assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
+            });
+
+            it('Variant #2: should return correct string array storage item data for literal node #1', () => {
+                assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
+            });
+        });
+
         describe('Analyzes of the AST tree with ignored nodes', () => {
             const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
             const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');

+ 136 - 0
test/unit-tests/node/node-literal-utils/NodeLiteralUtils.spec.ts

@@ -0,0 +1,136 @@
+import * as ESTree from 'estree';
+
+import { assert } from 'chai';
+
+import { NodeFactory } from '../../../../src/node/NodeFactory';
+import { NodeLiteralUtils } from '../../../../src/node/NodeLiteralUtils';
+
+describe('NodeLiteralUtils', () => {
+    describe('isProhibitedLiteralNode', () => {
+        describe('String literal node', () => {
+            describe('Variant #1: base string literal node', () => {
+                const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                let statementNode: ESTree.Statement;
+
+                before(() => {
+                    statementNode = NodeFactory.expressionStatementNode(
+                        literalNode
+                    );
+
+                    literalNode.parentNode = statementNode;
+                });
+
+                it('should return false for base string literal node', () => {
+                    assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, statementNode), false);
+                });
+            });
+
+            describe('Variant #2: property literal node', () => {
+                describe('Variant #1: property key literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let propertyNode: ESTree.Property;
+
+                    before(() => {
+                        propertyNode = NodeFactory.propertyNode(
+                            literalNode,
+                            NodeFactory.literalNode(1)
+                        );
+
+                        literalNode.parentNode = propertyNode;
+                    });
+
+                    it('should return false for property key literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), true);
+                    });
+                });
+
+                describe('Variant #2: computed property key literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let propertyNode: ESTree.Property;
+
+                    before(() => {
+                        propertyNode = NodeFactory.propertyNode(
+                            literalNode,
+                            NodeFactory.literalNode(1),
+                            true
+                        );
+
+                        literalNode.parentNode = propertyNode;
+                    });
+
+                    it('should return false for computed property key literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), false);
+                    });
+                });
+
+                describe('Variant #3: property value literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let propertyNode: ESTree.Property;
+
+                    before(() => {
+                        propertyNode = NodeFactory.propertyNode(
+                            NodeFactory.literalNode(1),
+                            literalNode
+                        );
+
+                        literalNode.parentNode = propertyNode;
+                    });
+
+                    it('should return false for property value literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), false);
+                    });
+                });
+            });
+
+            describe('Variant #3: import declaration node', () => {
+                describe('Variant #1: base import declaration literal node', () => {
+                    const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
+
+                    let importDeclarationNode: ESTree.ImportDeclaration;
+
+                    before(() => {
+                        importDeclarationNode = NodeFactory.importDeclarationNode(
+                            [],
+                            literalNode
+                        );
+
+                        literalNode.parentNode = importDeclarationNode;
+                    });
+
+                    it('should return false for import declaration literal node', () => {
+                        assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, importDeclarationNode), true);
+                    });
+                });
+            });
+        });
+
+        describe('Number literal node', () => {
+            describe('Variant #1: base number literal node', () => {
+                const literalNode: ESTree.Literal = NodeFactory.literalNode(1);
+
+                let statementNode: ESTree.Statement;
+
+                before(() => {
+                    statementNode = NodeFactory.expressionStatementNode(
+                        literalNode
+                    );
+
+                    const blockStatementNode: ESTree.BlockStatement = NodeFactory.blockStatementNode([
+                        statementNode
+                    ]);
+
+                    statementNode.parentNode = blockStatementNode;
+                    literalNode.parentNode = statementNode;
+                });
+
+                it('should return false for base number literal node', () => {
+                    assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, statementNode), false);
+                });
+            });
+        });
+    });
+});

部分文件因为文件数量过多而无法显示