浏览代码

Node Guards: Conditional Comment Node Guard

sanex3339 7 年之前
父节点
当前提交
92bc935cc8

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


+ 1 - 0
src/JavaScriptObfuscatorInternal.ts

@@ -103,6 +103,7 @@ export class JavaScriptObfuscatorInternal implements IJavaScriptObfuscator {
      */
     private parseCode (sourceCode: string): ESTree.Program {
         return esprima.parseScript(sourceCode, {
+            attachComment: true,
             loc: this.options.sourceMap
         });
     }

+ 19 - 25
src/Obfuscator.ts

@@ -4,7 +4,7 @@ import { ServiceIdentifiers } from './container/ServiceIdentifiers';
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
-import { TNodeGuard } from './types/node/TNodeGuard';
+import { TNodeGuardFactory } from './types/container/node-guards/TNodeGuardFactory';
 import { TNodeTransformerFactory } from './types/container/node-transformers/TNodeTransformerFactory';
 import { TVisitorDirection } from './types/TVisitorDirection';
 import { TVisitorFunction } from './types/TVisitorFunction';
@@ -21,6 +21,7 @@ import { IStorage } from './interfaces/storages/IStorage';
 import { IVisitor } from './interfaces/IVisitor';
 
 import { LoggingMessage } from './enums/logger/LoggingMessage';
+import { NodeGuard } from './enums/container/node-guards/NodeGuard';
 import { NodeTransformer } from './enums/container/node-transformers/NodeTransformer';
 import { ObfuscationEvent } from './enums/event-emitters/ObfuscationEvent';
 import { VisitorDirection } from './enums/VisitorDirection';
@@ -30,13 +31,6 @@ import { NodeUtils } from './node/NodeUtils';
 
 @injectable()
 export class Obfuscator implements IObfuscator {
-    /**
-     * @type {((node: Node) => boolean)[]}
-     */
-    private static readonly blackListGuards: TNodeGuard[] = [
-        Node.isUseStrictOperator
-    ];
-
     /**
      * @type {NodeTransformer[]}
      */
@@ -75,6 +69,11 @@ export class Obfuscator implements IObfuscator {
         NodeTransformer.VariableDeclarationTransformer
     ];
 
+    private static readonly nodeGuardsList: NodeGuard[] = [
+        NodeGuard.BlackListNodeGuard,
+        NodeGuard.ConditionalCommentNodeGuard
+    ];
+
     /**
      * @type {IStorage<ICustomNodeGroup>}
      */
@@ -85,6 +84,11 @@ export class Obfuscator implements IObfuscator {
      */
     private readonly logger: ILogger;
 
+    /**
+     * @type {TNodeGuardFactory}
+     */
+    private readonly nodeGuardFactory: TNodeGuardFactory;
+
     /**
      * @type {TNodeTransformerFactory}
      */
@@ -109,6 +113,7 @@ export class Obfuscator implements IObfuscator {
      * @param {IStackTraceAnalyzer} stackTraceAnalyzer
      * @param {IObfuscationEventEmitter} obfuscationEventEmitter
      * @param {IStorage<ICustomNodeGroup>} customNodeGroupStorage
+     * @param {TNodeGuardFactory} nodeGuardFactory
      * @param {TNodeTransformerFactory} nodeTransformerFactory
      * @param {ILogger} logger
      * @param {IOptions} options
@@ -117,6 +122,7 @@ export class Obfuscator implements IObfuscator {
         @inject(ServiceIdentifiers.IStackTraceAnalyzer) stackTraceAnalyzer: IStackTraceAnalyzer,
         @inject(ServiceIdentifiers.IObfuscationEventEmitter) obfuscationEventEmitter: IObfuscationEventEmitter,
         @inject(ServiceIdentifiers.TCustomNodeGroupStorage) customNodeGroupStorage: IStorage<ICustomNodeGroup>,
+        @inject(ServiceIdentifiers.Factory__INodeGuard) nodeGuardFactory: TNodeGuardFactory,
         @inject(ServiceIdentifiers.Factory__INodeTransformer) nodeTransformerFactory: TNodeTransformerFactory,
         @inject(ServiceIdentifiers.ILogger) logger: ILogger,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
@@ -124,27 +130,12 @@ export class Obfuscator implements IObfuscator {
         this.stackTraceAnalyzer = stackTraceAnalyzer;
         this.obfuscationEventEmitter = obfuscationEventEmitter;
         this.customNodeGroupStorage = customNodeGroupStorage;
+        this.nodeGuardFactory = nodeGuardFactory;
         this.nodeTransformerFactory = nodeTransformerFactory;
         this.logger = logger;
         this.options = options;
     }
 
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    private static isBlackListNode (node: ESTree.Node): boolean {
-        const guardsLength: number = Obfuscator.blackListGuards.length;
-
-        for (let i: number = 0; i < guardsLength; i++) {
-            if (Obfuscator.blackListGuards[i](node)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     /**
      * @param {Program} astTree
      * @returns {Program}
@@ -250,7 +241,10 @@ export class Obfuscator implements IObfuscator {
         }
 
         return (node: ESTree.Node, parentNode: ESTree.Node) => {
-            if (Obfuscator.isBlackListNode(node)) {
+            const obfuscationEnabled: boolean = Obfuscator.nodeGuardsList
+                .every((nodeGuardName: NodeGuard) => this.nodeGuardFactory(nodeGuardName).check(node));
+
+            if (!obfuscationEnabled) {
                 return estraverse.VisitorOption.Skip;
             }
 

+ 3 - 1
src/container/InversifyContainerFacade.ts

@@ -4,8 +4,9 @@ import { ServiceIdentifiers } from './ServiceIdentifiers';
 import { analyzersModule } from './modules/analyzers/AnalyzersModule';
 import { controlFlowTransformersModule } from './modules/node-transformers/ControlFlowTransformersModule';
 import { customNodesModule } from './modules/custom-nodes/CustomNodesModule';
-import { obfuscatingTransformersModule } from './modules/node-transformers/ObfuscatingTransformersModule';
+import { nodeGuardsModule } from './modules/node-guards/NodeGuardsModule';
 import { nodeTransformersModule } from './modules/node-transformers/NodeTransformersModule';
+import { obfuscatingTransformersModule } from './modules/node-transformers/ObfuscatingTransformersModule';
 import { storagesModule } from './modules/storages/StoragesModule';
 import { utilsModule } from './modules/utils/UtilsModule';
 
@@ -194,6 +195,7 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
         this.container.load(analyzersModule);
         this.container.load(controlFlowTransformersModule);
         this.container.load(customNodesModule);
+        this.container.load(nodeGuardsModule);
         this.container.load(nodeTransformersModule);
         this.container.load(obfuscatingTransformersModule);
         this.container.load(storagesModule);

+ 2 - 0
src/container/ServiceIdentifiers.ts

@@ -5,6 +5,7 @@ export enum ServiceIdentifiers {
     Factory__ICustomNode = 'Factory<ICustomNode>',
     Factory__ICustomNodeGroup = 'Factory<ICustomNodeGroup>',
     Factory__IIdentifierObfuscatingReplacer = 'Factory<IIdentifierObfuscatingReplacer>',
+    Factory__INodeGuard = 'Factory<INodeGuard>',
     Factory__INodeTransformer = 'Factory<INodeTransformer[]>',
     Factory__IObfuscationResult = 'Factory<IObfuscationResult>',
     Factory__IObfuscatingReplacer = 'Factory<IObfuscatingReplacer>',
@@ -20,6 +21,7 @@ export enum ServiceIdentifiers {
     IIdentifierObfuscatingReplacer = 'IIdentifierObfuscatingReplacer',
     IJavaScriptObfuscator = 'IJavaScriptObfuscator',
     ILogger = 'ILogger',
+    INodeGuard = 'INodeGuard',
     INodeTransformer = 'INodeTransformer',
     IObfuscationEventEmitter = 'IObfuscationEventEmitter',
     IObfuscationResult = 'IObfuscationResult',

+ 30 - 0
src/container/modules/node-guards/NodeGuardsModule.ts

@@ -0,0 +1,30 @@
+import { InversifyContainerFacade } from '../../InversifyContainerFacade';
+import { ContainerModule, interfaces } from 'inversify';
+import { ServiceIdentifiers } from '../../ServiceIdentifiers';
+
+import { INodeGuard } from '../../../interfaces/node-guards/INodeGuard';
+
+import { NodeGuard } from '../../../enums/container/node-guards/NodeGuard';
+
+import { BlackListNodeGuard } from '../../../node-guards/BlackListNodeGuard';
+import { ConditionalCommentNodeGuard } from '../../../node-guards/ConditionalCommentNodeGuard';
+
+export const nodeGuardsModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
+    // node guards
+    bind<INodeGuard>(ServiceIdentifiers.INodeGuard)
+        .to(BlackListNodeGuard)
+        .inSingletonScope()
+        .whenTargetNamed(NodeGuard.BlackListNodeGuard);
+
+    bind<INodeGuard>(ServiceIdentifiers.INodeGuard)
+        .to(ConditionalCommentNodeGuard)
+        .inSingletonScope()
+        .whenTargetNamed(NodeGuard.ConditionalCommentNodeGuard);
+
+    // node guards factory
+    bind<INodeGuard>(ServiceIdentifiers.Factory__INodeGuard)
+        .toFactory<INodeGuard>(InversifyContainerFacade
+            .getCacheFactory<NodeGuard, INodeGuard>(
+                ServiceIdentifiers.INodeGuard
+            ));
+});

+ 9 - 0
src/declarations/esprima.d.ts

@@ -0,0 +1,9 @@
+/* tslint:disable:interface-name */
+
+import * as esprima from 'esprima';
+
+declare module 'esprima' {
+    export interface ParseOptions {
+        attachComment?: boolean;
+    }
+}

+ 4 - 0
src/enums/container/node-guards/NodeGuard.ts

@@ -0,0 +1,4 @@
+export enum NodeGuard {
+    BlackListNodeGuard,
+    ConditionalCommentNodeGuard
+}

+ 9 - 0
src/interfaces/node-guards/INodeGuard.ts

@@ -0,0 +1,9 @@
+import { TNodeGuard } from '../../types/node/TNodeGuard';
+
+export interface INodeGuard {
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    check: TNodeGuard;
+}

+ 42 - 0
src/node-guards/BlackListNodeGuard.ts

@@ -0,0 +1,42 @@
+import { injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { TNodeGuard } from '../types/node/TNodeGuard';
+
+import { INodeGuard } from '../interfaces/node-guards/INodeGuard';
+
+import { Node } from '../node/Node';
+
+@injectable()
+export class BlackListNodeGuard implements INodeGuard {
+    /**
+     * @type {((node: Node) => boolean)[]}
+     */
+    private static readonly blackListGuards: TNodeGuard[] = [
+        Node.isUseStrictOperator
+    ];
+
+    /**
+     * @type {number}
+     */
+    private readonly blackListGuardsLength: number;
+
+    constructor () {
+        this.blackListGuardsLength = BlackListNodeGuard.blackListGuards.length;
+    }
+
+    /**
+     * @returns {boolean}
+     * @param node
+     */
+    public check (node: ESTree.Node): boolean {
+        for (let i: number = 0; i < this.blackListGuardsLength; i++) {
+            if (BlackListNodeGuard.blackListGuards[i](node)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}

+ 82 - 0
src/node-guards/ConditionalCommentNodeGuard.ts

@@ -0,0 +1,82 @@
+import { injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { INodeGuard } from '../interfaces/node-guards/INodeGuard';
+
+@injectable()
+export class ConditionalCommentNodeGuard implements INodeGuard {
+    /**
+     * @type {RegExp}
+     */
+    private static readonly obfuscationEnableCommentRegExp: RegExp = new RegExp('javascript-obfuscator *: *enable');
+
+    /**
+     * @type {RegExp}
+     */
+    private static readonly obfuscationDisableCommentRegExp: RegExp = new RegExp('javascript-obfuscator *: *disable');
+
+    /**
+     * @type {boolean}
+     */
+    private obfuscationEnabledForCurrentNode: boolean = true;
+
+    /**
+     * @type {boolean}
+     */
+    private obfuscationEnabledForNextNode: boolean | null = null;
+
+    /**
+     * @returns {boolean}
+     * @param node
+     */
+    public check (node: ESTree.Node): boolean {
+        if (this.obfuscationEnabledForNextNode) {
+            this.obfuscationEnabledForCurrentNode = this.obfuscationEnabledForNextNode;
+            this.obfuscationEnabledForNextNode = null;
+        }
+
+        if (!node.leadingComments && !node.trailingComments) {
+            return this.obfuscationEnabledForCurrentNode;
+        }
+
+        const leadingComments: ESTree.Comment[] | undefined = node.leadingComments;
+        const trailingComments: ESTree.Comment[] | undefined = node.trailingComments;
+
+        if (leadingComments) {
+            this.obfuscationEnabledForCurrentNode = this.checkComments(leadingComments);
+        }
+
+        if (trailingComments) {
+            this.obfuscationEnabledForNextNode = this.checkComments(trailingComments);
+        }
+
+        return this.obfuscationEnabledForCurrentNode;
+    }
+
+    /**
+     * @param {Comment[]} comments
+     * @returns {boolean}
+     */
+    private checkComments (comments: ESTree.Comment[]): boolean {
+        const commentsLength: number = comments.length;
+
+        let obfuscationEnabled: boolean = this.obfuscationEnabledForCurrentNode;
+
+        for (let i: number = 0; i < commentsLength; i++) {
+            const comment: ESTree.Comment = comments[i];
+
+            if (ConditionalCommentNodeGuard.obfuscationEnableCommentRegExp.test(comment.value)) {
+                obfuscationEnabled = true;
+
+                continue;
+            }
+
+            if (ConditionalCommentNodeGuard.obfuscationDisableCommentRegExp.test(comment.value)) {
+                obfuscationEnabled = false;
+            }
+        }
+
+        return obfuscationEnabled;
+    }
+}

+ 5 - 0
src/types/container/node-guards/TNodeGuardFactory.d.ts

@@ -0,0 +1,5 @@
+import { INodeGuard } from '../../../interfaces/node-guards/INodeGuard';
+
+import { NodeGuard } from '../../../enums/container/node-guards/NodeGuard';
+
+export type TNodeGuardFactory = (nodeGuard: NodeGuard) => INodeGuard;

+ 0 - 34
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -441,40 +441,6 @@ describe('JavaScriptObfuscator', () => {
             });
         });
 
-        describe('`\'use strict\';` operator', () => {
-            const useStrictOperatorRegExp: RegExp = /'use *strict';/;
-            const stringArrayLatinRegExp: RegExp = /var _0x(\w){4} *= *\['abc'\];/;
-            const stringArrayCallRegExp: RegExp = /var *test *= *_0x(\w){4}\('0x0'\);$/;
-
-            let obfuscatedCode: string;
-
-            beforeEach(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/use-strict-operator.js');
-                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
-                    code,
-                    {
-                        ...NO_CUSTOM_NODES_PRESET,
-                        stringArray: true,
-                        stringArrayThreshold: 1
-                    }
-                );
-
-                obfuscatedCode = obfuscationResult.getObfuscatedCode();
-            });
-
-            it('match #1: shouldn\'t obfuscate `use strict` operator', () => {
-                assert.match(obfuscatedCode, useStrictOperatorRegExp);
-            });
-
-            it('match #2: should return correct obfuscated code', () => {
-                assert.match(obfuscatedCode, stringArrayLatinRegExp);
-            });
-
-            it('match #3: should return correct obfuscated code', () => {
-                assert.match(obfuscatedCode, stringArrayCallRegExp);
-            });
-        });
-
         describe('mangle', () => {
             const regExp: RegExp = /var *a *= *0x1/;
 

+ 47 - 0
test/functional-tests/node-guards/black-list-node-guard/BlackListNodeGuard.spec.ts

@@ -0,0 +1,47 @@
+import { assert } from 'chai';
+
+import { IObfuscationResult } from '../../../../src/interfaces/IObfuscationResult';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscator';
+
+import { NO_CUSTOM_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+
+import { readFileAsString } from '../../../helpers/readFileAsString';
+
+describe('BlackListNodeGuard', () => {
+    describe('check (node: ESTree.Node): boolean', () => {
+        describe('`\'use strict\';` operator', () => {
+            const useStrictOperatorRegExp: RegExp = /'use *strict';/;
+            const stringArrayLatinRegExp: RegExp = /var _0x(\w){4} *= *\['abc'\];/;
+            const stringArrayCallRegExp: RegExp = /var *test *= *_0x(\w){4}\('0x0'\);$/;
+
+            let obfuscatedCode: string;
+
+            beforeEach(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/use-strict-operator.js');
+                const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_CUSTOM_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                );
+
+                obfuscatedCode = obfuscationResult.getObfuscatedCode();
+            });
+
+            it('match #1: shouldn\'t obfuscate `use strict` operator', () => {
+                assert.match(obfuscatedCode, useStrictOperatorRegExp);
+            });
+
+            it('match #2: should return correct obfuscated code', () => {
+                assert.match(obfuscatedCode, stringArrayLatinRegExp);
+            });
+
+            it('match #3: should return correct obfuscated code', () => {
+                assert.match(obfuscatedCode, stringArrayCallRegExp);
+            });
+        });
+    });
+});

+ 0 - 0
test/functional-tests/javascript-obfuscator/fixtures/use-strict-operator.js → test/functional-tests/node-guards/black-list-node-guard/fixtures/use-strict-operator.js


+ 1 - 0
test/index.spec.ts

@@ -39,6 +39,7 @@ import './functional-tests/custom-nodes/string-array-nodes/StringArrayRotateFunc
 import './functional-tests/custom-nodes/string-array-nodes/StringArrayNode.spec';
 import './functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec';
 import './functional-tests/javascript-obfuscator-internal/JavaScriptObfuscatorInternal.spec';
+import './functional-tests/node-guards/black-list-node-guard/BlackListNodeGuard.spec';
 import './functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/BlockStatementControlFlowTransformer.spec';
 import './functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/FunctionControlFlowTransformer.spec';
 import './functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer/BinaryExpressionControlFlowReplacer.spec';

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