فهرست منبع

Added class scope sanitizing

sanex3339 5 سال پیش
والد
کامیت
c0117912c1

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 
+v0.24.1
+---
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/531
+
 v0.24.0
 ---
 * **Internal refactoring:** completely new mechanism to rename variable names

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
dist/index.browser.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
dist/index.cli.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

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

+ 29 - 0
src/analyzers/scope-analyzer/ScopeAnalyzer.ts

@@ -132,6 +132,35 @@ export class ScopeAnalyzer implements IScopeAnalyzer {
             throw new Error('Cannot acquire scope for node');
         }
 
+        this.sanitizeScopes(scope);
+
         return scope;
     }
+
+    /**
+     * @param {Scope} scope
+     */
+    private sanitizeScopes (scope: eslintScope.Scope): void {
+        scope.childScopes.forEach((childScope: eslintScope.Scope) => {
+            // fix of class scopes
+            // trying to move class scope references to the parent scope
+            if (childScope.type === 'class' && childScope.upper) {
+                const upperVariable: eslintScope.Variable | undefined = childScope.upper.variables
+                    .find((variable: eslintScope.Variable) => {
+                        // class name variable is always first
+                        const classNameVariable: eslintScope.Variable = childScope.variables[0];
+                        const isValidClassNameVariable: boolean = classNameVariable.defs
+                            .some((definition: eslintScope.Definition) => definition.type === 'ClassName');
+
+                        return isValidClassNameVariable && variable.name === classNameVariable.name;
+                    });
+
+                upperVariable?.references.push(...childScope.variables[0].references);
+            }
+        });
+
+        for (const childScope of scope.childScopes) {
+            this.sanitizeScopes(childScope);
+        }
+    }
 }

+ 23 - 4
test/dev/dev.ts

@@ -7,13 +7,32 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            var passthrough = value => value;
-            var foo = 1, bar = {baz: passthrough(foo)}
+            class A {
+                foo() {
+                    return A;
+                }
+                
+                bar() {
+                    var A = 1;
+                    return A;
+                }
+            }
+            
+            console.log(A);
+            
+            function foo () {
+                class B {
+                    foo() {
+                        return B;
+                    }
+                }
+                
+                console.log(B);
+            }
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            compact: false,
-            transformObjectKeys: true
+            compact: false
         }
     ).getObfuscatedCode();
 

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

@@ -38,28 +38,107 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
 
         describe('Variant #2: `classDeclaration` parent block scope is a `ProgramNode`', () => {
             describe('Variant #1: `renameGlobals` option is disabled', () => {
-                const classNameIdentifierRegExp: RegExp = /class *Foo *\{/;
-                const classCallIdentifierRegExp: RegExp = /new *Foo *\( *\);/;
+                describe('Variant #1: base', () => {
+                    const classNameIdentifierRegExp: RegExp = /class *Foo *\{/;
+                    const classCallIdentifierRegExp: RegExp = /new *Foo *\( *\);/;
 
-                let obfuscatedCode: string;
+                    let obfuscatedCode: string;
 
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/parent-block-scope-is-program-node.js');
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/parent-block-scope-is-program-node.js');
 
-                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                        code,
-                        {
-                            ...NO_ADDITIONAL_NODES_PRESET
-                        }
-                    ).getObfuscatedCode();
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('match #1: shouldn\'t transform class name', () => {
+                        assert.match(obfuscatedCode, classNameIdentifierRegExp);
+                    });
+
+                    it('match #2: shouldn\'t transform class name', () => {
+                        assert.match(obfuscatedCode, classCallIdentifierRegExp);
+                    });
                 });
 
-                it('match #1: shouldn\'t transform class name', () => {
-                    assert.match(obfuscatedCode, classNameIdentifierRegExp);
+                describe('Variant #2: correct class name references in global scope', () => {
+                    const classNameIdentifierRegExp: RegExp = /class A *\{/;
+                    const outerClassNameReferenceRegExp: RegExp = /console\['log']\(A\);/;
+                    const innerClassNameReferenceRegExp: RegExp = /return A;/;
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-global-scope.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET
+                            }
+                        ).getObfuscatedCode();
+                    });
+
+                    it('match #1: shouldn\'t transform class name', () => {
+                        assert.match(obfuscatedCode, classNameIdentifierRegExp);
+                    });
+
+                    it('match #2: shouldn\'t transform class name reference outside of class', () => {
+                        assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
+                    });
+
+                    it('match #3: shouldn\'t transform class name reference inside class', () => {
+                        assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
+                    });
                 });
 
-                it('match #2: shouldn\'t transform class name', () => {
-                    assert.match(obfuscatedCode, classCallIdentifierRegExp);
+                describe('Variant #3: correct class name references in function scope', () => {
+                    const classNameIdentifierRegExp: RegExp = /class (_0x[a-f0-9]{4,6}) *\{/;
+                    const outerClassNameReferenceRegExp: RegExp = /console\['log']\((_0x[a-f0-9]{4,6})\);/;
+                    const innerClassNameReferenceRegExp: RegExp = /return (_0x[a-f0-9]{4,6});/;
+
+                    let obfuscatedCode: string;
+                    let classNameIdentifier: string;
+                    let outerClassNameReferenceIdentifierName: string;
+                    let innerClassNameReferenceIdentifierName: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-function-scope.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET
+                            }
+                        ).getObfuscatedCode();
+
+                        classNameIdentifier = getRegExpMatch(obfuscatedCode, classNameIdentifierRegExp);
+                        outerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, outerClassNameReferenceRegExp);
+                        innerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, innerClassNameReferenceRegExp);
+                    });
+
+                    it('match #1: should transform class name', () => {
+                        assert.match(obfuscatedCode, classNameIdentifierRegExp);
+                    });
+
+                    it('match #2: should transform class name reference outside of class', () => {
+                        assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
+                    });
+
+                    it('match #3: should transform class name reference inside class', () => {
+                        assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
+                    });
+
+                    it('match #4: should generate same identifier names for class name and outer class name reference', () => {
+                        assert.equal(classNameIdentifier, outerClassNameReferenceIdentifierName);
+                    });
+
+                    it('match #5: should generate same identifier names for class name and inner class name reference', () => {
+                        assert.equal(classNameIdentifier, innerClassNameReferenceIdentifierName);
+                    });
                 });
             });
 
