From 6bf5ba7bda65e9fb17dbf965dbc822b80c7fa2f7 Mon Sep 17 00:00:00 2001 From: sanex3339 Date: Tue, 8 Nov 2016 17:21:14 +0300 Subject: [PATCH] some optimisations and new tests --- dist/index.js | 34 +++++++---- src/Obfuscator.ts | 36 +++++++---- .../stack-trace-analyzer/ICalleeData.d.ts | 2 +- .../VariableDeclarationObfuscator.ts | 4 +- .../ObjectExpressionCalleeDataExtractor.ts | 59 ++++++++++++------- src/types/TObjectMembersCallsChain.d.ts | 1 + ... => call-expression-of-object-member-1.js} | 0 .../call-expression-of-object-member-2.js | 28 +++++++++ .../StackTraceAnalyzer.spec.ts | 31 +++++++++- 9 files changed, 145 insertions(+), 50 deletions(-) create mode 100644 src/types/TObjectMembersCallsChain.d.ts rename test/fixtures/stack-trace-analyzer/{call-expression-of-object-member.js => call-expression-of-object-member-1.js} (100%) create mode 100644 test/fixtures/stack-trace-analyzer/call-expression-of-object-member-2.js diff --git a/dist/index.js b/dist/index.js index a40e1fad..08c56c78 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1454,6 +1454,7 @@ var Obfuscator = function () { _classCallCheck(this, Obfuscator); this.customNodes = new Map(); + this.obfuscatorsCache = new Map(); this.options = options; } @@ -1494,13 +1495,15 @@ var Obfuscator = function () { value: function initializeCustomNodes(stackTraceData) { var _this = this; + var customNodes = []; Obfuscator.nodeGroups.forEach(function (nodeGroupConstructor) { var nodeGroupNodes = new nodeGroupConstructor(stackTraceData, _this.options).getNodes(); if (!nodeGroupNodes) { return; } - _this.customNodes = new Map([].concat(_toConsumableArray(_this.customNodes), _toConsumableArray(nodeGroupNodes))); + customNodes.push.apply(customNodes, _toConsumableArray(nodeGroupNodes)); }); + this.customNodes = new Map(customNodes); } }, { key: 'initializeNodeObfuscators', @@ -1512,7 +1515,12 @@ var Obfuscator = function () { return; } nodeObfuscators.forEach(function (obfuscator) { - new obfuscator(_this2.customNodes, _this2.options).obfuscateNode(node, parentNode); + var cachedObfuscator = _this2.obfuscatorsCache.get(obfuscator); + if (!cachedObfuscator) { + cachedObfuscator = new obfuscator(_this2.customNodes, _this2.options); + _this2.obfuscatorsCache.set(obfuscator, cachedObfuscator); + } + cachedObfuscator.obfuscateNode(node, parentNode); }); } }, { @@ -1520,7 +1528,7 @@ var Obfuscator = function () { value: function obfuscate(node) { var _this3 = this; - estraverse.replace(node, { + estraverse.traverse(node, { enter: function enter(node, parentNode) { _this3.initializeNodeObfuscators(node, parentNode); } @@ -1531,6 +1539,7 @@ var Obfuscator = function () { return Obfuscator; }(); +Obfuscator.counter = 0; Obfuscator.nodeGroups = [DomainLockNodesGroup_1.DomainLockNodesGroup, SelfDefendingNodesGroup_1.SelfDefendingNodesGroup, ConsoleOutputNodesGroup_1.ConsoleOutputNodesGroup, DebugProtectionNodesGroup_1.DebugProtectionNodesGroup, UnicodeArrayNodesGroup_1.UnicodeArrayNodesGroup]; Obfuscator.nodeObfuscators = new Map([[NodeType_1.NodeType.ArrowFunctionExpression, [FunctionObfuscator_1.FunctionObfuscator]], [NodeType_1.NodeType.ClassDeclaration, [FunctionDeclarationObfuscator_1.FunctionDeclarationObfuscator]], [NodeType_1.NodeType.CatchClause, [CatchClauseObfuscator_1.CatchClauseObfuscator]], [NodeType_1.NodeType.FunctionDeclaration, [FunctionDeclarationObfuscator_1.FunctionDeclarationObfuscator, FunctionObfuscator_1.FunctionObfuscator]], [NodeType_1.NodeType.FunctionExpression, [FunctionObfuscator_1.FunctionObfuscator]], [NodeType_1.NodeType.MemberExpression, [MemberExpressionObfuscator_1.MemberExpressionObfuscator]], [NodeType_1.NodeType.MethodDefinition, [MethodDefinitionObfuscator_1.MethodDefinitionObfuscator]], [NodeType_1.NodeType.ObjectExpression, [ObjectExpressionObfuscator_1.ObjectExpressionObfuscator]], [NodeType_1.NodeType.VariableDeclaration, [VariableDeclarationObfuscator_1.VariableDeclarationObfuscator]], [NodeType_1.NodeType.Literal, [LiteralObfuscator_1.LiteralObfuscator]]]); exports.Obfuscator = Obfuscator; @@ -3971,7 +3980,6 @@ var ObjectExpressionCalleeDataExtractor = function () { function ObjectExpressionCalleeDataExtractor(blockScopeBody, callee) { _classCallCheck(this, ObjectExpressionCalleeDataExtractor); - this.objectMembersCallsChain = []; this.blockScopeBody = blockScopeBody; this.callee = callee; } @@ -3982,12 +3990,12 @@ var ObjectExpressionCalleeDataExtractor = function () { var calleeBlockStatement = null, functionExpressionName = null; if (Nodes_1.Nodes.isMemberExpressionNode(this.callee)) { - this.objectMembersCallsChain = this.createObjectMembersCallsChain(this.objectMembersCallsChain, this.callee); - if (!this.objectMembersCallsChain.length) { + var objectMembersCallsChain = this.createObjectMembersCallsChain([], this.callee); + if (!objectMembersCallsChain.length) { return null; } - functionExpressionName = this.objectMembersCallsChain[this.objectMembersCallsChain.length - 1]; - calleeBlockStatement = this.getCalleeBlockStatement(NodeUtils_1.NodeUtils.getBlockScopeOfNode(this.blockScopeBody[0]), this.objectMembersCallsChain); + functionExpressionName = objectMembersCallsChain[objectMembersCallsChain.length - 1]; + calleeBlockStatement = this.getCalleeBlockStatement(NodeUtils_1.NodeUtils.getBlockScopeOfNode(this.blockScopeBody[0]), objectMembersCallsChain); } if (!calleeBlockStatement) { return null; @@ -4000,17 +4008,16 @@ var ObjectExpressionCalleeDataExtractor = function () { }, { key: 'createObjectMembersCallsChain', value: function createObjectMembersCallsChain(currentChain, memberExpression) { - if (Nodes_1.Nodes.isIdentifierNode(memberExpression.property)) { + if (Nodes_1.Nodes.isIdentifierNode(memberExpression.property) && memberExpression.computed === false) { currentChain.unshift(memberExpression.property.name); - } else if (Nodes_1.Nodes.isLiteralNode(memberExpression.property) && typeof memberExpression.property.value === 'string') { + } else if (Nodes_1.Nodes.isLiteralNode(memberExpression.property) && (typeof memberExpression.property.value === 'string' || typeof memberExpression.property.value === 'number')) { currentChain.unshift(memberExpression.property.value); } else { return currentChain; } if (Nodes_1.Nodes.isMemberExpressionNode(memberExpression.object)) { return this.createObjectMembersCallsChain(currentChain, memberExpression.object); - } - if (Nodes_1.Nodes.isIdentifierNode(memberExpression.object)) { + } else if (Nodes_1.Nodes.isIdentifierNode(memberExpression.object)) { currentChain.unshift(memberExpression.object.name); } return currentChain; @@ -4021,6 +4028,9 @@ var ObjectExpressionCalleeDataExtractor = function () { var _this = this; var objectName = objectMembersCallsChain.shift(); + if (!objectName) { + return null; + } var calleeBlockStatement = null; estraverse.traverse(node, { enter: function enter(node, parentNode) { diff --git a/src/Obfuscator.ts b/src/Obfuscator.ts index 9809cbd3..1037527c 100644 --- a/src/Obfuscator.ts +++ b/src/Obfuscator.ts @@ -1,14 +1,15 @@ import * as estraverse from 'estraverse'; import * as ESTree from 'estree'; +import { TNodeGroup } from './types/TNodeGroup'; +import { TNodeObfuscator } from './types/TNodeObfuscator'; + import { ICustomNode } from './interfaces/custom-nodes/ICustomNode'; +import { INodeObfuscator } from './interfaces/INodeObfuscator'; import { IObfuscator } from './interfaces/IObfuscator'; import { IOptions } from './interfaces/IOptions'; import { IStackTraceData } from './interfaces/stack-trace-analyzer/IStackTraceData'; -import { TNodeGroup } from './types/TNodeGroup'; -import { TNodeObfuscator } from './types/TNodeObfuscator'; - import { AppendState } from './enums/AppendState'; import { NodeType } from './enums/NodeType'; @@ -30,6 +31,8 @@ import { VariableDeclarationObfuscator } from './node-obfuscators/VariableDeclar import { StackTraceAnalyzer } from './stack-trace-analyzer/StackTraceAnalyzer'; export class Obfuscator implements IObfuscator { + static counter: number = 0; + /** * @type {TNodeGroup[]} */ @@ -65,6 +68,11 @@ export class Obfuscator implements IObfuscator { */ private customNodes: Map = new Map (); + /** + * @type {Map} + */ + private obfuscatorsCache: Map = new Map (); + /** * @type {IOptions} */ @@ -125,6 +133,8 @@ export class Obfuscator implements IObfuscator { * @param stackTraceData */ private initializeCustomNodes (stackTraceData: IStackTraceData[]): void { + let customNodes: [string, ICustomNode][] = []; + Obfuscator.nodeGroups.forEach((nodeGroupConstructor: TNodeGroup) => { const nodeGroupNodes: Map | undefined = new nodeGroupConstructor( stackTraceData, this.options @@ -134,11 +144,10 @@ export class Obfuscator implements IObfuscator { return; } - this.customNodes = new Map ([ - ...this.customNodes, - ...nodeGroupNodes - ]); + customNodes.push(...nodeGroupNodes); }); + + this.customNodes = new Map (customNodes); } @@ -154,7 +163,14 @@ export class Obfuscator implements IObfuscator { } nodeObfuscators.forEach((obfuscator: TNodeObfuscator) => { - new obfuscator(this.customNodes, this.options).obfuscateNode(node, parentNode); + let cachedObfuscator: INodeObfuscator|undefined = this.obfuscatorsCache.get(obfuscator); + + if (!cachedObfuscator) { + cachedObfuscator = new obfuscator(this.customNodes, this.options); + this.obfuscatorsCache.set(obfuscator, cachedObfuscator); + } + + cachedObfuscator.obfuscateNode(node, parentNode); }); } @@ -162,8 +178,8 @@ export class Obfuscator implements IObfuscator { * @param node */ private obfuscate (node: ESTree.Node): void { - estraverse.replace(node, { - enter: (node: ESTree.Node, parentNode: ESTree.Node): any => { + estraverse.traverse(node, { + enter: (node: ESTree.Node, parentNode: ESTree.Node): void => { this.initializeNodeObfuscators(node, parentNode); } }); diff --git a/src/interfaces/stack-trace-analyzer/ICalleeData.d.ts b/src/interfaces/stack-trace-analyzer/ICalleeData.d.ts index 30a2a8c5..7f5d1313 100644 --- a/src/interfaces/stack-trace-analyzer/ICalleeData.d.ts +++ b/src/interfaces/stack-trace-analyzer/ICalleeData.d.ts @@ -2,5 +2,5 @@ import * as ESTree from 'estree'; export interface ICalleeData { callee: ESTree.BlockStatement; - name: string | null; + name: string | number | null; } diff --git a/src/node-obfuscators/VariableDeclarationObfuscator.ts b/src/node-obfuscators/VariableDeclarationObfuscator.ts index 3ed28142..79609c51 100644 --- a/src/node-obfuscators/VariableDeclarationObfuscator.ts +++ b/src/node-obfuscators/VariableDeclarationObfuscator.ts @@ -68,8 +68,8 @@ export class VariableDeclarationObfuscator extends AbstractNodeObfuscator { */ private replaceVariableNames (variableDeclarationNode: ESTree.VariableDeclaration, variableParentNode: ESTree.Node): void { let scopeNode: ESTree.Node = variableDeclarationNode.kind === 'var' ? NodeUtils.getBlockScopeOfNode( - variableDeclarationNode - ) : variableParentNode; + variableDeclarationNode + ) : variableParentNode; estraverse.replace(scopeNode, { enter: (node: ESTree.Node, parentNode: ESTree.Node): any => { diff --git a/src/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts b/src/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts index a7f41974..ba9ee8e1 100644 --- a/src/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts +++ b/src/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts @@ -1,6 +1,8 @@ import * as estraverse from 'estraverse'; import * as ESTree from 'estree'; +import { TObjectMembersCallsChain } from '../../types/TObjectMembersCallsChain'; + import { ICalleeData } from '../../interfaces/stack-trace-analyzer/ICalleeData'; import { ICalleeDataExtractor } from '../../interfaces/stack-trace-analyzer/ICalleeDataExtractor'; @@ -18,11 +20,6 @@ export class ObjectExpressionCalleeDataExtractor implements ICalleeDataExtractor */ private callee: ESTree.MemberExpression; - /** - * @type {Array} - */ - private objectMembersCallsChain: string[] = []; - /** * @param blockScopeBody * @param callee @@ -37,20 +34,22 @@ export class ObjectExpressionCalleeDataExtractor implements ICalleeDataExtractor */ public extract (): ICalleeData|null { let calleeBlockStatement: ESTree.BlockStatement|null = null, - functionExpressionName: string|null = null; + functionExpressionName: string|number|null = null; if (Nodes.isMemberExpressionNode(this.callee)) { - this.objectMembersCallsChain = this.createObjectMembersCallsChain(this.objectMembersCallsChain, this.callee); + const objectMembersCallsChain: TObjectMembersCallsChain = this.createObjectMembersCallsChain( + [], + this.callee + ); - if (!this.objectMembersCallsChain.length) { + if (!objectMembersCallsChain.length) { return null; } - functionExpressionName = this.objectMembersCallsChain[this.objectMembersCallsChain.length - 1]; - + functionExpressionName = objectMembersCallsChain[objectMembersCallsChain.length - 1]; calleeBlockStatement = this.getCalleeBlockStatement( NodeUtils.getBlockScopeOfNode(this.blockScopeBody[0]), - this.objectMembersCallsChain + objectMembersCallsChain ); } @@ -71,22 +70,31 @@ export class ObjectExpressionCalleeDataExtractor implements ICalleeDataExtractor * * @param currentChain * @param memberExpression - * @returns {string[]} + * @returns {TObjectMembersCallsChain} */ - private createObjectMembersCallsChain (currentChain: string[], memberExpression: ESTree.MemberExpression): string[] { - if (Nodes.isIdentifierNode(memberExpression.property)) { + private createObjectMembersCallsChain ( + currentChain: TObjectMembersCallsChain, + memberExpression: ESTree.MemberExpression + ): TObjectMembersCallsChain { + // first step: processing memberExpression `property` property + if (Nodes.isIdentifierNode(memberExpression.property) && memberExpression.computed === false) { currentChain.unshift(memberExpression.property.name); - } else if (Nodes.isLiteralNode(memberExpression.property) && typeof memberExpression.property.value === 'string') { + } else if ( + Nodes.isLiteralNode(memberExpression.property) && + ( + typeof memberExpression.property.value === 'string' || + typeof memberExpression.property.value === 'number' + ) + ) { currentChain.unshift(memberExpression.property.value); } else { return currentChain; } + // second step: processing memberExpression `object` property if (Nodes.isMemberExpressionNode(memberExpression.object)) { return this.createObjectMembersCallsChain(currentChain, memberExpression.object); - } - - if (Nodes.isIdentifierNode(memberExpression.object)) { + } else if (Nodes.isIdentifierNode(memberExpression.object)) { currentChain.unshift(memberExpression.object.name); } @@ -98,8 +106,15 @@ export class ObjectExpressionCalleeDataExtractor implements ICalleeDataExtractor * @param objectMembersCallsChain * @returns {ESTree.BlockStatement|null} */ - private getCalleeBlockStatement (node: ESTree.Node, objectMembersCallsChain: string[]): ESTree.BlockStatement|null { - const objectName: string = objectMembersCallsChain.shift(); + private getCalleeBlockStatement ( + node: ESTree.Node, + objectMembersCallsChain: TObjectMembersCallsChain + ): ESTree.BlockStatement|null { + const objectName: string|number|undefined = objectMembersCallsChain.shift(); + + if (!objectName) { + return null; + } let calleeBlockStatement: ESTree.BlockStatement|null = null; @@ -129,9 +144,9 @@ export class ObjectExpressionCalleeDataExtractor implements ICalleeDataExtractor */ private findCalleeBlockStatement ( objectExpressionProperties: ESTree.Property[], - objectMembersCallsChain: string[] + objectMembersCallsChain: TObjectMembersCallsChain ): ESTree.BlockStatement|null { - const nextItemInCallsChain: string|undefined = objectMembersCallsChain.shift(); + const nextItemInCallsChain: string|number|undefined = objectMembersCallsChain.shift(); if (!nextItemInCallsChain) { return null; diff --git a/src/types/TObjectMembersCallsChain.d.ts b/src/types/TObjectMembersCallsChain.d.ts new file mode 100644 index 00000000..8f14c018 --- /dev/null +++ b/src/types/TObjectMembersCallsChain.d.ts @@ -0,0 +1 @@ +export type TObjectMembersCallsChain = (string|number)[]; diff --git a/test/fixtures/stack-trace-analyzer/call-expression-of-object-member.js b/test/fixtures/stack-trace-analyzer/call-expression-of-object-member-1.js similarity index 100% rename from test/fixtures/stack-trace-analyzer/call-expression-of-object-member.js rename to test/fixtures/stack-trace-analyzer/call-expression-of-object-member-1.js diff --git a/test/fixtures/stack-trace-analyzer/call-expression-of-object-member-2.js b/test/fixtures/stack-trace-analyzer/call-expression-of-object-member-2.js new file mode 100644 index 00000000..858d31a4 --- /dev/null +++ b/test/fixtures/stack-trace-analyzer/call-expression-of-object-member-2.js @@ -0,0 +1,28 @@ +var object = { + foo: { + bar: function () { + + }, + }, + 1: { + baz: function () { + + }, + } +}; + + +var object1 = { + foo: { + 1: function () { + + } + } +}; + +var foo = 'foo'; + +object[foo].bar(); +object[1].baz(); + +object1['foo'][1](); \ No newline at end of file diff --git a/test/functional-tests/stack-trace-analyzer/StackTraceAnalyzer.spec.ts b/test/functional-tests/stack-trace-analyzer/StackTraceAnalyzer.spec.ts index 64987716..b0eb8b5e 100644 --- a/test/functional-tests/stack-trace-analyzer/StackTraceAnalyzer.spec.ts +++ b/test/functional-tests/stack-trace-analyzer/StackTraceAnalyzer.spec.ts @@ -99,7 +99,7 @@ function getFunctionExpressionById (astTree: ESTree.Node, id: string): ESTree.Fu * @param name * @returns {ESTree.FunctionExpression|null} */ -function getObjectFunctionExpressionByName (astTree: ESTree.Node, objectName: string, name: string): ESTree.FunctionExpression|null { +function getObjectFunctionExpressionByName (astTree: ESTree.Node, objectName: string, name: string|number): ESTree.FunctionExpression|null { let functionExpressionNode: ESTree.FunctionExpression|null = null, targetObjectExpressionNode: ESTree.ObjectExpression|null = null; @@ -288,10 +288,10 @@ describe('StackTraceAnalyzer', () => { assert.deepEqual(stackTraceData, expectedStackTraceData); }); - it('should returns correct BlockScopeTraceData - variant #5: call expression of object member', () => { + it('should returns correct BlockScopeTraceData - variant #5: call expression of object member #1', () => { astTree = NodeMocks.getProgramNode( NodeUtils.convertCodeToStructure( - readFileAsString('./test/fixtures/stack-trace-analyzer/call-expression-of-object-member.js') + readFileAsString('./test/fixtures/stack-trace-analyzer/call-expression-of-object-member-1.js') ) ); @@ -344,6 +344,31 @@ describe('StackTraceAnalyzer', () => { assert.deepEqual(stackTraceData, expectedStackTraceData); }); + it('should returns correct BlockScopeTraceData - variant #5: call expression of object member #2', () => { + astTree = NodeMocks.getProgramNode( + NodeUtils.convertCodeToStructure( + readFileAsString('./test/fixtures/stack-trace-analyzer/call-expression-of-object-member-2.js') + ) + ); + + expectedStackTraceData = [ + { + name: 'baz', + callee: (getObjectFunctionExpressionByName(astTree, 'object', 'baz')).body, + stackTrace: [] + }, + { + name: 1, + callee: (getObjectFunctionExpressionByName(astTree, 'object1', 1)).body, + stackTrace: [] + }, + ]; + + stackTraceData = new StackTraceAnalyzer(astTree.body).analyze(); + + assert.deepEqual(stackTraceData, expectedStackTraceData); + }); + it('should returns correct BlockScopeTraceData - variant #6: no call expressions', () => { astTree = NodeMocks.getProgramNode( NodeUtils.convertCodeToStructure(