diff --git a/CHANGELOG.md b/CHANGELOG.md index 0df82e55..baedc626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ Change Log +v3.2.6 +--- +* Improved integration between `renameProperties` and `controlFlowFlattening` options. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1053 + v3.2.5 --- * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1056 diff --git a/package.json b/package.json index eb31e247..d7a40196 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "javascript-obfuscator", - "version": "3.2.5", + "version": "3.2.6", "description": "JavaScript obfuscator", "keywords": [ "obfuscator", diff --git a/src/declarations/ESTree.d.ts b/src/declarations/ESTree.d.ts index a73321c0..b11c2b82 100644 --- a/src/declarations/ESTree.d.ts +++ b/src/declarations/ESTree.d.ts @@ -3,7 +3,6 @@ import * as acorn from 'acorn'; import * as escodegen from '@javascript-obfuscator/escodegen'; import * as eslintScope from 'eslint-scope'; -import { BlockStatement } from 'estree'; declare module 'estree' { /** @@ -14,8 +13,13 @@ declare module 'estree' { ignoredNode?: boolean; } + export interface IdentifierNodeMetadata extends BaseNodeMetadata { + propertyKeyToRenameNode?: boolean + } + export interface LiteralNodeMetadata extends BaseNodeMetadata { stringArrayCallLiteralNode?: boolean; + propertyKeyToRenameNode?: boolean } /** @@ -40,6 +44,10 @@ declare module 'estree' { scope?: eslintScope.Scope | null; } + interface Identifier extends BaseNode { + metadata?: IdentifierNodeMetadata; + } + interface BigIntLiteral extends BaseNode { metadata?: LiteralNodeMetadata; 'x-verbatim-property'?: escodegen.XVerbatimProperty; diff --git a/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts index 29bfd06c..0528e895 100644 --- a/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts @@ -27,11 +27,6 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene */ private static readonly initMangledNameCharacter: string = '9'; - /** - * @type {WeakMap} - */ - private static readonly lastMangledNameInScopeMap: WeakMap = new WeakMap(); - /** * @type {string[]} */ @@ -47,16 +42,21 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene */ private static readonly reservedNamesSet: Set = new Set(reservedIdentifierNames); + /** + * @type {string} + */ + private lastMangledName: string = MangledIdentifierNamesGenerator.initMangledNameCharacter; + + /** + * @type {WeakMap} + */ + private readonly lastMangledNameForScopeMap: WeakMap = new WeakMap(); + /** * @type {WeakMap} */ private readonly lastMangledNameForLabelMap: Map = new Map(); - /** - * @type {string} - */ - private previousMangledName: string = MangledIdentifierNamesGenerator.initMangledNameCharacter; - /** * @type {ISetUtils} */ @@ -85,7 +85,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene * @returns {string} */ public generateNext (nameLength?: number): string { - const identifierName: string = this.generateNewMangledName(this.previousMangledName); + const identifierName: string = this.generateNewMangledName(this.lastMangledName); this.updatePreviousMangledName(identifierName); this.preserveName(identifierName); @@ -103,7 +103,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene : ''; const identifierName: string = this.generateNewMangledName( - this.previousMangledName, + this.lastMangledName, (newIdentifierName: string) => { const identifierNameWithPrefix: string = `${prefix}${newIdentifierName}`; @@ -136,7 +136,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene this.isValidIdentifierNameInLexicalScopes(newIdentifierName, lexicalScopes) ); - MangledIdentifierNamesGenerator.lastMangledNameInScopeMap.set(lexicalScopeNode, identifierName); + this.lastMangledNameForScopeMap.set(lexicalScopeNode, identifierName); this.updatePreviousMangledName(identifierName); this.preserveNameForLexicalScope(identifierName, lexicalScopeNode); @@ -216,11 +216,11 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene * @param {string} name */ protected updatePreviousMangledName (name: string): void { - if (!this.isIncrementedMangledName(name, this.previousMangledName)) { + if (!this.isIncrementedMangledName(name, this.lastMangledName)) { return; } - this.previousMangledName = name; + this.lastMangledName = name; } /** @@ -309,8 +309,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene */ private getLastMangledNameForScopes (lexicalScopeNodes: TNodeWithLexicalScope[]): string { for (const lexicalScope of lexicalScopeNodes) { - const lastMangledName: string | null = MangledIdentifierNamesGenerator.lastMangledNameInScopeMap - .get(lexicalScope) ?? null; + const lastMangledName: string | null = this.lastMangledNameForScopeMap.get(lexicalScope) ?? null; if (!lastMangledName) { continue; diff --git a/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts b/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts index 4f510184..fde1f972 100644 --- a/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/AbstractControlFlowReplacer.ts @@ -36,9 +36,9 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace protected readonly randomGenerator: IRandomGenerator; /** - * @type {Map>} + * @type {Map>} */ - protected readonly replacerDataByControlFlowStorageId: Map > = new Map(); + protected readonly replacerDataByControlFlowStorageId: Map > = new Map(); /** * @param {TControlFlowCustomNodeFactory} controlFlowCustomNodeFactory @@ -80,23 +80,23 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace /** * @param {ICustomNode} customNode * @param {IControlFlowStorage} controlFlowStorage - * @param {string} replacerId + * @param {string | number} replacerId * @param {number} usingExistingIdentifierChance * @returns {string} */ protected insertCustomNodeToControlFlowStorage ( customNode: ICustomNode, controlFlowStorage: IControlFlowStorage, - replacerId: string, + replacerId: string | number, usingExistingIdentifierChance: number ): string { const controlFlowStorageId: string = controlFlowStorage.getStorageId(); - const storageKeysById: Map = this.replacerDataByControlFlowStorageId.get(controlFlowStorageId) + const storageKeysById: Map = this.replacerDataByControlFlowStorageId.get(controlFlowStorageId) ?? new Map (); - const storageKeysForCurrentId: string[] | null = storageKeysById.get(replacerId) ?? null; + const storageKeysForCurrentId: string[] = storageKeysById.get(replacerId) ?? []; const shouldPickFromStorageKeysById = this.randomGenerator.getMathRandom() < usingExistingIdentifierChance - && storageKeysForCurrentId?.length; + && storageKeysForCurrentId.length; if (shouldPickFromStorageKeysById) { return this.randomGenerator.getRandomGenerator().pickone(storageKeysForCurrentId); @@ -104,7 +104,8 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace const storageKey: string = this.generateStorageKey(controlFlowStorage); - storageKeysById.set(replacerId, [storageKey]); + storageKeysForCurrentId.push(storageKey); + storageKeysById.set(replacerId, storageKeysForCurrentId); this.replacerDataByControlFlowStorageId.set(controlFlowStorageId, storageKeysById); controlFlowStorage.set(storageKey, customNode); diff --git a/src/node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer.ts b/src/node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer.ts index 7196b4d3..61824f68 100644 --- a/src/node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer.ts +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer.ts @@ -66,7 +66,7 @@ export class CallExpressionControlFlowReplacer extends AbstractControlFlowReplac return callExpressionNode; } - const replacerId: string = String(callExpressionNode.arguments.length); + const replacerId: number = callExpressionNode.arguments.length; const callExpressionFunctionCustomNode: ICustomNode> = this.controlFlowCustomNodeFactory(ControlFlowCustomNode.CallExpressionFunctionNode); const expressionArguments: (ESTree.Expression | ESTree.SpreadElement)[] = callExpressionNode.arguments; diff --git a/src/node-transformers/control-flow-transformers/control-flow-replacers/StringArrayCallControlFlowReplacer.ts b/src/node-transformers/control-flow-transformers/control-flow-replacers/StringArrayCallControlFlowReplacer.ts index cbe64f5e..0c90ec33 100644 --- a/src/node-transformers/control-flow-transformers/control-flow-replacers/StringArrayCallControlFlowReplacer.ts +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/StringArrayCallControlFlowReplacer.ts @@ -74,7 +74,7 @@ export class StringArrayCallControlFlowReplacer extends AbstractControlFlowRepla return literalNode; } - const replacerId: string = String(literalNode.value); + const replacerId: string | number = literalNode.value; const literalCustomNode: ICustomNode> = this.controlFlowCustomNodeFactory(ControlFlowCustomNode.LiteralNode); diff --git a/src/node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer.ts b/src/node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer.ts index 5aa05ff6..9ed65afa 100644 --- a/src/node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer.ts +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer.ts @@ -69,7 +69,7 @@ export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplace return literalNode; } - const replacerId: string = String(literalNode.value); + const replacerId: string = literalNode.value; const literalCustomNode: ICustomNode> = this.controlFlowCustomNodeFactory(ControlFlowCustomNode.LiteralNode); diff --git a/src/node-transformers/rename-properties-transformers/RenamePropertiesTransformer.ts b/src/node-transformers/rename-properties-transformers/RenamePropertiesTransformer.ts index 1dcbfdb9..55a321a1 100644 --- a/src/node-transformers/rename-properties-transformers/RenamePropertiesTransformer.ts +++ b/src/node-transformers/rename-properties-transformers/RenamePropertiesTransformer.ts @@ -14,7 +14,7 @@ import { RenamePropertiesMode } from '../../enums/node-transformers/rename-prope import { AbstractNodeTransformer } from '../AbstractNodeTransformer'; import { NodeGuards } from '../../node/NodeGuards'; import { NodeLiteralUtils } from '../../node/NodeLiteralUtils'; -import { NodeUtils } from '../../node/NodeUtils'; +import { NodeMetadata } from '../../node/NodeMetadata'; @injectable() export class RenamePropertiesTransformer extends AbstractNodeTransformer { @@ -48,7 +48,7 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer { | ESTree.PropertyDefinition | ESTree.MemberExpression | ESTree.MethodDefinition - > ( + > ( propertyNode: TNode, propertyKeyNode: ESTree.Expression | ESTree.PrivateIdentifier ): propertyKeyNode is ESTree.Identifier | ESTree.Literal { @@ -56,7 +56,7 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer { return false; } - return NodeGuards.isIdentifierNode(propertyKeyNode) || NodeGuards.isLiteralNode(propertyKeyNode); + return true; } /** @@ -96,6 +96,15 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer { node: ESTree.Node, parentNode: ESTree.Node ): void { + if ((NodeGuards.isPropertyNode(parentNode) && parentNode.key === node) + || NodeGuards.isMemberExpressionNode(parentNode) && parentNode.property === node + || NodeGuards.isMethodDefinitionNode(parentNode) && parentNode.key === node + || NodeGuards.isPropertyDefinitionNode(parentNode) && parentNode.key === node) { + NodeMetadata.set(node, {propertyKeyToRenameNode: true}); + + return; + } + if (this.options.renamePropertiesMode === RenamePropertiesMode.Safe) { this.analyzeAutoExcludedPropertyNames(node, parentNode); } @@ -107,87 +116,35 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer { * @returns {Node} */ public transformNode (node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node { - let propertyNode: ESTree.Node | null = null; - - if (NodeGuards.isPropertyNode(node)) { - propertyNode = this.transformPropertyNode(node); - } else if (NodeGuards.isPropertyDefinitionNode(node)) { - propertyNode = this.transformPropertyDefinitionNode(node); - } else if (NodeGuards.isMemberExpressionNode(node)) { - propertyNode = this.transformMemberExpressionNode(node); - } else if (NodeGuards.isMethodDefinitionNode(node)) { - propertyNode = this.transformMethodDefinitionNode(node); + if (!NodeGuards.isIdentifierNode(node) && !NodeGuards.isLiteralNode(node)) { + return node; } - if (propertyNode) { - NodeUtils.parentizeNode(propertyNode, parentNode); + if (!NodeMetadata.isPropertyKeyToRenameNode(node)) { + return node; } - return node; - } + const isPropertyNode = NodeGuards.isPropertyNode(parentNode); + const isPropertyLikeNode = isPropertyNode + || NodeGuards.isPropertyDefinitionNode(parentNode) + || NodeGuards.isMemberExpressionNode(parentNode) + || NodeGuards.isMethodDefinitionNode(parentNode); - /** - * @param {Property} propertyNode - * @returns {Property} - */ - private transformPropertyNode (propertyNode: ESTree.Property): ESTree.Property { - const propertyKeyNode: ESTree.Expression | ESTree.PrivateIdentifier = propertyNode.key; - - if (RenamePropertiesTransformer.isValidPropertyNode(propertyNode, propertyKeyNode)) { - propertyNode.key = this.renamePropertiesReplacer.replace(propertyKeyNode); - propertyNode.shorthand = false; + if (isPropertyLikeNode && !RenamePropertiesTransformer.isValidPropertyNode(parentNode, node)) { + return node; } - return propertyNode; - } - - /** - * @param {PropertyDefinition} propertyNode - * @returns {PropertyDefinition} - */ - private transformPropertyDefinitionNode (propertyNode: ESTree.PropertyDefinition): ESTree.PropertyDefinition { - const propertyKeyNode: ESTree.Expression | ESTree.PrivateIdentifier = propertyNode.key; - - if (RenamePropertiesTransformer.isValidPropertyNode(propertyNode, propertyKeyNode)) { - propertyNode.key = this.renamePropertiesReplacer.replace(propertyKeyNode); + if (isPropertyNode) { + parentNode.shorthand = false; } - return propertyNode; - } - - /** - * @param {Property} memberExpressionNode - * @returns {Property} - */ - private transformMemberExpressionNode (memberExpressionNode: ESTree.MemberExpression): ESTree.MemberExpression { - const propertyKeyNode: ESTree.Expression | ESTree.PrivateIdentifier = memberExpressionNode.property; - - if (RenamePropertiesTransformer.isValidPropertyNode(memberExpressionNode, propertyKeyNode)) { - memberExpressionNode.property = this.renamePropertiesReplacer.replace(propertyKeyNode); - } - - return memberExpressionNode; - } - - /** - * @param {MethodDefinition} methodDefinitionNode - * @returns {MethodDefinition} - */ - private transformMethodDefinitionNode (methodDefinitionNode: ESTree.MethodDefinition): ESTree.MethodDefinition { - const propertyKeyNode: ESTree.Expression | ESTree.PrivateIdentifier = methodDefinitionNode.key; - - if (RenamePropertiesTransformer.isValidPropertyNode(methodDefinitionNode, propertyKeyNode)) { - methodDefinitionNode.key = this.renamePropertiesReplacer.replace(propertyKeyNode); - } - - return methodDefinitionNode; + return this.renamePropertiesReplacer.replace(node); } /** * @param {Node} node * @param {Node} parentNode */ - // eslint-disable-next-line complexity private analyzeAutoExcludedPropertyNames ( node: ESTree.Node, parentNode: ESTree.Node @@ -196,15 +153,6 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer { return; } - if ( - (NodeGuards.isPropertyNode(parentNode) && parentNode.key === node) - || NodeGuards.isMemberExpressionNode(parentNode) && parentNode.property === node - || NodeGuards.isMethodDefinitionNode(parentNode) && parentNode.key === node - || NodeGuards.isPropertyDefinitionNode(parentNode) && parentNode.key === node - ) { - return; - } - this.renamePropertiesReplacer.excludePropertyName(node.value); } } diff --git a/src/node/NodeMetadata.ts b/src/node/NodeMetadata.ts index acb1e7e4..d3c57fb1 100644 --- a/src/node/NodeMetadata.ts +++ b/src/node/NodeMetadata.ts @@ -39,6 +39,17 @@ export class NodeMetadata { return NodeMetadata.get(node, 'ignoredNode') === true; } + /** + * @param {Identifier | Literal} node + * @returns {boolean} + */ + public static isPropertyKeyToRenameNode (node: ESTree.Identifier | ESTree.Literal): boolean { + return NodeMetadata.get( + node, + 'propertyKeyToRenameNode' + ) === true; + } + /** * @param {Node} literalNode * @returns {boolean} diff --git a/test/dev/dev.ts b/test/dev/dev.ts index 3495e2fa..ec778a48 100644 --- a/test/dev/dev.ts +++ b/test/dev/dev.ts @@ -1,55 +1,38 @@ 'use strict'; -import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType'; - (function () { const JavaScriptObfuscator: any = require('../../index'); let obfuscatedCode: string = JavaScriptObfuscator.obfuscate( ` - function foo () { - function bar() { - var string1 = 'string1'; - var string2 = 'string2'; - var string3 = 'string3'; - var string4 = 'string4'; - var string5 = 'string5'; - var string6 = 'string6'; - - function bark () { - var string1 = 'string1'; - var string2 = 'string2'; - var string3 = 'string3'; - var string4 = 'string4'; - var string5 = 'string5'; - var string6 = 'string6'; - } - } - - bar() + var obj = { + foo: 1 } - - console.log(foo()); `, { - identifierNamesGenerator: 'mangled', - compact: false, - controlFlowFlattening: false, - controlFlowFlatteningThreshold: 1, - simplify: false, - stringArrayRotate: false, - stringArray: true, - stringArrayIndexesType: [ - 'hexadecimal-number', - 'hexadecimal-numeric-string' - ], - stringArrayThreshold: 1, - stringArrayCallsTransform: true, - stringArrayCallsTransformThreshold: 1, - rotateStringArray: true, - stringArrayWrappersType: StringArrayWrappersType.Function, - transformObjectKeys: false, - seed: 1 + "compact": false, + "controlFlowFlattening": true, + "controlFlowFlatteningThreshold": 1, + "disableConsoleOutput": false, + "identifierNamesGenerator": "mangled", + "log": true, + "numbersToExpressions": true, + "renameProperties": true, + "renamePropertiesMode": "safe", + "simplify": false, + "stringArray": true, + "stringArrayCallsTransform": true, + "stringArrayIndexShift": true, + "stringArrayRotate": false, + "stringArrayShuffle": false, + "stringArrayWrappersCount": 5, + "stringArrayWrappersChainedCalls": true, + "stringArrayWrappersParametersMaxCount": 5, + "stringArrayWrappersType": "function", + "stringArrayThreshold": 0, + "transformObjectKeys": true, + "unicodeEscapeSequence": false, + "ignoreRequireImports": false } ).getObfuscatedCode(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/StringLiteralControlFlowReplacer.spec.ts b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/StringLiteralControlFlowReplacer.spec.ts index 1ab7f1ad..4ea956aa 100644 --- a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/StringLiteralControlFlowReplacer.spec.ts +++ b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/StringLiteralControlFlowReplacer.spec.ts @@ -3,6 +3,7 @@ import { assert } from 'chai'; import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes'; import { readFileAsString } from '../../../../../helpers/readFileAsString'; +import { getRegExpMatch } from '../../../../../helpers/getRegExpMatch'; import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscatorFacade'; @@ -10,34 +11,67 @@ describe('StringLiteralControlFlowReplacer', () => { describe('replace', () => { const variableMatch: string = '_0x([a-f0-9]){4,6}'; - const controlFlowStorageStringLiteralRegExp: RegExp = new RegExp( - `var ${variableMatch} *= *\\{'\\w{5}' *: *'test'\\};` - ); - const controlFlowStorageCallRegExp: RegExp = new RegExp( - `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\];` - ); + describe('Variant #1 - base behavior', () => { - let obfuscatedCode: string; + const controlFlowStorageStringLiteralRegExp: RegExp = new RegExp( + `var ${variableMatch} *= *\\{'\\w{5}' *: *'test'\\};` + ); + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\];` + ); - before(() => { - const code: string = readFileAsString(__dirname + '/fixtures/input-1.js'); + let obfuscatedCode: string; - obfuscatedCode = JavaScriptObfuscator.obfuscate( - code, - { - ...NO_ADDITIONAL_NODES_PRESET, - controlFlowFlattening: true, - controlFlowFlatteningThreshold: 1 - } - ).getObfuscatedCode(); + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/input-1.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + controlFlowFlattening: true, + controlFlowFlatteningThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should add string literal node as property of control flow storage node', () => { + assert.match(obfuscatedCode, controlFlowStorageStringLiteralRegExp); + }); + + it('should replace string literal node with call to control flow storage node', () => { + assert.match(obfuscatedCode, controlFlowStorageCallRegExp); + }); }); - it('should add string literal node as property of control flow storage node', () => { - assert.match(obfuscatedCode, controlFlowStorageStringLiteralRegExp); - }); + describe('Variant #2 - same storage key for same string values', () => { + const storageKeyRegExp: RegExp = /'(\w{5})': 'value'/; + const expectedStorageCallsMatchesCount: number = 5; - it('should replace string literal node with call to control flow storage node', () => { - assert.match(obfuscatedCode, controlFlowStorageCallRegExp); + let storageCallsMatchesCount: number; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/same-storage-key-for-same-string-values.js'); + + const obfuscatedCode: string = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + compact: false, + controlFlowFlattening: true, + controlFlowFlatteningThreshold: 1 + } + ).getObfuscatedCode(); + + const storageKeyMatch = getRegExpMatch(obfuscatedCode, storageKeyRegExp); + const storageCallsRegExp = new RegExp(`${variableMatch}\\[\'${storageKeyMatch}\']`, 'g') + + storageCallsMatchesCount = obfuscatedCode.match(storageCallsRegExp)?.length ?? 0; + }); + + it('should add string literal nodes with same values under same storage item', () => { + assert.equal(storageCallsMatchesCount, expectedStorageCallsMatchesCount); + }); }); }); }); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/fixtures/same-storage-key-for-same-string-values.js b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/fixtures/same-storage-key-for-same-string-values.js new file mode 100644 index 00000000..a1ab8e9b --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/fixtures/same-storage-key-for-same-string-values.js @@ -0,0 +1,7 @@ +(function () { + var string1 = 'value'; + var string2 = 'value'; + var string3 = 'value'; + var string4 = 'value'; + var string5 = 'value'; +})(); diff --git a/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts b/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts index 49631241..8f0a46ef 100644 --- a/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts +++ b/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/RenamePropertiesTransformer.spec.ts @@ -411,6 +411,70 @@ describe('RenamePropertiesTransformer', () => { assert.match(obfuscatedCode, propertyRegExp); }); }); + + describe('Variant #10: integration with `controlFlowFlattening` option', () => { + const propertyRegExp: RegExp = new RegExp( + 'const b *= *{ *' + + '\'\\w{5}\' *: *\'a\' *' + + '}; *' + + 'const c *= *{' + + '\'a\': *0x1' + + '};' + + 'c\\[b\\[\'\\w{5}\']];' + ); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/control-flow-flattening-integration.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + renameProperties: true, + renamePropertiesMode: RenamePropertiesMode.Unsafe, + identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator, + controlFlowFlattening: true, + controlFlowFlatteningThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('Should correctly rename property when `controlFlowFlattening` option is enabled', () => { + assert.match(obfuscatedCode, propertyRegExp); + }); + }); + + describe('Variant #11: integration with `transformObjectKeys` option', () => { + const propertyRegExp: RegExp = new RegExp( + 'const b *= *{}; *' + + 'b\\[\'a\'] *= *0x1;' + + 'const foo *= *b;' + + 'foo\\[\'a\'];' + ); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/transform-object-keys-integration.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + renameProperties: true, + renamePropertiesMode: RenamePropertiesMode.Unsafe, + identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator, + transformObjectKeys: true + } + ).getObfuscatedCode(); + }); + + it('Should correctly rename property when `transformObjectKeys` option is enabled', () => { + assert.match(obfuscatedCode, propertyRegExp); + }); + }); }); describe('Variant #3: Ignored literal node type', () => { diff --git a/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/control-flow-flattening-integration.js b/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/control-flow-flattening-integration.js new file mode 100644 index 00000000..a332d3c2 --- /dev/null +++ b/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/control-flow-flattening-integration.js @@ -0,0 +1,6 @@ +(() => { + const foo = { + bar: 1 + }; + foo['bar']; +})(); \ No newline at end of file diff --git a/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/transform-object-keys-integration.js b/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/transform-object-keys-integration.js new file mode 100644 index 00000000..00ecbaae --- /dev/null +++ b/test/functional-tests/node-transformers/rename-properties-transformers/rename-properties-transformer/fixtures/transform-object-keys-integration.js @@ -0,0 +1,4 @@ +const foo = { + bar: 1 +}; +foo['bar']; \ No newline at end of file diff --git a/test/functional-tests/node-transformers/string-array-transformers/string-array-rotate-function-transformer/StringArrayRotateFunctionTransformer.spec.ts b/test/functional-tests/node-transformers/string-array-transformers/string-array-rotate-function-transformer/StringArrayRotateFunctionTransformer.spec.ts index c3176699..a5b99b10 100644 --- a/test/functional-tests/node-transformers/string-array-transformers/string-array-rotate-function-transformer/StringArrayRotateFunctionTransformer.spec.ts +++ b/test/functional-tests/node-transformers/string-array-transformers/string-array-rotate-function-transformer/StringArrayRotateFunctionTransformer.spec.ts @@ -132,13 +132,14 @@ describe('StringArrayRotateFunctionTransformer', function () { }); describe('Code evaluation', function () { - this.timeout(100000); + const samplesCount: number = 50; + const evaluationTimeout: number = 5000; - const samplesCount: number = 100; + this.timeout(samplesCount * evaluationTimeout); let hasRuntimeErrors: boolean = false; - before(() => { + before(async() => { const code: string = readFileAsString(__dirname + '/fixtures/code-evaluation.js'); const obfuscateFunc = () => { @@ -186,7 +187,10 @@ describe('StringArrayRotateFunctionTransformer', function () { for (let i = 0; i < samplesCount; i++) { try { - const evaluationResult = eval(obfuscateFunc()); + const evaluationResult = await evaluateInWorker( + obfuscateFunc(), + evaluationTimeout + ); if (evaluationResult !== 'fooooooo') { hasRuntimeErrors = true; @@ -205,7 +209,7 @@ describe('StringArrayRotateFunctionTransformer', function () { }); describe('Prevent early successful comparison', () => { - const evaluationTimeout: number = 1000; + const evaluationTimeout: number = 1000; const samplesCount: number = 100; let numberNumericalExpressionAnalyzerAnalyzeStub: sinon.SinonStub; diff --git a/test/unit-tests/node/node-metadata/NodeMetadata.spec.ts b/test/unit-tests/node/node-metadata/NodeMetadata.spec.ts index c73a8436..2ce34c5d 100644 --- a/test/unit-tests/node/node-metadata/NodeMetadata.spec.ts +++ b/test/unit-tests/node/node-metadata/NodeMetadata.spec.ts @@ -86,6 +86,24 @@ describe('NodeMetadata', () => { }); }); + describe('propertyKeyToRenameNode', () => { + const expectedValue: boolean = true; + + let node: ESTree.Identifier, + value: boolean | undefined; + + before(() => { + node = NodeFactory.identifierNode('foo'); + node.metadata = {}; + node.metadata.propertyKeyToRenameNode = true; + value = NodeMetadata.isPropertyKeyToRenameNode(node); + }); + + it('should return metadata value', () => { + assert.equal(value, expectedValue); + }); + }); + describe('isStringArrayCallLiteralNode', () => { const expectedValue: boolean = true;