Selaa lähdekoodia

Fixed invalid behaviour of `transformObjectKeys` option when object values contains `this` references

sanex 3 vuotta sitten
vanhempi
commit
3cf94b7152
20 muutettua tiedostoa jossa 411 lisäystä ja 5 poistoa
  1. 4 0
      CHANGELOG.md
  2. 0 0
      dist/index.browser.js
  3. 0 0
      dist/index.cli.js
  4. 0 0
      dist/index.js
  5. 1 1
      package.json
  6. 1 0
      src/enums/node/NodeType.ts
  7. 24 3
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  8. 8 0
      src/node/NodeGuards.ts
  9. 304 1
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec.ts
  10. 9 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/assignment-expression-conditional-expression-this-reference.js
  11. 9 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/conditional-expression-this-reference.js
  12. 9 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement-conditional-expression-this-reference.js
  13. 4 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement-sequence-expression-this-reference-1.js
  14. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement-sequence-expression-this-reference-2.js
  15. 3 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/sequence-expression-this-reference.js
  16. 6 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-1.js
  17. 5 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-2.js
  18. 7 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-3.js
  19. 9 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-4.js
  20. 2 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-this-reference.js

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 
+v2.15.2
+---
+* Fixed invalid behaviour of `transformObjectKeys` option when object values contains `this` references
+
 v2.15.1
 ---
 * **Hotfix**: `domainDest` => `domainLockRedirectUrl` option rename

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/index.browser.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/index.cli.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

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

+ 1 - 0
src/enums/node/NodeType.ts

@@ -49,6 +49,7 @@ export enum NodeType {
     TaggedTemplateExpression = 'TaggedTemplateExpression',
     TemplateElement = 'TemplateElement',
     TemplateLiteral = 'TemplateLiteral',
+    ThisExpression = 'ThisExpression',
     ThrowStatement = 'ThrowStatement',
     TryStatement = 'TryStatement',
     UnaryExpression = 'UnaryExpression',

+ 24 - 3
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -19,6 +19,11 @@ import { NodeStatementUtils } from '../../node/NodeStatementUtils';
 
 @injectable()
 export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {string}
+     */
+    private static readonly thisIdentifierName: string = 'this';
+
     /**
      * @type {ObjectExpressionExtractor[]}
      */
@@ -73,6 +78,18 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
             );
     }
 