@@ -117,6 +196,100 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
                         assert.match(obfuscatedCode, identifierRegExp2);
                     });
                 });
+
+                describe('Variant #3: correct class name references in global scope', () => {
+                    const classNameIdentifierRegExp: RegExp = /class (_0x[a-f0-9]{4,6}) *\{/;
+                    const outerClassNameReferenceRegExp: RegExp = /console\['log']\((_0x[a-f0-9]{4,6})\);/;
+                    const innerClassNameReferenceRegExp: RegExp = /return (_0x[a-f0-9]{4,6});/;
+
+                    let obfuscatedCode: string;
+                    let classNameIdentifier: string;
+                    let outerClassNameReferenceIdentifierName: string;
+                    let innerClassNameReferenceIdentifierName: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-global-scope.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameGlobals: true
+                            }
+                        ).getObfuscatedCode();
+
+                        classNameIdentifier = getRegExpMatch(obfuscatedCode, classNameIdentifierRegExp);
+                        outerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, outerClassNameReferenceRegExp);
+                        innerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, innerClassNameReferenceRegExp);
+                    });
+
+                    it('match #1: should transform class name', () => {
+                        assert.match(obfuscatedCode, classNameIdentifierRegExp);
+                    });
+
+                    it('match #2: should transform class name reference outside of class', () => {
+                        assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
+                    });
+
+                    it('match #3: should transform class name reference inside class', () => {
+                        assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
+                    });
+
+                    it('match #4: should generate same identifier names for class name and outer class name reference', () => {
+                        assert.equal(classNameIdentifier, outerClassNameReferenceIdentifierName);
+                    });
+
+                    it('match #5: should generate same identifier names for class name and inner class name reference', () => {
+                        assert.equal(classNameIdentifier, innerClassNameReferenceIdentifierName);
+                    });
+                });
+
+                describe('Variant #4: correct class name references in function scope', () => {
+                    const classNameIdentifierRegExp: RegExp = /class (_0x[a-f0-9]{4,6}) *\{/;
+                    const outerClassNameReferenceRegExp: RegExp = /console\['log']\((_0x[a-f0-9]{4,6})\);/;
+                    const innerClassNameReferenceRegExp: RegExp = /return (_0x[a-f0-9]{4,6});/;
+
+                    let obfuscatedCode: string;
+                    let classNameIdentifier: string;
+                    let outerClassNameReferenceIdentifierName: string;
+                    let innerClassNameReferenceIdentifierName: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-function-scope.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                renameGlobals: true
+                            }
+                        ).getObfuscatedCode();
+
+                        classNameIdentifier = getRegExpMatch(obfuscatedCode, classNameIdentifierRegExp);
+                        outerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, outerClassNameReferenceRegExp);
+                        innerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, innerClassNameReferenceRegExp);
+                    });
+
+                    it('match #1: should transform class name', () => {
+                        assert.match(obfuscatedCode, classNameIdentifierRegExp);
+                    });
+
+                    it('match #2: should transform class name reference outside of class', () => {
+                        assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
+                    });
+
+                    it('match #3: should transform class name reference inside class', () => {
+                        assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
+                    });
+
+                    it('match #4: should generate same identifier names for class name and outer class name reference', () => {
+                        assert.equal(classNameIdentifier, outerClassNameReferenceIdentifierName);
+                    });
+
+                    it('match #5: should generate same identifier names for class name and inner class name reference', () => {
+                        assert.equal(classNameIdentifier, innerClassNameReferenceIdentifierName);
+                    });
+                });
             });
         });
 

+ 9 - 0
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/fixtures/class-name-references-function-scope.js

@@ -0,0 +1,9 @@
+function foo () {
+    class B {
+        foo() {
+            return B;
+        }
+    }
+
+    console.log(B);
+}

+ 7 - 0
test/functional-tests/node-transformers/obfuscating-transformers/scope-identifiers-transformer/class-declaration/fixtures/class-name-references-global-scope.js

@@ -0,0 +1,7 @@
+class A {
+    foo() {
+        return A;
+    }
+}
+
+console.log(A);

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