+    /**
+     * @param {Identifier | ThisExpression} node
+     * @returns {string}
+     */
+    private static getReferencedIdentifierName (node: ESTree.Identifier | ESTree.ThisExpression): string {
+        if (NodeGuards.isIdentifierNode(node)) {
+            return node.name;
+        } else {
+            return ObjectExpressionKeysTransformer.thisIdentifierName;
+        }
+    }
+
     /**
      * @param {ObjectExpression} objectExpressionNode
      * @param {Node} objectExpressionHostNode
@@ -94,17 +111,21 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
                     isCurrentNode = true;
                 }
 
-                if (!NodeGuards.isIdentifierNode(node)) {
+                if (!NodeGuards.isIdentifierNode(node) && !NodeGuards.isThisExpressionNode(node)) {
                     return;
                 }
 
                 if (!isCurrentNode) {
-                    identifierNamesSet.push(node.name);
+                    identifierNamesSet.push(ObjectExpressionKeysTransformer.getReferencedIdentifierName(node));
 
                     return;
                 }
 
-                if (identifierNamesSet.includes(node.name)) {
+                const hasReferencedIdentifierName: boolean = identifierNamesSet.includes(
+                    ObjectExpressionKeysTransformer.getReferencedIdentifierName(node)
+                );
+
+                if (hasReferencedIdentifierName) {
                     isReferencedIdentifierName = true;
                 }
             },

+ 8 - 0
src/node/NodeGuards.ts

@@ -482,6 +482,14 @@ export class NodeGuards {
         return node.type === NodeType.TemplateLiteral;
     }
 
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    public static isThisExpressionNode (node: ESTree.Node): node is ESTree.ThisExpression {
+        return node.type === NodeType.ThisExpression;
+    }
+
     /**
      * @param {Node} node
      * @returns {boolean}

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

@@ -558,7 +558,7 @@ describe('ObjectExpressionKeysTransformer', () => {
         });
 
         // issue https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
-        describe('Variant #16: object expression inside inside variable declaration', () => {
+        describe('Variant #16: object expression inside variable declaration', () => {
             describe('Without reference on other property', () => {
                 describe('Variant #1: Single variable declarator and object expression parent node is expression node', () => {
                     const match: string = `` +
@@ -807,6 +807,120 @@ describe('ObjectExpressionKeysTransformer', () => {
                 });
             });
         });
+
+        describe('Variant #20: `this` expression', () => {
+            describe('Variant #1: `this` expression as object expression', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{};` +
+                    `${variableMatch}\\['foo'] *= *'bar';` +
+                    `${variableMatch}\\['baz'] *= *'bark';` +
+                    `this\\['object'] *= *${variableMatch};` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/this-expression-1.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should correctly transform object keys', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #2: `this` expression as property value without `this` reference', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{};` +
+                    `${variableMatch}\\['foo'] *= *this\\['foo'];` +
+                    `var ${variableMatch} *= *${variableMatch};` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/this-expression-2.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should correctly transform object keys', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #3: `this` expression as property value with `this` reference after object expression', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{};` +
+                    `${variableMatch}\\['foo'] *= *this\\['foo'];` +
+                    `var ${variableMatch} *= *${variableMatch};` +
+                    `this\\['bar'] *= *'bar';` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/this-expression-3.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should correctly transform object keys', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #4: `this` expression as property value with `this` reference before and after object expression', () => {
+                const match: string = `` +
+                    `this\\['foo'] *= *'foo';` +
+                    `var ${variableMatch} *= *{};` +
+                    `${variableMatch}\\['foo'] *= *this\\['foo'];` +
+                    `var ${variableMatch} *= *${variableMatch};` +
+                    `this\\['bar'] *= *'bar';` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/this-expression-4.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should correctly transform object keys', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+        });
     });
 
     describe('member expression as host of object expression', () => {
@@ -1975,5 +2089,194 @@ describe('ObjectExpressionKeysTransformer', () => {
                 assert.match(obfuscatedCode,  regExp);
             });
         });
+
+        describe('Variant #14: sequence expression `this` reference', () => {
+            const match: string = `` +
+                `this\\['foo'] *= *0x1, *` +
+                `this\\['bar'] *= *{'bar' *: *this\\['foo']};` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/sequence-expression-this-reference.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn ignore sequence expression object expression if it references other sequence expression `this` expression`', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
+
+        describe('Variant #15: return statement sequence expression `this` reference', () => {
+            describe('Variant #1: reference on other sequence expression `this` expression`', () => {
+                const match: string = `` +
+                    `return this\\['foo'] *= *0x1, *` +
+                    `this\\['bar'] *= *{'bar' *: *this\\['foo']};` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/return-statement-sequence-expression-this-reference-1.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore sequence expression object expression if it references other sequence expression `this` expression', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #2: reference on same sequence expression `this` expression', () => {
+                const match: string = `` +
+                    `return *\\(this\\['foo'] *= *${variableMatch}\\)\\['state'] *= *{'expanded' *: *this\\['foo']\\['props']}, *` +
+                    `this\\['foo']\\['state']\\['expanded'];` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/return-statement-sequence-expression-this-reference-2.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore sequence expression object expression if it references other sequence expression `this` expression', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+        });
+
+        describe('Variant #16: conditional expression `this` reference', () => {
+            describe('Variant #1: conditional expression identifier reference', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{};` +
+                    `${variableMatch}\\['bar'] *= *0x1;` +
+                    `this\\['foo'] *\\? *{'bar' *: *this\\['foo']} *: *${variableMatch};` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/conditional-expression-this-reference.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore conditional expression object expression if it references other conditional expression `this` expression', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #2: return statement conditional expression `this` reference', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{};` +
+                    `${variableMatch}\\['bar'] *= *0x1;` +
+                    `return this\\['foo'] *\\? *{'bar' *: *this\\['foo']} *: *${variableMatch};` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/return-statement-conditional-expression-this-reference.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore conditional expression object expression if it references other conditional expression `this` expression', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+
+            describe('Variant #3: assignment expression conditional expression `this` reference', () => {
+                const match: string = `` +
+                    `var ${variableMatch} *= *{};` +
+                    `${variableMatch}\\['bar'] *= *0x1;` +
+                    `this\\['bar'] *= *this\\['foo'] *\\? *{'bar' *: *this\\['foo']} *: *${variableMatch};` +
+                ``;
+                const regExp: RegExp = new RegExp(match);
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/assignment-expression-conditional-expression-this-reference.js');
+
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            transformObjectKeys: true
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('shouldn ignore conditional expression object expression if it references other conditional expression `this` expression', () => {
+                    assert.match(obfuscatedCode,  regExp);
+                });
+            });
+        });
+
+        describe('Variant #17: variable declarator `this` reference', () => {
+            const match: string = `` +
+                `var passthrough *= *${variableMatch} *=> *${variableMatch};` +
+                `var foo *= *this\\['foo'], *bar *= *{'baz' *: *passthrough\\(this\\['foo']\\)};` +
+            ``;
+            const regExp: RegExp = new RegExp(match);
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-this-reference.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        transformObjectKeys: true
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn ignore variable declarator object expression if it references other variable declarator `this` expression', () => {
+                assert.match(obfuscatedCode,  regExp);
+            });
+        });
     });
 });

+ 9 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/assignment-expression-conditional-expression-this-reference.js

@@ -0,0 +1,9 @@
+function test() {
+    this.bar = this.foo
+        ? {
+            bar: this.foo
+        }
+        : {
+            bar: 1
+        };
+}

+ 9 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/conditional-expression-this-reference.js

@@ -0,0 +1,9 @@
+function test() {
+    this.foo
+        ? {
+            bar: this.foo
+        }
+        : {
+            bar: 1
+        };
+}

+ 9 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement-conditional-expression-this-reference.js

@@ -0,0 +1,9 @@
+function test() {
+    return this.foo
+        ? {
+            bar: this.foo
+        }
+        : {
+            bar: 1
+        };
+}

+ 4 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement-sequence-expression-this-reference-1.js

@@ -0,0 +1,4 @@
+function foo () {
+    return (this.foo = 1),
+        (this.bar = {bar: this.foo});
+}

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/return-statement-sequence-expression-this-reference-2.js

@@ -0,0 +1,6 @@
+function test() {
+    return (this.foo = {props: 1})['state'] = {
+            expanded: this.foo.props
+        },
+        this.foo.state.expanded;
+}

+ 3 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/sequence-expression-this-reference.js

@@ -0,0 +1,3 @@
+function foo () {
+    (this.foo = 1), (this.bar = {bar: this.foo});
+}

+ 6 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-1.js

@@ -0,0 +1,6 @@
+(function(){
+    this.object = {
+        foo: 'bar',
+        baz: 'bark'
+    };
+})();

+ 5 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-2.js

@@ -0,0 +1,5 @@
+(function(){
+    var object = {
+        foo: this.foo
+    };
+})();

+ 7 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-3.js

@@ -0,0 +1,7 @@
+(function(){
+    var object = {
+        foo: this.foo
+    };
+
+    this.bar = 'bar';
+})();

+ 9 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/this-expression-4.js

@@ -0,0 +1,9 @@
+(function(){
+    this.foo = 'foo';
+
+    var object = {
+        foo: this.foo
+    };
+
+    this.bar = 'bar';
+})();

+ 2 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/fixtures/variable-declarator-this-reference.js

@@ -0,0 +1,2 @@
+var passthrough = value => value;
+var foo = this.foo, bar = {baz: passthrough(this.foo)}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä