diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d5ffe5d5..af8517796 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: node-version: 14.x - os: ubuntu-latest, node-version: 16.x - - os: windows-latest, - node-version: 14.x + - os: ubuntu-latest, + node-version: 17.x - os: windows-latest, node-version: 16.x diff --git a/CHANGELOG.md b/CHANGELOG.md index cef368a21..3cf19ced1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ Change Log +v3.2.0 +--- +* **New options**: `stringArrayCallsTransform` and `stringArrayCallsTransformThreshold` +* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/pull/1046 + v3.1.0 --- * Added support of `es2022` features: class static block diff --git a/README.md b/README.md index 5b91eb544..92a3763a3 100644 --- a/README.md +++ b/README.md @@ -379,10 +379,12 @@ Following options are available for the JS Obfuscator: splitStrings: false, splitStringsChunkLength: 10, stringArray: true, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 0.5, + stringArrayEncoding: [], stringArrayIndexesType: [ 'hexadecimal-number' ], - stringArrayEncoding: [], stringArrayIndexShift: true, stringArrayRotate: true, stringArrayShuffle: true, @@ -441,8 +443,10 @@ Following options are available for the JS Obfuscator: --split-strings --split-strings-chunk-length --string-array - --string-array-indexes-type '' (comma separated) [hexadecimal-number, hexadecimal-numeric-string] + --string-array-calls-transform + --string-array-calls-transform-threshold --string-array-encoding '' (comma separated) [none, base64, rc4] + --string-array-indexes-type '' (comma separated) [hexadecimal-number, hexadecimal-numeric-string] --string-array-index-shift --string-array-rotate --string-array-shuffle @@ -1097,6 +1101,20 @@ Type: `boolean` Default: `true` Removes string literals and place them in a special array. For instance, the string `"Hello World"` in `var m = "Hello World";` will be replaced with something like `var m = _0x12c456[0x1];` +### `stringArrayCallsTransform` +Type: `boolean` Default: `true` + +##### :warning: [`stringArray`](#stringarray) option must be enabled + +Enables the transformation of calls to the [`stringArray`](#stringarray). All arguments of these calls may be extracted to different object depending on [`stringArrayCallsTransformThreshold`](#stringarraycallstransformthreshold) value. + +### `stringArrayCallsTransformThreshold` +Type: `number` Default: `0.5` + +##### :warning: [`stringArray`](#stringarray) and [`stringArrayCallsTransformThreshold`](#stringarraycallstransformthreshold) options must be enabled + +You can use this setting to adjust the probability (from 0 to 1) that calls to the string array will be transformed. + ### `stringArrayEncoding` Type: `string[]` Default: `[]` @@ -1419,7 +1437,7 @@ Unicode escape sequence increases code size greatly and strings easily can be re ## Preset Options ### High obfuscation, low performance -Performance will 50-100% slower than without obfuscation +The performance will be much slower than without obfuscation ```javascript { @@ -1440,6 +1458,8 @@ Performance will 50-100% slower than without obfuscation splitStrings: true, splitStringsChunkLength: 5, stringArray: true, + stringArray: true, + stringArrayCallsTransform: true, stringArrayEncoding: ['rc4'], stringArrayIndexShift: true, stringArrayRotate: true, @@ -1456,7 +1476,7 @@ Performance will 50-100% slower than without obfuscation ### Medium obfuscation, optimal performance -Performance will 30-35% slower than without obfuscation +The performance will be slower than without obfuscation ```javascript { @@ -1477,6 +1497,8 @@ Performance will 30-35% slower than without obfuscation splitStrings: true, splitStringsChunkLength: 10, stringArray: true, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 0.75, stringArrayEncoding: ['base64'], stringArrayIndexShift: true, stringArrayRotate: true, @@ -1493,7 +1515,7 @@ Performance will 30-35% slower than without obfuscation ### Low obfuscation, High performance -Performance will slightly slower than without obfuscation +The performance will be at a relatively normal level ```javascript { @@ -1511,6 +1533,7 @@ Performance will slightly slower than without obfuscation simplify: true, splitStrings: false, stringArray: true, + stringArrayCallsTransform: false, stringArrayEncoding: [], stringArrayIndexShift: true, stringArrayRotate: true, @@ -1542,6 +1565,8 @@ Performance will slightly slower than without obfuscation simplify: true, splitStrings: false, stringArray: true, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 0.5, stringArrayEncoding: [], stringArrayIndexShift: true, stringArrayRotate: true, diff --git a/package.json b/package.json index f4e31e45c..5c861e733 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "test:devRuntimePerformance": "ts-node test/dev/dev-runtime-performance.ts", "test:full": "yarn run test:dev && yarn run test:mocha-coverage && yarn run test:mocha-memory-performance", "test:mocha": "mocha --require ts-node/register --require source-map-support/register test/index.spec.ts --exit", - "test:mocha-coverage": "nyc --reporter text-summary --no-clean yarn run test:mocha", + "test:mocha-coverage": "NODE_OPTIONS=--max-old-space-size=4096 nyc --reporter text-summary --no-clean yarn run test:mocha", "test:mocha-coverage:report": "nyc report --reporter=lcov", "test:mocha-memory-performance": "cross-env NODE_OPTIONS=--max-old-space-size=230 mocha --require ts-node/register test/performance-tests/JavaScriptObfuscatorMemory.spec.ts", "test": "yarn run test:full", diff --git a/src/JavaScriptObfuscator.ts b/src/JavaScriptObfuscator.ts index a2afb569d..4728c05a3 100644 --- a/src/JavaScriptObfuscator.ts +++ b/src/JavaScriptObfuscator.ts @@ -20,7 +20,7 @@ import { CodeTransformer } from './enums/code-transformers/CodeTransformer'; import { CodeTransformationStage } from './enums/code-transformers/CodeTransformationStage'; import { LoggingMessage } from './enums/logger/LoggingMessage'; import { NodeTransformer } from './enums/node-transformers/NodeTransformer'; -import { NodeTransformationStage } from './enums/node-transformers/NodeTransformationStage'; +import { NodeTransformationStage } from './enums/node-transformers/NodeTransformationStage'; import { SourceMapSourcesMode } from './enums/source-map/SourceMapSourcesMode'; import { ecmaVersion } from './constants/EcmaVersion'; @@ -90,6 +90,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator { NodeTransformer.ScopeIdentifiersTransformer, NodeTransformer.ScopeThroughIdentifiersTransformer, NodeTransformer.SplitStringTransformer, + NodeTransformer.StringArrayControlFlowTransformer, NodeTransformer.StringArrayRotateFunctionTransformer, NodeTransformer.StringArrayScopeCallsWrapperTransformer, NodeTransformer.StringArrayTransformer, @@ -220,9 +221,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator { astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.DeadCodeInjection); } - if (this.options.controlFlowFlattening) { - astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.ControlFlowFlattening); - } + astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.ControlFlowFlattening); if (this.options.renameProperties) { astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.RenameProperties); diff --git a/src/cli/JavaScriptObfuscatorCLI.ts b/src/cli/JavaScriptObfuscatorCLI.ts index 1d789a405..cc9428eb3 100644 --- a/src/cli/JavaScriptObfuscatorCLI.ts +++ b/src/cli/JavaScriptObfuscatorCLI.ts @@ -363,9 +363,19 @@ export class JavaScriptObfuscatorCLI implements IInitializable { ) .option( '--string-array ', - 'Disables gathering of all literal strings into an array and replacing every literal string with an array call', + 'Enables gathering of all literal strings into an array and replacing every literal string with an array call', BooleanSanitizer ) + .option( + '--string-array-calls-transform ', + 'Enables the transformation of calls to the string array', + BooleanSanitizer + ) + .option( + '--string-array-calls-transform-threshold ', + 'The probability that that calls to the string array will be transformed', + parseFloat + ) .option( '--string-array-encoding (comma separated, without whitespaces)', 'Encodes each string in strings array using base64 or rc4 (this option can slow down your code speed). ' + diff --git a/src/container/ServiceIdentifiers.ts b/src/container/ServiceIdentifiers.ts index 041e75af8..c1d3569cb 100644 --- a/src/container/ServiceIdentifiers.ts +++ b/src/container/ServiceIdentifiers.ts @@ -21,6 +21,7 @@ export enum ServiceIdentifiers { ICodeTransformer = 'ICodeTransformer', ICodeTransformerNamesGroupsBuilder = 'ICodeTransformerNamesGroupsBuilder', ICodeTransformersRunner = 'ICodeTransformersRunner', + IControlFlowStorage = 'IControlFlowStorage', ICryptUtils = 'ICryptUtils', ICryptUtilsStringArray = 'ICryptUtilsStringArray', ICustomCodeHelper = 'ICustomCodeHelper', @@ -50,6 +51,7 @@ export enum ServiceIdentifiers { IRandomGenerator = 'IRandomGenerator', IRenamePropertiesReplacer = 'IRenamePropertiesReplacer', IScopeIdentifiersTraverser = 'IScopeIdentifiersTraverser', + ISetUtils = 'ISetUtils', ISourceCode = 'ISourceCode', IScopeAnalyzer = 'IScopeAnalyzer', IStringArrayIndexNode = 'IStringArrayIndexNode', @@ -59,7 +61,6 @@ export enum ServiceIdentifiers { IThroughIdentifierReplacer = 'IThroughIdentifierReplacer', IVisitedLexicalScopeNodesStackStorage = 'IVisitedLexicalScopeNodesStackStorage', Newable__ICustomNode = 'Newable', - Newable__TControlFlowStorage = 'Newable', TCustomNodeGroupStorage = 'TCustomNodeGroupStorage', TInputOptions = 'TInputOptions' } diff --git a/src/container/modules/custom-nodes/CustomNodesModule.ts b/src/container/modules/custom-nodes/CustomNodesModule.ts index 1e0586948..b73aa8b8f 100644 --- a/src/container/modules/custom-nodes/CustomNodesModule.ts +++ b/src/container/modules/custom-nodes/CustomNodesModule.ts @@ -26,7 +26,7 @@ import { StringArrayIndexNode } from '../../../enums/custom-nodes/string-array-i import { StringArrayScopeCallsWrapperFunctionNode } from '../../../custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode'; import { StringArrayScopeCallsWrapperVariableNode } from '../../../custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode'; import { StringLiteralControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode'; -import { StringLiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/StringLiteralNode'; +import { LiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/LiteralNode'; export const customNodesModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => { // control flow custom nodes @@ -55,12 +55,12 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule .whenTargetNamed(ControlFlowCustomNode.ExpressionWithOperatorControlFlowStorageCallNode); bind>(ServiceIdentifiers.Newable__ICustomNode) - .toConstructor(LogicalExpressionFunctionNode) - .whenTargetNamed(ControlFlowCustomNode.LogicalExpressionFunctionNode); + .toConstructor(LiteralNode) + .whenTargetNamed(ControlFlowCustomNode.LiteralNode); bind>(ServiceIdentifiers.Newable__ICustomNode) - .toConstructor(StringLiteralNode) - .whenTargetNamed(ControlFlowCustomNode.StringLiteralNode); + .toConstructor(LogicalExpressionFunctionNode) + .whenTargetNamed(ControlFlowCustomNode.LogicalExpressionFunctionNode); bind>(ServiceIdentifiers.Newable__ICustomNode) .toConstructor(StringLiteralControlFlowStorageCallNode) diff --git a/src/container/modules/node-transformers/ControlFlowTransformersModule.ts b/src/container/modules/node-transformers/ControlFlowTransformersModule.ts index 376509db0..a2406c1b3 100644 --- a/src/container/modules/node-transformers/ControlFlowTransformersModule.ts +++ b/src/container/modules/node-transformers/ControlFlowTransformersModule.ts @@ -13,6 +13,8 @@ import { BlockStatementControlFlowTransformer } from '../../../node-transformers import { CallExpressionControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer'; import { FunctionControlFlowTransformer } from '../../../node-transformers/control-flow-transformers/FunctionControlFlowTransformer'; import { LogicalExpressionControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer'; +import { StringArrayCallControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/StringArrayCallControlFlowReplacer'; +import { StringArrayControlFlowTransformer } from '../../../node-transformers/control-flow-transformers/StringArrayControlFlowTransformer'; import { StringLiteralControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer'; export const controlFlowTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => { @@ -25,6 +27,10 @@ export const controlFlowTransformersModule: interfaces.ContainerModule = new Con .to(FunctionControlFlowTransformer) .whenTargetNamed(NodeTransformer.FunctionControlFlowTransformer); + bind(ServiceIdentifiers.INodeTransformer) + .to(StringArrayControlFlowTransformer) + .whenTargetNamed(NodeTransformer.StringArrayControlFlowTransformer); + // control flow replacers bind(ServiceIdentifiers.IControlFlowReplacer) .to(BinaryExpressionControlFlowReplacer) @@ -38,6 +44,10 @@ export const controlFlowTransformersModule: interfaces.ContainerModule = new Con .to(LogicalExpressionControlFlowReplacer) .whenTargetNamed(ControlFlowReplacer.LogicalExpressionControlFlowReplacer); + bind(ServiceIdentifiers.IControlFlowReplacer) + .to(StringArrayCallControlFlowReplacer) + .whenTargetNamed(ControlFlowReplacer.StringArrayCallControlFlowReplacer); + bind(ServiceIdentifiers.IControlFlowReplacer) .to(StringLiteralControlFlowReplacer) .whenTargetNamed(ControlFlowReplacer.StringLiteralControlFlowReplacer); diff --git a/src/container/modules/storages/StoragesModule.ts b/src/container/modules/storages/StoragesModule.ts index 58fb5011a..52bc65055 100644 --- a/src/container/modules/storages/StoragesModule.ts +++ b/src/container/modules/storages/StoragesModule.ts @@ -1,25 +1,31 @@ import { ContainerModule, interfaces } from 'inversify'; import { ServiceIdentifiers } from '../../ServiceIdentifiers'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; -import { TConstructor } from '../../../types/TConstructor'; +import { TControlFlowStorageFactory } from '../../../types/container/node-transformers/TControlFlowStorageFactory'; +import { + TControlFlowStorageFactoryCreator +} from '../../../types/container/node-transformers/TControlFlowStorageFactoryCreator'; import { TCustomCodeHelperGroupStorage } from '../../../types/storages/TCustomCodeHelperGroupStorage'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { IGlobalIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage'; import { ILiteralNodesCacheStorage } from '../../../interfaces/storages/string-array-transformers/ILiteralNodesCacheStorage'; -import { IOptions } from '../../../interfaces/options/IOptions'; import { IPropertyIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage'; -import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator'; import { IStringArrayScopeCallsWrappersDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage'; import { IStringArrayStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayStorage'; import { IVisitedLexicalScopeNodesStackStorage } from '../../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage'; -import { ControlFlowStorage } from '../../../storages/custom-nodes/ControlFlowStorage'; +import { ControlFlowStorage } from '../../../enums/storages/ControlFlowStorage'; + import { CustomCodeHelperGroupStorage } from '../../../storages/custom-code-helpers/CustomCodeHelperGroupStorage'; +import { FunctionControlFlowStorage } from '../../../storages/control-flow-transformers/FunctionControlFlowStorage'; import { GlobalIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/GlobalIdentifierNamesCacheStorage'; import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage'; import { PropertyIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/PropertyIdentifierNamesCacheStorage'; import { StringArrayScopeCallsWrappersDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrappersDataStorage'; +import { + StringControlFlowStorage +} from '../../../storages/control-flow-transformers/StringControlFlowStorage'; import { StringArrayStorage } from '../../../storages/string-array-transformers/StringArrayStorage'; import { VisitedLexicalScopeNodesStackStorage } from '../../../storages/string-array-transformers/VisitedLexicalScopeNodesStackStorage'; @@ -29,6 +35,10 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b .to(CustomCodeHelperGroupStorage) .inSingletonScope(); + bind(ServiceIdentifiers.IControlFlowStorage) + .to(FunctionControlFlowStorage) + .whenTargetNamed(ControlFlowStorage.FunctionControlFlowStorage); + bind(ServiceIdentifiers.IGlobalIdentifierNamesCacheStorage) .to(GlobalIdentifierNamesCacheStorage) .inSingletonScope(); @@ -49,34 +59,21 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b .to(StringArrayScopeCallsWrappersDataStorage) .inSingletonScope(); + bind(ServiceIdentifiers.IControlFlowStorage) + .to(StringControlFlowStorage) + .whenTargetNamed(ControlFlowStorage.StringControlFlowStorage); + bind(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage) .to(VisitedLexicalScopeNodesStackStorage) .inSingletonScope(); - bind>(ServiceIdentifiers.Newable__TControlFlowStorage) - .toConstructor(ControlFlowStorage); - // controlFlowStorage factory - bind(ServiceIdentifiers.Factory__TControlFlowStorage) - .toFactory((context: interfaces.Context) => { - return (): TControlFlowStorage => { - const constructor = context.container - .get>( - ServiceIdentifiers.Newable__TControlFlowStorage - ); - const randomGenerator: IRandomGenerator = context.container - .get(ServiceIdentifiers.IRandomGenerator); - const options: IOptions = context.container - .get(ServiceIdentifiers.IOptions); - - const storage: TControlFlowStorage = new constructor( - randomGenerator, - options - ); - - storage.initialize(); - - return storage; - }; - }); + bind(ServiceIdentifiers.Factory__TControlFlowStorage) + .toFactory((context: interfaces.Context): TControlFlowStorageFactoryCreator => + (controlFlowStorageName: ControlFlowStorage): TControlFlowStorageFactory => (): IControlFlowStorage => + context.container.getNamed( + ServiceIdentifiers.IControlFlowStorage, + controlFlowStorageName + ) + ); }); diff --git a/src/container/modules/utils/UtilsModule.ts b/src/container/modules/utils/UtilsModule.ts index b83e83f36..8ec11832a 100644 --- a/src/container/modules/utils/UtilsModule.ts +++ b/src/container/modules/utils/UtilsModule.ts @@ -7,6 +7,7 @@ import { ICryptUtilsStringArray } from '../../../interfaces/utils/ICryptUtilsStr import { IEscapeSequenceEncoder } from '../../../interfaces/utils/IEscapeSequenceEncoder'; import { ILevelledTopologicalSorter } from '../../../interfaces/utils/ILevelledTopologicalSorter'; import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator'; +import { ISetUtils } from '../../../interfaces/utils/ISetUtils'; import { ArrayUtils } from '../../../utils/ArrayUtils'; import { CryptUtils } from '../../../utils/CryptUtils'; @@ -14,6 +15,7 @@ import { CryptUtilsStringArray } from '../../../utils/CryptUtilsStringArray'; import { EscapeSequenceEncoder } from '../../../utils/EscapeSequenceEncoder'; import { LevelledTopologicalSorter } from '../../../utils/LevelledTopologicalSorter'; import { RandomGenerator } from '../../../utils/RandomGenerator'; +import { SetUtils } from '../../../utils/SetUtils'; export const utilsModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => { // array utils @@ -44,4 +46,9 @@ export const utilsModule: interfaces.ContainerModule = new ContainerModule((bind // levelled topological sorter bind(ServiceIdentifiers.ILevelledTopologicalSorter) .to(LevelledTopologicalSorter); + + // set utils + bind(ServiceIdentifiers.ISetUtils) + .to(SetUtils) + .inSingletonScope(); }); diff --git a/src/custom-nodes/control-flow-flattening-nodes/StringLiteralNode.ts b/src/custom-nodes/control-flow-flattening-nodes/LiteralNode.ts similarity index 84% rename from src/custom-nodes/control-flow-flattening-nodes/StringLiteralNode.ts rename to src/custom-nodes/control-flow-flattening-nodes/LiteralNode.ts index 936cd428b..1e418d112 100644 --- a/src/custom-nodes/control-flow-flattening-nodes/StringLiteralNode.ts +++ b/src/custom-nodes/control-flow-flattening-nodes/LiteralNode.ts @@ -1,6 +1,8 @@ import { inject, injectable, } from 'inversify'; import { ServiceIdentifiers } from '../../container/ServiceIdentifiers'; +import type * as ESTree from 'estree'; + import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory'; import { TStatement } from '../../types/node/TStatement'; @@ -14,12 +16,12 @@ import { AbstractCustomNode } from '../AbstractCustomNode'; import { NodeFactory } from '../../node/NodeFactory'; @injectable() -export class StringLiteralNode extends AbstractCustomNode { +export class LiteralNode extends AbstractCustomNode { /** - * @type {string} + * @type {ESTree.Literal} */ @initializable() - private literalValue!: string; + private literalNode!: ESTree.Literal; /** * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory @@ -43,19 +45,17 @@ export class StringLiteralNode extends AbstractCustomNode { } /** - * @param {string} literalValue + * @param {ESTree.Literal} literalNode */ - public initialize (literalValue: string): void { - this.literalValue = literalValue; + public initialize (literalNode: ESTree.Literal): void { + this.literalNode = literalNode; } /** * @returns {TStatement[]} */ protected getNodeStructure (): TStatement[] { - const structure: TStatement = NodeFactory.expressionStatementNode( - NodeFactory.literalNode(this.literalValue) - ); + const structure: TStatement = NodeFactory.expressionStatementNode(this.literalNode); return [structure]; } diff --git a/src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts b/src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts index a8ddf5fd9..8db3102df 100644 --- a/src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts +++ b/src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode.ts @@ -3,10 +3,10 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers'; import * as ESTree from 'estree'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory'; import { TStatement } from '../../../types/node/TStatement'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { ICustomCodeHelperFormatter } from '../../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter'; import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode'; import { IOptions } from '../../../interfaces/options/IOptions'; @@ -21,10 +21,10 @@ import { NodeGuards } from '../../../node/NodeGuards'; @injectable() export class ControlFlowStorageNode extends AbstractCustomNode { /** - * @type {TControlFlowStorage} + * @type {IControlFlowStorage} */ @initializable() - private controlFlowStorage!: TControlFlowStorage; + private controlFlowStorage!: IControlFlowStorage; /** * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory @@ -48,9 +48,9 @@ export class ControlFlowStorageNode extends AbstractCustomNode { } /** - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage */ - public initialize (controlFlowStorage: TControlFlowStorage): void { + public initialize (controlFlowStorage: IControlFlowStorage): void { this.controlFlowStorage = controlFlowStorage; } diff --git a/src/enums/custom-nodes/ControlFlowCustomNode.ts b/src/enums/custom-nodes/ControlFlowCustomNode.ts index ae4310b9a..5ed6068ea 100644 --- a/src/enums/custom-nodes/ControlFlowCustomNode.ts +++ b/src/enums/custom-nodes/ControlFlowCustomNode.ts @@ -5,7 +5,7 @@ export enum ControlFlowCustomNode { CallExpressionFunctionNode = 'CallExpressionFunctionNode', ControlFlowStorageNode = 'ControlFlowStorageNode', ExpressionWithOperatorControlFlowStorageCallNode = 'ExpressionWithOperatorControlFlowStorageCallNode', + LiteralNode = 'LiteralNode', LogicalExpressionFunctionNode = 'LogicalExpressionFunctionNode', - StringLiteralControlFlowStorageCallNode = 'StringLiteralControlFlowStorageCallNode', - StringLiteralNode = 'StringLiteralNode' + StringLiteralControlFlowStorageCallNode = 'StringLiteralControlFlowStorageCallNode' } diff --git a/src/enums/node-transformers/NodeTransformer.ts b/src/enums/node-transformers/NodeTransformer.ts index bd3e1897a..a466737f5 100644 --- a/src/enums/node-transformers/NodeTransformer.ts +++ b/src/enums/node-transformers/NodeTransformer.ts @@ -28,6 +28,7 @@ export enum NodeTransformer { ScopeIdentifiersTransformer = 'ScopeIdentifiersTransformer', ScopeThroughIdentifiersTransformer = 'ScopeThroughIdentifiersTransformer', SplitStringTransformer = 'SplitStringTransformer', + StringArrayControlFlowTransformer = 'StringArrayControlFlowTransformer', StringArrayTransformer = 'StringArrayTransformer', StringArrayRotateFunctionTransformer = 'StringArrayRotateFunctionTransformer', StringArrayScopeCallsWrapperTransformer = 'StringArrayScopeCallsWrapperTransformer', diff --git a/src/enums/node-transformers/control-flow-transformers/control-flow-replacers/ControlFlowReplacer.ts b/src/enums/node-transformers/control-flow-transformers/control-flow-replacers/ControlFlowReplacer.ts index 079ec1d20..03e2e5cfa 100644 --- a/src/enums/node-transformers/control-flow-transformers/control-flow-replacers/ControlFlowReplacer.ts +++ b/src/enums/node-transformers/control-flow-transformers/control-flow-replacers/ControlFlowReplacer.ts @@ -2,5 +2,6 @@ export enum ControlFlowReplacer { BinaryExpressionControlFlowReplacer = 'BinaryExpressionControlFlowReplacer', CallExpressionControlFlowReplacer = 'CallExpressionControlFlowReplacer', LogicalExpressionControlFlowReplacer = 'LogicalExpressionControlFlowReplacer', + StringArrayCallControlFlowReplacer = 'StringArrayCallControlFlowReplacer', StringLiteralControlFlowReplacer = 'StringLiteralControlFlowReplacer' } diff --git a/src/enums/storages/ControlFlowStorage.ts b/src/enums/storages/ControlFlowStorage.ts new file mode 100644 index 000000000..a563b0ff5 --- /dev/null +++ b/src/enums/storages/ControlFlowStorage.ts @@ -0,0 +1,4 @@ +export enum ControlFlowStorage { + FunctionControlFlowStorage = 'function-control-flow-storage', + StringControlFlowStorage = 'string-control-flow-storage' +} diff --git a/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts index a5c25c934..5dc970709 100644 --- a/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/AbstractIdentifierNamesGenerator.ts @@ -134,6 +134,13 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam */ public abstract generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string; + /** + * @param {string} label + * @param {number} nameLength + * @returns {string} + */ + public abstract generateForLabel (label: string, nameLength?: number): string; + /** * @param {number} nameLength * @returns {string} diff --git a/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts index 44235af4d..448a251b5 100644 --- a/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/DictionaryIdentifierNamesGenerator.ts @@ -84,12 +84,13 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG const prefix: string = this.options.identifiersPrefix ? `${this.options.identifiersPrefix}` : ''; - const identifierName: string = this.generateNewDictionaryName(); - const identifierNameWithPrefix: string = `${prefix}${identifierName}`; - if (!this.isValidIdentifierName(identifierNameWithPrefix)) { - return this.generateForGlobalScope(); - } + const identifierName: string = this.generateNewDictionaryName((newIdentifierName: string) => { + const identifierNameWithPrefix: string = `${prefix}${newIdentifierName}`; + + return this.isValidIdentifierName(identifierNameWithPrefix); + }); + const identifierNameWithPrefix = `${prefix}${identifierName}`; this.preserveName(identifierNameWithPrefix); @@ -105,11 +106,9 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG lexicalScopeNode, ...NodeLexicalScopeUtils.getLexicalScopes(lexicalScopeNode) ]; - const identifierName: string = this.generateNewDictionaryName(); - - if (!this.isValidIdentifierNameInLexicalScopes(identifierName, lexicalScopes)) { - return this.generateForLexicalScope(lexicalScopeNode); - } + const identifierName: string = this.generateNewDictionaryName((newIdentifierName: string) => + this.isValidIdentifierNameInLexicalScopes(newIdentifierName, lexicalScopes) + ); this.preserveNameForLexicalScope(identifierName, lexicalScopeNode); @@ -117,29 +116,45 @@ export class DictionaryIdentifierNamesGenerator extends AbstractIdentifierNamesG } /** + * @param {string} label * @returns {string} */ - private generateNewDictionaryName (): string { - if (!this.identifierNamesSet.size) { - throw new Error('Too many identifiers in the code, add more words to identifiers dictionary'); - } + public generateForLabel (label: string): string { + return this.generateNewDictionaryName(); + } - const iteratorResult: IteratorResult = this.identifiersIterator.next(); + /** + * @param {(newIdentifierName: string) => boolean} validationFunction + * @returns {string} + */ + private generateNewDictionaryName (validationFunction?: (newIdentifierName: string) => boolean): string { + const generateNewDictionaryName = (): string => { + if (!this.identifierNamesSet.size) { + throw new Error('Too many identifiers in the code, add more words to identifiers dictionary'); + } + + const iteratorResult: IteratorResult = this.identifiersIterator.next(); + + if (!iteratorResult.done) { + const identifierName: string = iteratorResult.value; - if (!iteratorResult.done) { - const identifierName: string =iteratorResult.value; + const isValidIdentifierName = validationFunction?.(identifierName) + ?? this.isValidIdentifierName(identifierName); - if (!this.isValidIdentifierName(identifierName)) { - return this.generateNewDictionaryName(); + if (!isValidIdentifierName) { + return generateNewDictionaryName(); + } + + return iteratorResult.value; } - return iteratorResult.value; - } + this.identifierNamesSet = new Set(this.getIncrementedIdentifierNames([...this.identifierNamesSet])); + this.identifiersIterator = this.identifierNamesSet.values(); - this.identifierNamesSet = new Set(this.getIncrementedIdentifierNames([...this.identifierNamesSet])); - this.identifiersIterator = this.identifierNamesSet.values(); + return generateNewDictionaryName(); + }; - return this.generateNewDictionaryName(); + return generateNewDictionaryName(); } /** diff --git a/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts index c5da0458f..c5f0e2c18 100644 --- a/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.ts @@ -70,4 +70,13 @@ export class HexadecimalIdentifierNamesGenerator extends AbstractIdentifierNames public generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string { return this.generateNext(nameLength); } + + /** + * @param {string} label + * @param {number} nameLength + * @returns {string} + */ + public generateForLabel (label: string, nameLength?: number): string { + return this.generateNext(nameLength); + } } diff --git a/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts index 3dff896c5..c38d403dc 100644 --- a/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts @@ -5,6 +5,7 @@ import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope'; import { IOptions } from '../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; +import { ISetUtils } from '../../interfaces/utils/ISetUtils'; import { numbersString } from '../../constants/NumbersString'; import { alphabetString } from '../../constants/AlphabetString'; @@ -15,6 +16,11 @@ import { NodeLexicalScopeUtils } from '../../node/NodeLexicalScopeUtils'; @injectable() export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGenerator { + /** + * @type {number} + */ + private static readonly maxRegenerationAttempts: number = 20; + /** * @type {string} */ @@ -43,20 +49,34 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene 'var', 'void', 'with' ]); + /** + * @type {WeakMap} + */ + private readonly lastMangledNameForLabelMap: Map = new Map(); + /** * @type {string} */ private previousMangledName: string = MangledIdentifierNamesGenerator.initMangledNameCharacter; + /** + * @type {ISetUtils} + */ + private readonly setUtils: ISetUtils; + /** * @param {IRandomGenerator} randomGenerator * @param {IOptions} options + * @param {ISetUtils} setUtils */ public constructor ( @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, - @inject(ServiceIdentifiers.IOptions) options: IOptions + @inject(ServiceIdentifiers.IOptions) options: IOptions, + @inject(ServiceIdentifiers.ISetUtils) setUtils: ISetUtils, ) { super(randomGenerator, options); + + this.setUtils = setUtils; } /** @@ -83,15 +103,18 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene const prefix: string = this.options.identifiersPrefix ? `${this.options.identifiersPrefix}` : ''; - const identifierName: string = this.generateNewMangledName(this.previousMangledName); - const identifierNameWithPrefix: string = `${prefix}${identifierName}`; - this.updatePreviousMangledName(identifierName); + const identifierName: string = this.generateNewMangledName( + this.previousMangledName, + (newIdentifierName: string) => { + const identifierNameWithPrefix: string = `${prefix}${newIdentifierName}`; - if (!this.isValidIdentifierName(identifierNameWithPrefix)) { - return this.generateForGlobalScope(nameLength); - } + return this.isValidIdentifierName(identifierNameWithPrefix); + } + ); + const identifierNameWithPrefix: string = `${prefix}${identifierName}`; + this.updatePreviousMangledName(identifierName); this.preserveName(identifierNameWithPrefix); return identifierNameWithPrefix; @@ -109,12 +132,11 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene ]; const lastMangledNameForScope: string = this.getLastMangledNameForScopes(lexicalScopes); - - let identifierName: string = lastMangledNameForScope; - - do { - identifierName = this.generateNewMangledName(identifierName); - } while (!this.isValidIdentifierNameInLexicalScopes(identifierName, lexicalScopes)); + const identifierName: string = this.generateNewMangledName( + lastMangledNameForScope, + (newIdentifierName: string) => + this.isValidIdentifierNameInLexicalScopes(newIdentifierName, lexicalScopes) + ); MangledIdentifierNamesGenerator.lastMangledNameInScopeMap.set(lexicalScopeNode, identifierName); @@ -124,6 +146,21 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene return identifierName; } + /** + * @param {string} label + * @param {number} nameLength + * @returns {string} + */ + public generateForLabel (label: string, nameLength?: number): string { + const lastMangledNameForLabel: string = this.getLastMangledNameForLabel(label); + + const identifierName: string = this.generateNewMangledName(lastMangledNameForLabel); + + this.updatePreviousMangledNameForLabel(identifierName, label, lastMangledNameForLabel); + + return identifierName; + } + /** * @param {string} nextName * @param {string} prevName @@ -188,12 +225,42 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene this.previousMangledName = name; } + /** + * @param {string} name + * @param {string} label + * @param {string} lastMangledNameForLabel + */ + protected updatePreviousMangledNameForLabel (name: string, label: string, lastMangledNameForLabel: string): void { + if (!this.isIncrementedMangledName(name, lastMangledNameForLabel)) { + return; + } + + this.lastMangledNameForLabelMap.set(label, name); + } + /** * @param {string} previousMangledName + * @param {(newIdentifierName: string) => boolean} validationFunction * @returns {string} */ - protected generateNewMangledName (previousMangledName: string): string { - const generateNewMangledName: (name: string) => string = (name: string): string => { + protected generateNewMangledName ( + previousMangledName: string, + validationFunction?: (newIdentifierName: string) => boolean + ): string { + const generateNewMangledName = (name: string, regenerationAttempt: number = 0): string => { + /** + * Attempt to decrease amount of regeneration tries because of large preserved names set + * When we reached the limit, we're trying to generate next mangled name based on the latest + * preserved name + */ + if (regenerationAttempt > MangledIdentifierNamesGenerator.maxRegenerationAttempts) { + const lastPreservedName = this.setUtils.getLastElement(this.preservedNamesSet); + + if (lastPreservedName) { + return this.generateNewMangledName(lastPreservedName); + } + } + const nameSequence: string[] = this.getNameSequence(); const nameSequenceLength: number = nameSequence.length; const nameLength: number = name.length; @@ -226,13 +293,16 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene return `${firstLetterCharacter}${zeroSequence(nameLength)}`; }; - let newMangledName: string = generateNewMangledName(previousMangledName); + let identifierName: string = previousMangledName; + let isValidIdentifierName: boolean; - if (!this.isValidIdentifierName(newMangledName)) { - newMangledName = this.generateNewMangledName(newMangledName); - } + do { + identifierName = generateNewMangledName(identifierName); + isValidIdentifierName = validationFunction?.(identifierName) + ?? this.isValidIdentifierName(identifierName); + } while (!isValidIdentifierName); - return newMangledName; + return identifierName; } /** @@ -253,4 +323,14 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene return MangledIdentifierNamesGenerator.initMangledNameCharacter; } + + /** + * @param {string} label + * @returns {string} + */ + private getLastMangledNameForLabel (label: string): string { + const lastMangledName: string | null = this.lastMangledNameForLabelMap.get(label) ?? null; + + return lastMangledName ?? MangledIdentifierNamesGenerator.initMangledNameCharacter; + } } diff --git a/src/generators/identifier-names-generators/MangledShuffledIdentifierNamesGenerator.ts b/src/generators/identifier-names-generators/MangledShuffledIdentifierNamesGenerator.ts index eeef9b346..aeeaf5928 100644 --- a/src/generators/identifier-names-generators/MangledShuffledIdentifierNamesGenerator.ts +++ b/src/generators/identifier-names-generators/MangledShuffledIdentifierNamesGenerator.ts @@ -4,6 +4,7 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers'; import { IArrayUtils } from '../../interfaces/utils/IArrayUtils'; import { IOptions } from '../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; +import { ISetUtils } from '../../interfaces/utils/ISetUtils'; import { numbersString } from '../../constants/NumbersString'; import { alphabetString } from '../../constants/AlphabetString'; @@ -27,13 +28,15 @@ export class MangledShuffledIdentifierNamesGenerator extends MangledIdentifierNa * @param {IArrayUtils} arrayUtils * @param {IRandomGenerator} randomGenerator * @param {IOptions} options + * @param {ISetUtils} setUtils */ public constructor ( @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils, @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, - @inject(ServiceIdentifiers.IOptions) options: IOptions + @inject(ServiceIdentifiers.IOptions) options: IOptions, + @inject(ServiceIdentifiers.ISetUtils) setUtils: ISetUtils ) { - super(randomGenerator, options); + super(randomGenerator, options, setUtils); this.arrayUtils = arrayUtils; } @@ -61,12 +64,4 @@ export class MangledShuffledIdentifierNamesGenerator extends MangledIdentifierNa protected override getNameSequence (): string[] { return MangledShuffledIdentifierNamesGenerator.shuffledNameSequence; } - - /** - * @param {string} previousMangledName - * @returns {string} - */ - protected override generateNewMangledName (previousMangledName: string): string { - return super.generateNewMangledName(previousMangledName); - } } diff --git a/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts b/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts index 2791e3ac5..f0d2f8974 100644 --- a/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts +++ b/src/interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator.ts @@ -21,6 +21,12 @@ export interface IIdentifierNamesGenerator { */ generateForLexicalScope (lexicalScopeNode: TNodeWithLexicalScope, nameLength?: number): string; + /** + * @param {string} label + * @param {number} nameLength + * @returns {string} + */ + generateForLabel (label: string, nameLength?: number): string; /** * @param {number} nameLength diff --git a/src/interfaces/node-transformers/control-flow-transformers/IControlFlowReplacer.ts b/src/interfaces/node-transformers/control-flow-transformers/IControlFlowReplacer.ts index a3881ebe6..67c183f88 100644 --- a/src/interfaces/node-transformers/control-flow-transformers/IControlFlowReplacer.ts +++ b/src/interfaces/node-transformers/control-flow-transformers/IControlFlowReplacer.ts @@ -1,23 +1,23 @@ import * as ESTree from 'estree'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; +import { IControlFlowStorage } from '../../storages/control-flow-transformers/IControlFlowStorage'; export interface IControlFlowReplacer { /** * @param {Node} node * @param {Node} parentNode - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @returns {Node} */ replace ( node: ESTree.Node, parentNode: ESTree.Node, - controlFlowStorage: TControlFlowStorage + controlFlowStorage: IControlFlowStorage ): ESTree.Node; /** * @param {TControlFlowStorage} controlFlowStorage * @returns {string} */ - generateStorageKey (controlFlowStorage: TControlFlowStorage): string; + generateStorageKey (controlFlowStorage: IControlFlowStorage): string; } diff --git a/src/interfaces/options/IOptions.ts b/src/interfaces/options/IOptions.ts index fdec2b61a..4675d5c7d 100644 --- a/src/interfaces/options/IOptions.ts +++ b/src/interfaces/options/IOptions.ts @@ -48,6 +48,8 @@ export interface IOptions { readonly splitStrings: boolean; readonly splitStringsChunkLength: number; readonly stringArray: boolean; + readonly stringArrayCallsTransform: boolean; + readonly stringArrayCallsTransformThreshold: number; readonly stringArrayEncoding: TStringArrayEncoding[]; readonly stringArrayIndexesType: TStringArrayIndexesType[]; readonly stringArrayIndexShift: boolean; diff --git a/src/interfaces/storages/control-flow-transformers/IControlFlowStorage.ts b/src/interfaces/storages/control-flow-transformers/IControlFlowStorage.ts new file mode 100644 index 000000000..7c59d6df7 --- /dev/null +++ b/src/interfaces/storages/control-flow-transformers/IControlFlowStorage.ts @@ -0,0 +1,5 @@ +import { IMapStorage } from '../IMapStorage'; +import { ICustomNode } from '../../custom-nodes/ICustomNode'; + +// eslint-disable-next-line +export interface IControlFlowStorage extends IMapStorage {} diff --git a/src/interfaces/utils/ISetUtils.ts b/src/interfaces/utils/ISetUtils.ts new file mode 100644 index 000000000..7a81941e1 --- /dev/null +++ b/src/interfaces/utils/ISetUtils.ts @@ -0,0 +1,7 @@ +export interface ISetUtils { + /** + * @param {Set} set + * @returns {T | undefined} + */ + getLastElement (set: Set): T | undefined; +} diff --git a/src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts b/src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts index cb0299010..1e656219b 100644 --- a/src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts +++ b/src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts @@ -6,23 +6,31 @@ import * as ESTree from 'estree'; import { TControlFlowCustomNodeFactory } from '../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; import { TControlFlowReplacerFactory } from '../../types/container/node-transformers/TControlFlowReplacerFactory'; -import { TControlFlowStorage } from '../../types/storages/TControlFlowStorage'; import { TControlFlowStorageFactory } from '../../types/container/node-transformers/TControlFlowStorageFactory'; +import { + TControlFlowStorageFactoryCreator +} from '../../types/container/node-transformers/TControlFlowStorageFactoryCreator'; import { TInitialData } from '../../types/TInitialData'; import { TNodeWithStatements } from '../../types/node/TNodeWithStatements'; +import { IControlFlowStorage } from '../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode'; import { IOptions } from '../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; import { IVisitor } from '../../interfaces/node-transformers/IVisitor'; import { ControlFlowCustomNode } from '../../enums/custom-nodes/ControlFlowCustomNode'; -import { ControlFlowReplacer } from '../../enums/node-transformers/control-flow-transformers/control-flow-replacers/ControlFlowReplacer'; +import { + ControlFlowReplacer +} from '../../enums/node-transformers/control-flow-transformers/control-flow-replacers/ControlFlowReplacer'; +import { ControlFlowStorage } from '../../enums/storages/ControlFlowStorage'; import { NodeType } from '../../enums/node/NodeType'; import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage'; import { AbstractNodeTransformer } from '../AbstractNodeTransformer'; -import { ControlFlowStorageNode } from '../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode'; +import { + ControlFlowStorageNode +} from '../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ControlFlowStorageNode'; import { NodeAppender } from '../../node/NodeAppender'; import { NodeGuards } from '../../node/NodeGuards'; import { NodeMetadata } from '../../node/NodeMetadata'; @@ -31,16 +39,6 @@ import { NodeUtils } from '../../node/NodeUtils'; @injectable() export class FunctionControlFlowTransformer extends AbstractNodeTransformer { - /** - * @type {Map } - */ - private static readonly controlFlowReplacersMap: Map = new Map([ - [NodeType.BinaryExpression, ControlFlowReplacer.BinaryExpressionControlFlowReplacer], - [NodeType.CallExpression, ControlFlowReplacer.CallExpressionControlFlowReplacer], - [NodeType.LogicalExpression, ControlFlowReplacer.LogicalExpressionControlFlowReplacer], - [NodeType.Literal, ControlFlowReplacer.StringLiteralControlFlowReplacer] - ]); - /** * @type {number} */ @@ -52,37 +50,47 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer { private static readonly hostNodeSearchMaxDepth: number = 2; /** - * @type {Map} + * @type {Map } */ - private readonly controlFlowData: Map = new Map(); + protected readonly controlFlowReplacersMap: Map = new Map([ + [NodeType.BinaryExpression, ControlFlowReplacer.BinaryExpressionControlFlowReplacer], + [NodeType.CallExpression, ControlFlowReplacer.CallExpressionControlFlowReplacer], + [NodeType.LogicalExpression, ControlFlowReplacer.LogicalExpressionControlFlowReplacer], + [NodeType.Literal, ControlFlowReplacer.StringLiteralControlFlowReplacer] + ]); /** - * @type {Set} + * @type {WeakMap} */ - private readonly visitedFunctionNodes: Set = new Set(); + protected readonly controlFlowData: WeakMap = new WeakMap(); /** * @type {WeakMap} */ - private readonly hostNodesWithControlFlowNode: WeakMap = new WeakMap(); + protected readonly hostNodesWithControlFlowNode: WeakMap = new WeakMap(); /** * @type {TControlFlowReplacerFactory} */ - private readonly controlFlowReplacerFactory: TControlFlowReplacerFactory; + protected readonly controlFlowReplacerFactory: TControlFlowReplacerFactory; /** * @type {TControlFlowStorageFactory} */ - private readonly controlFlowStorageFactory: TControlFlowStorageFactory; + protected controlFlowStorageFactory: TControlFlowStorageFactory; /** * @type {TControlFlowCustomNodeFactory} */ - private readonly controlFlowCustomNodeFactory: TControlFlowCustomNodeFactory; + protected readonly controlFlowCustomNodeFactory: TControlFlowCustomNodeFactory; + + /** + * @type {WeakSet} + */ + protected readonly visitedFunctionNodes: WeakSet = new WeakSet(); /** - * @param {TControlFlowStorageFactory} controlFlowStorageFactory + * @param {TControlFlowStorageFactoryCreator} controlFlowStorageFactoryCreator * @param {TControlFlowReplacerFactory} controlFlowReplacerFactory * @param {TControlFlowCustomNodeFactory} controlFlowCustomNodeFactory * @param {IRandomGenerator} randomGenerator @@ -90,7 +98,7 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer { */ public constructor ( @inject(ServiceIdentifiers.Factory__TControlFlowStorage) - controlFlowStorageFactory: TControlFlowStorageFactory, + controlFlowStorageFactoryCreator: TControlFlowStorageFactoryCreator, @inject(ServiceIdentifiers.Factory__IControlFlowReplacer) controlFlowReplacerFactory: TControlFlowReplacerFactory, @inject(ServiceIdentifiers.Factory__IControlFlowCustomNode) @@ -100,7 +108,7 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer { ) { super(randomGenerator, options); - this.controlFlowStorageFactory = controlFlowStorageFactory; + this.controlFlowStorageFactory = controlFlowStorageFactoryCreator(ControlFlowStorage.FunctionControlFlowStorage); this.controlFlowReplacerFactory = controlFlowReplacerFactory; this.controlFlowCustomNodeFactory = controlFlowCustomNodeFactory; } @@ -110,17 +118,18 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer { * @returns {IVisitor | null} */ public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null { + if (!this.options.controlFlowFlattening) { + return null; + } + switch (nodeTransformationStage) { case NodeTransformationStage.ControlFlowFlattening: return { - leave: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => { - if ( - parentNode && ( - NodeGuards.isFunctionDeclarationNode(node) || - NodeGuards.isFunctionExpressionNode(node) || - NodeGuards.isArrowFunctionExpressionNode(node) - ) - ) { + leave: ( + node: ESTree.Node, + parentNode: ESTree.Node | null + ): ESTree.Node | estraverse.VisitorOption | void => { + if (parentNode && NodeGuards.isFunctionNode(node)) { return this.transformNode(node, parentNode); } } @@ -133,7 +142,7 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer { /** * @param {Function} functionNode - * @param {NodeGuards} parentNode + * @param {Node} parentNode * @returns {Function} */ public transformNode (functionNode: ESTree.Function, parentNode: ESTree.Node): ESTree.Function { @@ -144,77 +153,81 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer { } const hostNode: TNodeWithStatements = this.getHostNode(functionNode.body); - const controlFlowStorage: TControlFlowStorage = this.getControlFlowStorage(hostNode); + const controlFlowStorage: IControlFlowStorage = this.getControlFlowStorage(hostNode); - this.controlFlowData.set(hostNode, controlFlowStorage); - this.transformFunctionBody(functionNode.body, controlFlowStorage); + this.transformFunctionBody(functionNode, controlFlowStorage); if (!controlFlowStorage.getLength()) { return functionNode; } - const controlFlowStorageCustomNode: ICustomNode> = - this.controlFlowCustomNodeFactory(ControlFlowCustomNode.ControlFlowStorageNode); - - controlFlowStorageCustomNode.initialize(controlFlowStorage); - const controlFlowStorageNode: ESTree.VariableDeclaration = this.getControlFlowStorageNode(controlFlowStorage); - NodeAppender.prepend(hostNode, [controlFlowStorageNode]); - this.hostNodesWithControlFlowNode.set(hostNode, controlFlowStorageNode); - - NodeUtils.parentizeAst(functionNode); + this.appendControlFlowStorageNode(hostNode, controlFlowStorageNode); return functionNode; } /** - * @param {TNodeWithStatements} hostNode - * @returns {TControlFlowStorage} + * @param {BlockStatement} functionNode + * @param {IControlFlowStorage} controlFlowStorage */ - private getControlFlowStorage (hostNode: TNodeWithStatements): TControlFlowStorage { - const controlFlowStorage: TControlFlowStorage = this.controlFlowStorageFactory(); - - if (this.controlFlowData.has(hostNode)) { - const existingControlFlowStorageNode: ESTree.VariableDeclaration | null = - this.hostNodesWithControlFlowNode.get(hostNode) ?? null; - - if (existingControlFlowStorageNode) { - NodeAppender.remove(hostNode, existingControlFlowStorageNode); - } - - const hostControlFlowStorage: TControlFlowStorage = this.controlFlowData.get(hostNode); - - controlFlowStorage.mergeWith(hostControlFlowStorage, true); - } - - return controlFlowStorage; + protected transformFunctionBody (functionNode: ESTree.Function, controlFlowStorage: IControlFlowStorage): void { + estraverse.replace(functionNode.body, { + enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | ESTree.Node => + this.transformFunctionBodyNode(node, parentNode, functionNode, controlFlowStorage) + }); } /** - * @param {TControlFlowStorage} controlFlowStorage - * @returns {VariableDeclaration} + * @param {Node} node + * @param {Node | null} parentNode + * @param {Function} functionNode + * @param {IControlFlowStorage} controlFlowStorage + * @returns {ESTraverse.VisitorOption | Node} */ - private getControlFlowStorageNode (controlFlowStorage: TControlFlowStorage): ESTree.VariableDeclaration { - const controlFlowStorageCustomNode: ICustomNode> = - this.controlFlowCustomNodeFactory(ControlFlowCustomNode.ControlFlowStorageNode); + protected transformFunctionBodyNode ( + node: ESTree.Node, + parentNode: ESTree.Node | null, + functionNode: ESTree.Function, + controlFlowStorage: IControlFlowStorage + ): estraverse.VisitorOption | ESTree.Node { + const shouldSkipTraverse = !parentNode + || NodeMetadata.isIgnoredNode(node) + || this.isVisitedFunctionNode(node); + + if (shouldSkipTraverse) { + return estraverse.VisitorOption.Skip; + } - controlFlowStorageCustomNode.initialize(controlFlowStorage); + const controlFlowReplacerName: ControlFlowReplacer | null = this.controlFlowReplacersMap.get(node.type) + ?? null; - const controlFlowStorageNode: ESTree.Node = controlFlowStorageCustomNode.getNode()[0]; + if (!controlFlowReplacerName) { + return node; + } - if (!NodeGuards.isVariableDeclarationNode(controlFlowStorageNode)) { - throw new Error('`controlFlowStorageNode` should contain `VariableDeclaration` node with control flow storage object'); + if (!this.isAllowedTransformationByThreshold()) { + return node; } - return controlFlowStorageNode; + const replacedNode: ESTree.Node = this.controlFlowReplacerFactory(controlFlowReplacerName) + .replace( + node, + parentNode, + controlFlowStorage + ); + + NodeUtils.parentizeNode(replacedNode, parentNode); + + return replacedNode; } /** * @param {BlockStatement} functionNodeBody * @returns {TNodeWithStatements} */ - private getHostNode (functionNodeBody: ESTree.BlockStatement): TNodeWithStatements { + protected getHostNode (functionNodeBody: ESTree.BlockStatement): TNodeWithStatements { const blockScopesOfNode: TNodeWithStatements[] = NodeStatementUtils.getParentNodesWithStatements(functionNodeBody); if (blockScopesOfNode.length === 1) { @@ -235,52 +248,77 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer { } /** - * @param {NodeGuards} node - * @returns {boolean} + * @param {TNodeWithStatements} hostNode + * @returns {TControlFlowStorage} */ - private isVisitedFunctionNode (node: ESTree.Node): boolean { - return ( - NodeGuards.isFunctionDeclarationNode(node) || - NodeGuards.isFunctionExpressionNode(node) || - NodeGuards.isArrowFunctionExpressionNode(node) - ) && this.visitedFunctionNodes.has(node); + protected getControlFlowStorage (hostNode: TNodeWithStatements): IControlFlowStorage { + let controlFlowStorage: IControlFlowStorage; + + const hostControlFlowStorage: IControlFlowStorage | null = this.controlFlowData.get(hostNode) ?? null; + + if (!hostControlFlowStorage) { + controlFlowStorage = this.controlFlowStorageFactory(); + } else { + const existingControlFlowStorageNode: ESTree.VariableDeclaration | null = + this.hostNodesWithControlFlowNode.get(hostNode) ?? null; + + if (existingControlFlowStorageNode) { + NodeAppender.remove(hostNode, existingControlFlowStorageNode); + } + + controlFlowStorage = hostControlFlowStorage; + } + + this.controlFlowData.set(hostNode, controlFlowStorage); + + return controlFlowStorage; } /** - * @param {BlockStatement} functionNodeBody - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage + * @returns {VariableDeclaration} */ - private transformFunctionBody (functionNodeBody: ESTree.BlockStatement, controlFlowStorage: TControlFlowStorage): void { - estraverse.replace(functionNodeBody, { - enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | ESTree.Node => { - if (NodeMetadata.isIgnoredNode(node)) { - return estraverse.VisitorOption.Skip; - } + protected getControlFlowStorageNode (controlFlowStorage: IControlFlowStorage): ESTree.VariableDeclaration { + const controlFlowStorageCustomNode: ICustomNode> = + this.controlFlowCustomNodeFactory(ControlFlowCustomNode.ControlFlowStorageNode); - if (this.isVisitedFunctionNode(node) || !parentNode) { - return estraverse.VisitorOption.Skip; - } + controlFlowStorageCustomNode.initialize(controlFlowStorage); - if (!FunctionControlFlowTransformer.controlFlowReplacersMap.has(node.type)) { - return node; - } + const controlFlowStorageNode: ESTree.Node = controlFlowStorageCustomNode.getNode()[0]; - if (this.randomGenerator.getMathRandom() > this.options.controlFlowFlatteningThreshold) { - return node; - } + if (!NodeGuards.isVariableDeclarationNode(controlFlowStorageNode)) { + throw new Error('`controlFlowStorageNode` should contain `VariableDeclaration` node with control flow storage object'); + } - const controlFlowReplacerName: ControlFlowReplacer = FunctionControlFlowTransformer - .controlFlowReplacersMap.get(node.type); + return controlFlowStorageNode; + } - if (controlFlowReplacerName === undefined) { - return node; - } + /** + * @param {TNodeWithStatements} hostNode + * @param {VariableDeclaration} controlFlowStorageNode + */ + protected appendControlFlowStorageNode ( + hostNode: TNodeWithStatements, + controlFlowStorageNode: ESTree.VariableDeclaration + ): void { + NodeUtils.parentizeAst(controlFlowStorageNode); + NodeAppender.prepend(hostNode, [controlFlowStorageNode]); - return { - ...this.controlFlowReplacerFactory(controlFlowReplacerName).replace(node, parentNode, controlFlowStorage), - parentNode - }; - } - }); + this.hostNodesWithControlFlowNode.set(hostNode, controlFlowStorageNode); + } + + /** + * @param {NodeGuards} node + * @returns {boolean} + */ + protected isVisitedFunctionNode (node: ESTree.Node): boolean { + return NodeGuards.isFunctionNode(node) && this.visitedFunctionNodes.has(node); + } + + /** + * @returns {boolean} + */ + protected isAllowedTransformationByThreshold (): boolean { + return this.randomGenerator.getMathRandom() <= this.options.controlFlowFlatteningThreshold; } } diff --git a/src/node-transformers/control-flow-transformers/StringArrayControlFlowTransformer.ts b/src/node-transformers/control-flow-transformers/StringArrayControlFlowTransformer.ts new file mode 100644 index 000000000..974380381 --- /dev/null +++ b/src/node-transformers/control-flow-transformers/StringArrayControlFlowTransformer.ts @@ -0,0 +1,148 @@ +import { inject, injectable, } from 'inversify'; +import { ServiceIdentifiers } from '../../container/ServiceIdentifiers'; + +import * as estraverse from '@javascript-obfuscator/estraverse'; +import * as ESTree from 'estree'; + +import { TControlFlowCustomNodeFactory } from '../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; +import { TControlFlowReplacerFactory } from '../../types/container/node-transformers/TControlFlowReplacerFactory'; +import { + TControlFlowStorageFactoryCreator +} from '../../types/container/node-transformers/TControlFlowStorageFactoryCreator'; +import { TNodeWithStatements } from '../../types/node/TNodeWithStatements'; + +import { IOptions } from '../../interfaces/options/IOptions'; +import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; +import { IVisitor } from '../../interfaces/node-transformers/IVisitor'; + +import { ControlFlowReplacer } from '../../enums/node-transformers/control-flow-transformers/control-flow-replacers/ControlFlowReplacer'; +import { ControlFlowStorage } from '../../enums/storages/ControlFlowStorage'; +import { NodeType } from '../../enums/node/NodeType'; +import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage'; +import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer'; + +import { FunctionControlFlowTransformer } from './FunctionControlFlowTransformer'; +import { NodeGuards } from '../../node/NodeGuards'; +import { IControlFlowStorage } from '../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; + +@injectable() +export class StringArrayControlFlowTransformer extends FunctionControlFlowTransformer { + /** + * @type {NodeTransformer[]} + */ + public override readonly runAfter: NodeTransformer[] = [ + NodeTransformer.StringArrayTransformer, + NodeTransformer.StringArrayRotateFunctionTransformer, + NodeTransformer.StringArrayScopeCallsWrapperTransformer + ]; + + /** + * @type {Map } + */ + protected override readonly controlFlowReplacersMap: Map = new Map([ + [NodeType.Literal, ControlFlowReplacer.StringArrayCallControlFlowReplacer] + ]); + + /** + * @type {WeakSet} + */ + protected readonly controlFlowStorageNodes: WeakSet = new WeakSet(); + + /** + * @param {TControlFlowStorageFactoryCreator} controlFlowStorageFactoryCreator + * @param {TControlFlowReplacerFactory} controlFlowReplacerFactory + * @param {TControlFlowCustomNodeFactory} controlFlowCustomNodeFactory + * @param {IRandomGenerator} randomGenerator + * @param {IOptions} options + */ + public constructor ( + @inject(ServiceIdentifiers.Factory__TControlFlowStorage) + controlFlowStorageFactoryCreator: TControlFlowStorageFactoryCreator, + @inject(ServiceIdentifiers.Factory__IControlFlowReplacer) + controlFlowReplacerFactory: TControlFlowReplacerFactory, + @inject(ServiceIdentifiers.Factory__IControlFlowCustomNode) + controlFlowCustomNodeFactory: TControlFlowCustomNodeFactory, + @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, + @inject(ServiceIdentifiers.IOptions) options: IOptions + ) { + super( + controlFlowStorageFactoryCreator, + controlFlowReplacerFactory, + controlFlowCustomNodeFactory, + randomGenerator, + options + ); + + this.controlFlowStorageFactory = controlFlowStorageFactoryCreator(ControlFlowStorage.StringControlFlowStorage); + } + + /** + * @param {NodeTransformationStage} nodeTransformationStage + * @returns {IVisitor | null} + */ + public override getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null { + if (!this.options.stringArrayCallsTransform) { + return null; + } + + switch (nodeTransformationStage) { + case NodeTransformationStage.StringArray: + return { + leave: ( + node: ESTree.Node, + parentNode: ESTree.Node | null + ): ESTree.Node | estraverse.VisitorOption | void => { + if (parentNode && NodeGuards.isFunctionNode(node)) { + return this.transformNode(node, parentNode); + } + } + }; + + default: + return null; + } + } + + /** + * @param {Node} node + * @param {Node | null} parentNode + * @param {Function} functionNode + * @param {IControlFlowStorage} controlFlowStorage + * @returns {ESTraverse.VisitorOption | Node} + */ + protected override transformFunctionBodyNode ( + node: ESTree.Node, + parentNode: ESTree.Node | null, + functionNode: ESTree.Function, + controlFlowStorage: IControlFlowStorage + ): estraverse.VisitorOption | ESTree.Node { + const isControlFlowStorageNode = NodeGuards.isVariableDeclarationNode(node) + && this.controlFlowStorageNodes.has(node); + + if (isControlFlowStorageNode) { + return estraverse.VisitorOption.Break; + } + + return super.transformFunctionBodyNode(node, parentNode, functionNode, controlFlowStorage); + } + + /** + * @param {TNodeWithStatements} hostNode + * @param {VariableDeclaration} controlFlowStorageNode + */ + protected override appendControlFlowStorageNode ( + hostNode: TNodeWithStatements, + controlFlowStorageNode: ESTree.VariableDeclaration + ): void { + super.appendControlFlowStorageNode(hostNode, controlFlowStorageNode); + + this.controlFlowStorageNodes.add(controlFlowStorageNode); + } + + /** + * @returns {boolean} + */ + protected override isAllowedTransformationByThreshold (): boolean { + return this.randomGenerator.getMathRandom() <= this.options.stringArrayCallsTransformThreshold; + } +} 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 9c98d5367..4f510184c 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 @@ -4,10 +4,10 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers'; import * as ESTree from 'estree'; import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory'; import { IControlFlowReplacer } from '../../../interfaces/node-transformers/control-flow-transformers/IControlFlowReplacer'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode'; import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator'; import { IOptions } from '../../../interfaces/options/IOptions'; @@ -64,10 +64,10 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace * Generates storage key with length of 5 characters to prevent collisions and to guarantee that * these keys will be added to the string array storage * - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @returns {string} */ - public generateStorageKey (controlFlowStorage: TControlFlowStorage): string { + public generateStorageKey (controlFlowStorage: IControlFlowStorage): string { const key: string = this.randomGenerator.getRandomString(5); if (controlFlowStorage.has(key)) { @@ -79,14 +79,14 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace /** * @param {ICustomNode} customNode - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @param {string} replacerId * @param {number} usingExistingIdentifierChance * @returns {string} */ protected insertCustomNodeToControlFlowStorage ( customNode: ICustomNode, - controlFlowStorage: TControlFlowStorage, + controlFlowStorage: IControlFlowStorage, replacerId: string, usingExistingIdentifierChance: number ): string { @@ -115,12 +115,12 @@ export abstract class AbstractControlFlowReplacer implements IControlFlowReplace * @param {Node} node * @param {Node} parentNode * @param {TNodeWithLexicalScope} controlFlowStorageLexicalScopeNode - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @returns {Node} */ public abstract replace ( node: ESTree.Node, parentNode: ESTree.Node, - controlFlowStorage: TControlFlowStorage + controlFlowStorage: IControlFlowStorage ): ESTree.Node; } diff --git a/src/node-transformers/control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts b/src/node-transformers/control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts index e3df01c2e..d92e2a53f 100644 --- a/src/node-transformers/control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer.ts @@ -4,10 +4,10 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers'; import * as ESTree from 'estree'; import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory'; import { TInitialData } from '../../../types/TInitialData'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode'; import { IOptions } from '../../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator'; @@ -50,13 +50,13 @@ export class BinaryExpressionControlFlowReplacer extends ExpressionWithOperatorC * @param {BinaryExpression} binaryExpressionNode * @param {Node} parentNode * @param {TNodeWithLexicalScope} controlFlowStorageLexicalScopeNode - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @returns {Node} */ public replace ( binaryExpressionNode: ESTree.BinaryExpression, parentNode: ESTree.Node, - controlFlowStorage: TControlFlowStorage + controlFlowStorage: IControlFlowStorage ): ESTree.Node { const operator: ESTree.BinaryOperator = binaryExpressionNode.operator; const binaryExpressionFunctionCustomNode: ICustomNode> = 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 de4ef72d5..7196b4d30 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 @@ -4,11 +4,11 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers'; import * as ESTree from 'estree'; import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory'; import { TInitialData } from '../../../types/TInitialData'; import { TStatement } from '../../../types/node/TStatement'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode'; import { IOptions } from '../../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator'; @@ -52,13 +52,13 @@ export class CallExpressionControlFlowReplacer extends AbstractControlFlowReplac /** * @param {CallExpression} callExpressionNode * @param {Node} parentNode - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @returns {Node} */ public replace ( callExpressionNode: ESTree.CallExpression, parentNode: ESTree.Node, - controlFlowStorage: TControlFlowStorage + controlFlowStorage: IControlFlowStorage ): ESTree.Node { const callee: ESTree.Expression = callExpressionNode.callee; diff --git a/src/node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer.ts b/src/node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer.ts index a2403f9d1..ef24c3a69 100644 --- a/src/node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer.ts +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer.ts @@ -4,10 +4,10 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers'; import * as ESTree from 'estree'; import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory'; import { TInitialData } from '../../../types/TInitialData'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode'; import { IOptions } from '../../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator'; @@ -51,13 +51,13 @@ export class LogicalExpressionControlFlowReplacer extends ExpressionWithOperator /** * @param {LogicalExpression} logicalExpressionNode * @param {Node} parentNode - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @returns {Node} */ public replace ( logicalExpressionNode: ESTree.LogicalExpression, parentNode: ESTree.Node, - controlFlowStorage: TControlFlowStorage + controlFlowStorage: IControlFlowStorage ): ESTree.Node { if (this.checkForProhibitedExpressions(logicalExpressionNode.left, logicalExpressionNode.right)) { return logicalExpressionNode; 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 new file mode 100644 index 000000000..cbe64f5e8 --- /dev/null +++ b/src/node-transformers/control-flow-transformers/control-flow-replacers/StringArrayCallControlFlowReplacer.ts @@ -0,0 +1,132 @@ +import { inject, injectable, } from 'inversify'; +import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers'; + +import * as ESTree from 'estree'; + +import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; +import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory'; +import { TInitialData } from '../../../types/TInitialData'; +import { TStatement } from '../../../types/node/TStatement'; + +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; +import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode'; +import { IOptions } from '../../../interfaces/options/IOptions'; +import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator'; + +import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCustomNode'; + +import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer'; +import { NodeGuards } from '../../../node/NodeGuards'; +import { NodeLiteralUtils } from '../../../node/NodeLiteralUtils'; +import { NodeMetadata } from '../../../node/NodeMetadata'; +import { + StringLiteralControlFlowStorageCallNode +} from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode'; +import { LiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/LiteralNode'; + +@injectable() +export class StringArrayCallControlFlowReplacer extends AbstractControlFlowReplacer { + /** + * @type {number} + */ + private static readonly usingExistingIdentifierChance: number = 0.5; + + /** + * @param {TControlFlowCustomNodeFactory} controlFlowCustomNodeFactory + * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory + * @param {IRandomGenerator} randomGenerator + * @param {IOptions} options + */ + public constructor ( + @inject(ServiceIdentifiers.Factory__IControlFlowCustomNode) + controlFlowCustomNodeFactory: TControlFlowCustomNodeFactory, + @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator) + identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory, + @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, + @inject(ServiceIdentifiers.IOptions) options: IOptions + ) { + super( + controlFlowCustomNodeFactory, + identifierNamesGeneratorFactory, + randomGenerator, + options + ); + } + + /** + * @param {Literal} literalNode + * @param {Node} parentNode + * @param {IControlFlowStorage} controlFlowStorage + * @returns {Node} + */ + public override replace ( + literalNode: ESTree.Literal, + parentNode: ESTree.Node, + controlFlowStorage: IControlFlowStorage + ): ESTree.Node { + const isStringArrayCallLiteralNode = NodeMetadata.isStringArrayCallLiteralNode(literalNode) + && ( + NodeLiteralUtils.isNumberLiteralNode(literalNode) + || NodeLiteralUtils.isStringLiteralNode(literalNode) + ); + + if (!isStringArrayCallLiteralNode) { + return literalNode; + } + + const replacerId: string = String(literalNode.value); + const literalCustomNode: ICustomNode> = + this.controlFlowCustomNodeFactory(ControlFlowCustomNode.LiteralNode); + + literalCustomNode.initialize(literalNode); + + const storageKey: string = this.insertCustomNodeToControlFlowStorage( + literalCustomNode, + controlFlowStorage, + replacerId, + StringArrayCallControlFlowReplacer.usingExistingIdentifierChance + ); + + return this.getControlFlowStorageCallNode(controlFlowStorage.getStorageId(), storageKey); + } + + /** + * Generates storage key based on a current control flow storage identifier + * + * @param {IControlFlowStorage} controlFlowStorage + * @returns {string} + */ + public override generateStorageKey (controlFlowStorage: IControlFlowStorage): string { + const key: string = this.identifierNamesGenerator + .generateForLabel(controlFlowStorage.getStorageId()); + + if (controlFlowStorage.has(key)) { + return this.generateStorageKey(controlFlowStorage); + } + + return key; + } + + /** + * @param {string} controlFlowStorageId + * @param {string} storageKey + * @returns {NodeGuards} + */ + protected getControlFlowStorageCallNode ( + controlFlowStorageId: string, + storageKey: string + ): ESTree.Node { + const controlFlowStorageCallCustomNode: ICustomNode> = + this.controlFlowCustomNodeFactory(ControlFlowCustomNode.StringLiteralControlFlowStorageCallNode); + + controlFlowStorageCallCustomNode.initialize(controlFlowStorageId, storageKey); + + const statementNode: TStatement = controlFlowStorageCallCustomNode.getNode()[0]; + + if (!statementNode || !NodeGuards.isExpressionStatementNode(statementNode)) { + throw new Error('`controlFlowStorageCallCustomNode.getNode()[0]` should returns array with `ExpressionStatement` node'); + } + + return statementNode.expression; + } +} 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 061618f4f..5aa05ff65 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 @@ -4,11 +4,11 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers'; import * as ESTree from 'estree'; import { TControlFlowCustomNodeFactory } from '../../../types/container/custom-nodes/TControlFlowCustomNodeFactory'; -import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage'; import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory'; import { TInitialData } from '../../../types/TInitialData'; import { TStatement } from '../../../types/node/TStatement'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode'; import { IOptions } from '../../../interfaces/options/IOptions'; import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator'; @@ -19,7 +19,7 @@ import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer'; import { NodeGuards } from '../../../node/NodeGuards'; import { NodeLiteralUtils } from '../../../node/NodeLiteralUtils'; import { StringLiteralControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode'; -import { StringLiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/StringLiteralNode'; +import { LiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/LiteralNode'; @injectable() export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplacer { @@ -53,13 +53,13 @@ export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplace /** * @param {Literal} literalNode * @param {Node} parentNode - * @param {TControlFlowStorage} controlFlowStorage + * @param {IControlFlowStorage} controlFlowStorage * @returns {Node} */ public replace ( literalNode: ESTree.Literal, parentNode: ESTree.Node, - controlFlowStorage: TControlFlowStorage + controlFlowStorage: IControlFlowStorage ): ESTree.Node { if (NodeGuards.isPropertyNode(parentNode) && parentNode.key === literalNode) { return literalNode; @@ -70,13 +70,13 @@ export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplace } const replacerId: string = String(literalNode.value); - const literalFunctionCustomNode: ICustomNode> = - this.controlFlowCustomNodeFactory(ControlFlowCustomNode.StringLiteralNode); + const literalCustomNode: ICustomNode> = + this.controlFlowCustomNodeFactory(ControlFlowCustomNode.LiteralNode); - literalFunctionCustomNode.initialize(literalNode.value); + literalCustomNode.initialize(literalNode); const storageKey: string = this.insertCustomNodeToControlFlowStorage( - literalFunctionCustomNode, + literalCustomNode, controlFlowStorage, replacerId, StringLiteralControlFlowReplacer.usingExistingIdentifierChance diff --git a/src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer.ts b/src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer.ts index 3df7f6be9..eac0bcba8 100644 --- a/src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer.ts +++ b/src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer.ts @@ -197,7 +197,7 @@ export class StringArrayRotateFunctionTransformer extends AbstractNodeTransforme !NodeGuards.isLiteralNode(node) || !NodeLiteralUtils.isStringLiteralNode(node) ) { - return; + return; } // force add item data for string literal nodes of comparison expressions diff --git a/src/options/Options.ts b/src/options/Options.ts index 0a59a6e1f..d114fc838 100644 --- a/src/options/Options.ts +++ b/src/options/Options.ts @@ -328,6 +328,20 @@ export class Options implements IOptions { @IsBoolean() public readonly stringArray!: boolean; + /** + * @type {boolean} + */ + @IsBoolean() + public readonly stringArrayCallsTransform!: boolean; + + /** + * @type {number} + */ + @IsNumber() + @Min(0) + @Max(1) + public readonly stringArrayCallsTransformThreshold!: number; + /** * @type {TStringArrayEncoding[]} */ diff --git a/src/options/OptionsNormalizer.ts b/src/options/OptionsNormalizer.ts index 84d269cbc..4a1490a5e 100644 --- a/src/options/OptionsNormalizer.ts +++ b/src/options/OptionsNormalizer.ts @@ -18,6 +18,7 @@ import { SourceMapBaseUrlRule } from './normalizer-rules/SourceMapBaseUrlRule'; import { SourceMapFileNameRule } from './normalizer-rules/SourceMapFileNameRule'; import { SplitStringsChunkLengthRule } from './normalizer-rules/SplitStringsChunkLengthRule'; import { StringArrayRule } from './normalizer-rules/StringArrayRule'; +import { StringArrayCallsTransformRule } from './normalizer-rules/StringArrayCallsTransform'; import { StringArrayEncodingRule } from './normalizer-rules/StringArrayEncodingRule'; import { StringArrayWrappersChainedCallsRule } from './normalizer-rules/StringArrayWappersChainedCalls'; @@ -40,6 +41,7 @@ export class OptionsNormalizer implements IOptionsNormalizer { SourceMapFileNameRule, SplitStringsChunkLengthRule, StringArrayRule, + StringArrayCallsTransformRule, StringArrayEncodingRule, StringArrayWrappersChainedCallsRule, ]; diff --git a/src/options/normalizer-rules/StringArrayCallsTransform.ts b/src/options/normalizer-rules/StringArrayCallsTransform.ts new file mode 100644 index 000000000..e606ba282 --- /dev/null +++ b/src/options/normalizer-rules/StringArrayCallsTransform.ts @@ -0,0 +1,19 @@ +import { TOptionsNormalizerRule } from '../../types/options/TOptionsNormalizerRule'; + +import { IOptions } from '../../interfaces/options/IOptions'; + +/** + * @param {IOptions} options + * @returns {IOptions} + */ +export const StringArrayCallsTransformRule: TOptionsNormalizerRule = (options: IOptions): IOptions => { + if (!options.stringArrayCallsTransform) { + options = { + ...options, + stringArrayCallsTransform: false, + stringArrayCallsTransformThreshold: 0 + }; + } + + return options; +}; diff --git a/src/options/normalizer-rules/StringArrayRule.ts b/src/options/normalizer-rules/StringArrayRule.ts index 280ab0e53..b766ae8bb 100644 --- a/src/options/normalizer-rules/StringArrayRule.ts +++ b/src/options/normalizer-rules/StringArrayRule.ts @@ -13,6 +13,8 @@ export const StringArrayRule: TOptionsNormalizerRule = (options: IOptions): IOpt options = { ...options, stringArray: false, + stringArrayCallsTransform: false, + stringArrayCallsTransformThreshold: 0, stringArrayEncoding: [ StringArrayEncoding.None ], diff --git a/src/options/presets/Default.ts b/src/options/presets/Default.ts index c5c193dc9..fc84f53be 100644 --- a/src/options/presets/Default.ts +++ b/src/options/presets/Default.ts @@ -51,6 +51,8 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({ splitStrings: false, splitStringsChunkLength: 10, stringArray: true, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 0.5, stringArrayEncoding: [ StringArrayEncoding.None ], diff --git a/src/options/presets/HighObfuscation.ts b/src/options/presets/HighObfuscation.ts index 0a01af737..e704b8902 100644 --- a/src/options/presets/HighObfuscation.ts +++ b/src/options/presets/HighObfuscation.ts @@ -13,6 +13,7 @@ export const HIGH_OBFUSCATION_PRESET: TInputOptions = Object.freeze({ debugProtectionInterval: true, optionsPreset: OptionsPreset.HighObfuscation, splitStringsChunkLength: 5, + stringArrayCallsTransformThreshold: 1, stringArrayEncoding: [ StringArrayEncoding.Rc4 ], diff --git a/src/options/presets/LowObfuscation.ts b/src/options/presets/LowObfuscation.ts index d144739ab..6c76496b4 100644 --- a/src/options/presets/LowObfuscation.ts +++ b/src/options/presets/LowObfuscation.ts @@ -10,6 +10,8 @@ export const LOW_OBFUSCATION_PRESET: TInputOptions = Object.freeze({ optionsPreset: OptionsPreset.LowObfuscation, stringArrayRotate: true, selfDefending: true, - stringArrayShuffle: true, - simplify: true + simplify: true, + stringArrayCallsTransform: false, + stringArrayCallsTransformThreshold: 0, + stringArrayShuffle: true }); diff --git a/src/options/presets/MediumObfuscation.ts b/src/options/presets/MediumObfuscation.ts index a88044e6b..721a1cfe9 100644 --- a/src/options/presets/MediumObfuscation.ts +++ b/src/options/presets/MediumObfuscation.ts @@ -14,6 +14,7 @@ export const MEDIUM_OBFUSCATION_PRESET: TInputOptions = Object.freeze({ optionsPreset: OptionsPreset.MediumObfuscation, splitStrings: true, splitStringsChunkLength: 10, + stringArrayCallsTransformThreshold: 0.75, stringArrayEncoding: [ StringArrayEncoding.Base64 ], diff --git a/src/options/presets/NoCustomNodes.ts b/src/options/presets/NoCustomNodes.ts index e808f8beb..2c95f930f 100644 --- a/src/options/presets/NoCustomNodes.ts +++ b/src/options/presets/NoCustomNodes.ts @@ -47,6 +47,8 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({ splitStrings: false, splitStringsChunkLength: 0, stringArray: false, + stringArrayCallsTransform: false, + stringArrayCallsTransformThreshold: 0, stringArrayEncoding: [ StringArrayEncoding.None ], diff --git a/src/storages/control-flow-transformers/FunctionControlFlowStorage.ts b/src/storages/control-flow-transformers/FunctionControlFlowStorage.ts new file mode 100644 index 000000000..655f23e11 --- /dev/null +++ b/src/storages/control-flow-transformers/FunctionControlFlowStorage.ts @@ -0,0 +1,38 @@ +import { inject, injectable } from 'inversify'; +import { ServiceIdentifiers } from '../../container/ServiceIdentifiers'; + +import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory'; + +import { IControlFlowStorage } from '../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; +import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode'; +import { + IIdentifierNamesGenerator +} from '../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator'; +import { IOptions } from '../../interfaces/options/IOptions'; +import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; + +import { MapStorage } from '../MapStorage'; + +@injectable() +export class FunctionControlFlowStorage extends MapStorage implements IControlFlowStorage { + /** + * @type {IIdentifierNamesGenerator} + */ + protected readonly identifierNamesGenerator: IIdentifierNamesGenerator; + + /** + * @param {IRandomGenerator} randomGenerator + * @param {IOptions} options + * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory + */ + public constructor ( + @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, + @inject(ServiceIdentifiers.IOptions) options: IOptions, + @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator) + identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory, + ) { + super(randomGenerator, options); + + this.identifierNamesGenerator = identifierNamesGeneratorFactory(options); + } +} diff --git a/src/storages/control-flow-transformers/StringControlFlowStorage.ts b/src/storages/control-flow-transformers/StringControlFlowStorage.ts new file mode 100644 index 000000000..5ff22c7d2 --- /dev/null +++ b/src/storages/control-flow-transformers/StringControlFlowStorage.ts @@ -0,0 +1,33 @@ +import { inject, injectable } from 'inversify'; +import { ServiceIdentifiers } from '../../container/ServiceIdentifiers'; + +import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory'; + +import { IOptions } from '../../interfaces/options/IOptions'; +import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; + +import { FunctionControlFlowStorage } from './FunctionControlFlowStorage'; + +@injectable() +export class StringControlFlowStorage extends FunctionControlFlowStorage { + /** + * @param {IRandomGenerator} randomGenerator + * @param {IOptions} options + * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory + */ + public constructor ( + + @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, + @inject(ServiceIdentifiers.IOptions) options: IOptions, + @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator) + identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory, + ) { + super(randomGenerator, options, identifierNamesGeneratorFactory); + } + + public override initialize (): void { + super.initialize(); + + this.storageId = this.identifierNamesGenerator.generateForGlobalScope(); + } +} diff --git a/src/storages/custom-nodes/ControlFlowStorage.ts b/src/storages/custom-nodes/ControlFlowStorage.ts deleted file mode 100644 index 1ee3a3580..000000000 --- a/src/storages/custom-nodes/ControlFlowStorage.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { inject, injectable, } from 'inversify'; -import { ServiceIdentifiers } from '../../container/ServiceIdentifiers'; - -import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode'; -import { IOptions } from '../../interfaces/options/IOptions'; -import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator'; - -import { MapStorage } from '../MapStorage'; - -@injectable() -export class ControlFlowStorage extends MapStorage { - /** - * @param {IRandomGenerator} randomGenerator - * @param {IOptions} options - */ - public constructor ( - @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator, - @inject(ServiceIdentifiers.IOptions) options: IOptions - ) { - super(randomGenerator, options); - } -} diff --git a/src/types/container/node-transformers/TControlFlowStorageFactory.ts b/src/types/container/node-transformers/TControlFlowStorageFactory.ts index 5b1b9e439..d7d82667b 100644 --- a/src/types/container/node-transformers/TControlFlowStorageFactory.ts +++ b/src/types/container/node-transformers/TControlFlowStorageFactory.ts @@ -1,3 +1,3 @@ -import { TControlFlowStorage } from '../../storages/TControlFlowStorage'; +import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage'; -export type TControlFlowStorageFactory = () => TControlFlowStorage; +export type TControlFlowStorageFactory = () => IControlFlowStorage; diff --git a/src/types/container/node-transformers/TControlFlowStorageFactoryCreator.ts b/src/types/container/node-transformers/TControlFlowStorageFactoryCreator.ts new file mode 100644 index 000000000..3a2a1ecbc --- /dev/null +++ b/src/types/container/node-transformers/TControlFlowStorageFactoryCreator.ts @@ -0,0 +1,5 @@ +import { TControlFlowStorageFactory } from './TControlFlowStorageFactory'; + +import { ControlFlowStorage } from '../../../enums/storages/ControlFlowStorage'; + +export type TControlFlowStorageFactoryCreator = (controlFlowStorageName: ControlFlowStorage) => TControlFlowStorageFactory; diff --git a/src/types/storages/TControlFlowStorage.ts b/src/types/storages/TControlFlowStorage.ts deleted file mode 100644 index 512491a6a..000000000 --- a/src/types/storages/TControlFlowStorage.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode'; -import { IMapStorage } from '../../interfaces/storages/IMapStorage'; - -export type TControlFlowStorage = IMapStorage ; diff --git a/src/utils/SetUtils.ts b/src/utils/SetUtils.ts new file mode 100644 index 000000000..e19ef24b0 --- /dev/null +++ b/src/utils/SetUtils.ts @@ -0,0 +1,32 @@ +import { inject, injectable } from 'inversify'; +import { ServiceIdentifiers } from '../container/ServiceIdentifiers'; + +import { IArrayUtils } from '../interfaces/utils/IArrayUtils'; +import { ISetUtils } from '../interfaces/utils/ISetUtils'; + +@injectable() +export class SetUtils implements ISetUtils { + /** + * @type {IArrayUtils} + */ + private readonly arrayUtils: IArrayUtils; + + /** + * @param {IArrayUtils} arrayUtils + */ + public constructor ( + @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils + ) { + this.arrayUtils = arrayUtils; + } + + /** + * @param {Set} set + * @returns {T | undefined} + */ + public getLastElement (set: Set): T | undefined { + const array = [...set]; + + return this.arrayUtils.getLastElement(array); + } +} diff --git a/test/dev/dev.ts b/test/dev/dev.ts index 584cb81e6..611ca670a 100644 --- a/test/dev/dev.ts +++ b/test/dev/dev.ts @@ -1,22 +1,46 @@ 'use strict'; +import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType'; + (function () { const JavaScriptObfuscator: any = require('../../index'); - const code: string = ` - class Foo {} - `; - let obfuscationResult = JavaScriptObfuscator.obfuscate( - code, + let obfuscatedCode: string = JavaScriptObfuscator.obfuscate( + ` + function foo () { + var bar = 'bar'; + + function baz () { + var baz = 'baz'; + + return baz; + } + + return bar + baz(); + } + + console.log(foo()); + `, { - compact: false + identifierNamesGenerator: 'mangled', + compact: false, + controlFlowFlattening: true, + controlFlowFlatteningThreshold: 1, + simplify: false, + stringArray: true, + stringArrayIndexesType: [ + 'hexadecimal-number', + 'hexadecimal-numeric-string' + ], + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1, + rotateStringArray: true, + stringArrayWrappersType: StringArrayWrappersType.Function, + transformObjectKeys: true } - ); - - let obfuscatedCode: string = obfuscationResult.getObfuscatedCode(); - let identifierNamesCache = obfuscationResult.getIdentifierNamesCache(); + ).getObfuscatedCode(); console.log(obfuscatedCode); console.log(eval(obfuscatedCode)); - console.log(identifierNamesCache); -})(); \ No newline at end of file +})(); diff --git a/test/functional-tests/generators/identifier-names-generators/dictionary-identifier-names-generator/DictionaryIdentifierNamesGenerator.spec.ts b/test/functional-tests/generators/identifier-names-generators/dictionary-identifier-names-generator/DictionaryIdentifierNamesGenerator.spec.ts index ef577d133..8d2520d28 100644 --- a/test/functional-tests/generators/identifier-names-generators/dictionary-identifier-names-generator/DictionaryIdentifierNamesGenerator.spec.ts +++ b/test/functional-tests/generators/identifier-names-generators/dictionary-identifier-names-generator/DictionaryIdentifierNamesGenerator.spec.ts @@ -41,7 +41,8 @@ describe('DictionaryIdentifierNamesGenerator', () => { identifiersPrefix: 'a', transformObjectKeys: true, stringArray: true, - stringArrayThreshold: 1 + stringArrayThreshold: 1, + seed: 1 } ).getObfuscatedCode(); diff --git a/test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts b/test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts index 95f3bd0e8..36088f76a 100644 --- a/test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts +++ b/test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts @@ -1310,6 +1310,8 @@ describe('JavaScriptObfuscator', () => { renameProperties: true, stringArrayRotate: true, stringArray: true, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1, stringArrayEncoding: [ StringArrayEncoding.Base64, StringArrayEncoding.Rc4 diff --git a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer/BinaryExpressionControlFlowReplacer.spec.ts b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer/BinaryExpressionControlFlowReplacer.spec.ts index 2712512ec..1886150dd 100644 --- a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer/BinaryExpressionControlFlowReplacer.spec.ts +++ b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/binary-expression-control-flow-replacer/BinaryExpressionControlFlowReplacer.spec.ts @@ -10,8 +10,12 @@ describe('BinaryExpressionControlFlowReplacer', function () { this.timeout(100000); describe('replace', () => { + const variableMatch: string = '_0x([a-f0-9]){4,6}'; + describe('Variant #1 - single binary expression', () => { - const controlFlowStorageCallRegExp: RegExp = /var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(0x1, *0x2\);/; + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\]\\(0x1, *0x2\\);` + ); let obfuscatedCode: string; @@ -40,8 +44,12 @@ describe('BinaryExpressionControlFlowReplacer', function () { const samplesCount: number = 1000; const delta: number = 0.1; - const controlFlowStorageCallRegExp1: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(0x1, *0x2\);/; - const controlFlowStorageCallRegExp2: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(0x2, *0x3\);/; + const controlFlowStorageCallRegExp1: RegExp = new RegExp( + `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(0x1, *0x2\\);` + ); + const controlFlowStorageCallRegExp2: RegExp = new RegExp( + `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(0x2, *0x3\\);` + ); let matchErrorsCount: number = 0, usingExistingIdentifierChance: number; diff --git a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts index 9dc1a6bd2..10a260bdc 100644 --- a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts +++ b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/call-expression-control-flow-replacer/CallExpressionControlFlowReplacer.spec.ts @@ -10,8 +10,12 @@ describe('CallExpressionControlFlowReplacer', function () { this.timeout(100000); describe('replace', () => { + const variableMatch: string = '_0x([a-f0-9]){4,6}'; + describe('Variant #1 - single call expression', () => { - const controlFlowStorageCallRegExp: RegExp = /var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(_0x([a-f0-9]){4,6}, *0x1, *0x2\);/; + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\]\\(${variableMatch}, *0x1, *0x2\\);` + ); let obfuscatedCode: string; @@ -40,8 +44,12 @@ describe('CallExpressionControlFlowReplacer', function () { const samplesCount: number = 1000; const delta: number = 0.1; - const controlFlowStorageCallRegExp1: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(_0x([a-f0-9]){4,6}, *0x1, *0x2\);/; - const controlFlowStorageCallRegExp2: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(_0x([a-f0-9]){4,6}, *0x2, *0x3\);/; + const controlFlowStorageCallRegExp1: RegExp = new RegExp( + `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(${variableMatch}, *0x1, *0x2\\);` + ); + const controlFlowStorageCallRegExp2: RegExp = new RegExp( + `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(${variableMatch}, *0x2, *0x3\\);` + ); let matchErrorsCount: number = 0, usingExistingIdentifierChance: number; @@ -96,7 +104,9 @@ describe('CallExpressionControlFlowReplacer', function () { }); describe('Variant #3 - call expression callee is member expression node', () => { - const regExp: RegExp = /var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['sum'\]\(0x1, *0x2\);/; + const regExp: RegExp = new RegExp( + `var ${variableMatch} *= *${variableMatch}\\['sum'\\]\\(0x1, *0x2\\);` + ); let obfuscatedCode: string; @@ -119,10 +129,12 @@ describe('CallExpressionControlFlowReplacer', function () { }); describe('Variant #4 - rest as start call argument', () => { - const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);/; + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `${variableMatch}\\['\\w{5}']\\(${variableMatch}, *\\.\\.\\.${variableMatch}, *${variableMatch}\\);` + ); const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` + - `'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` + - `return *_0x([a-f0-9]){4,6}\\(\.\.\._0x([a-f0-9]){4,6}\\);` + + `'\\w{5}' *: *function *\\(${variableMatch}, *\.\.\.${variableMatch}\\) *\\{` + + `return *${variableMatch}\\(\.\.\.${variableMatch}\\);` + `\\}` + ``); @@ -151,10 +163,12 @@ describe('CallExpressionControlFlowReplacer', function () { }); describe('Variant #5 - rest as middle call argument', () => { - const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}\);/; + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `${variableMatch}\\['\\w{5}']\\(${variableMatch}, *${variableMatch}, *\\.\\.\\.${variableMatch}, *${variableMatch}\\);` + ); const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` + - `'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` + - `return *_0x([a-f0-9]){4,6}\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\);` + + `'\\w{5}' *: *function *\\(${variableMatch}, *${variableMatch}, *\.\.\.${variableMatch}\\) *\\{` + + `return *${variableMatch}\\(${variableMatch}, *\.\.\.${variableMatch}\\);` + `\\}` + ``); @@ -183,10 +197,12 @@ describe('CallExpressionControlFlowReplacer', function () { }); describe('Variant #6 - rest as last call argument', () => { - const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\);/; + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `${variableMatch}\\['\\w{5}']\\(${variableMatch}, *${variableMatch}, *\\.\\.\\.${variableMatch}\\);` + ); const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` + - `'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` + - `return *_0x([a-f0-9]){4,6}\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\);` + + `'\\w{5}' *: *function *\\(${variableMatch}, *${variableMatch}, *\.\.\.${variableMatch}\\) *\\{` + + `return *${variableMatch}\\(${variableMatch}, *\.\.\.${variableMatch}\\);` + `\\}` + ``); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/logical-expression-control-flow-replacer/LogicalExpressionControlFlowReplacer.spec.ts b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/logical-expression-control-flow-replacer/LogicalExpressionControlFlowReplacer.spec.ts index 0e06e265e..4ad5ccd5f 100644 --- a/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/logical-expression-control-flow-replacer/LogicalExpressionControlFlowReplacer.spec.ts +++ b/test/functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/logical-expression-control-flow-replacer/LogicalExpressionControlFlowReplacer.spec.ts @@ -10,8 +10,12 @@ describe('LogicalExpressionControlFlowReplacer', function () { this.timeout(100000); describe('replace', () => { + const variableMatch: string = '_0x([a-f0-9]){4,6}'; + describe('Variant #1 - single logical expression', () => { - const controlFlowStorageCallRegExp: RegExp = /var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(!!\[\], *!\[\]\);/; + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\]\\(!!\\[\\], *!\\[\\]\\);` + ); let obfuscatedCode: string; @@ -40,8 +44,12 @@ describe('LogicalExpressionControlFlowReplacer', function () { const samplesCount: number = 1000; const delta: number = 0.1; - const controlFlowStorageCallRegExp1: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(!!\[\], *!\[\]\);/; - const controlFlowStorageCallRegExp2: RegExp = /var _0x(?:[a-f0-9]){4,6} *= *(_0x([a-f0-9]){4,6}\['\w{5}'\])\(!\[\], *!!\[\]\);/; + const controlFlowStorageCallRegExp1: RegExp = new RegExp( + `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(!!\\[\\], *!\\[\\]\\);` + ); + const controlFlowStorageCallRegExp2: RegExp = new RegExp( + `var _0x(?:[a-f0-9]){4,6} *= *(${variableMatch}\\['\\w{5}'\\])\\(!\\[\\], *!!\\[\\]\\);` + ); let matchErrorsCount: number = 0, usingExistingIdentifierChance: number; @@ -96,7 +104,9 @@ describe('LogicalExpressionControlFlowReplacer', function () { }); describe('Variant #3 - single logical expression with unary expression', () => { - const controlFlowStorageCallRegExp: RegExp = /var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\]\(!_0x([a-f0-9]){4,6}, *!_0x([a-f0-9]){4,6}\);/; + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\]\\(!${variableMatch}, *!${variableMatch}\\);` + ); let obfuscatedCode: string; @@ -119,7 +129,9 @@ describe('LogicalExpressionControlFlowReplacer', function () { }); describe('prohibited nodes Variant #1', () => { - const regExp: RegExp = /var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\[_0x([a-f0-9]){4,6}\] *&& *!\[\];/; + const regExp: RegExp = new RegExp( + `var ${variableMatch} *= *${variableMatch}\\[${variableMatch}\\] *&& *!\\[\\];` + ); let obfuscatedCode: string; 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 8833d9976..1ab7f1adc 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 @@ -8,8 +8,14 @@ import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscator describe('StringLiteralControlFlowReplacer', () => { describe('replace', () => { - const controlFlowStorageStringLiteralRegExp: RegExp = /var _0x([a-f0-9]){4,6} *= *\{'\w{5}' *: *'test'\};/; - const controlFlowStorageCallRegExp: RegExp = /var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\['\w{5}'\];/; + 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}'\\];` + ); let obfuscatedCode: string; diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/StringArrayControlFlowTransformer.spec.ts b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/StringArrayControlFlowTransformer.spec.ts new file mode 100644 index 000000000..ab90c25e3 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/StringArrayControlFlowTransformer.spec.ts @@ -0,0 +1,427 @@ +import { assert } from 'chai'; + +import { + IdentifierNamesGenerator +} from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator'; +import { + StringArrayIndexesType +} from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayIndexesType'; + +import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes'; + +import { readFileAsString } from '../../../../helpers/readFileAsString'; + +import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade'; + +describe('StringArrayControlFlowTransformer', function () { + this.timeout(100000); + + const hexadecimalVariableMatch: string = '_0x([a-f0-9]){4,6}'; + + describe('transformNode', () => { + describe('Variant #1 - hexadecimal number generator', () => { + const stringArrayVariableMatch: string = '_0x([a-f0-9]){4}'; + + const controlFlowStorageMatch: string = `var ${hexadecimalVariableMatch} *= *\\{` + + `${hexadecimalVariableMatch} *: *0x0, *` + + `${hexadecimalVariableMatch} *: *0x1 *` + + `\\};`; + const controlFlowStorageCallMatch: string = `${stringArrayVariableMatch}\\(${hexadecimalVariableMatch}.${hexadecimalVariableMatch}\\)`; + + describe('Variant #1 - positive cases', () => { + describe('Variant #1 - hexadecimal number items', () => { + const controlFlowStorageRegExp: RegExp = new RegExp(controlFlowStorageMatch); + + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var ${hexadecimalVariableMatch} *= *${controlFlowStorageCallMatch} *\\+ *${controlFlowStorageCallMatch};` + ); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/input-1.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayIndexesType: [StringArrayIndexesType.HexadecimalNumber], + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should add `control flow storage` node to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageRegExp); + }); + + it('should add calls to `control flow storage` node to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageCallRegExp); + }); + }); + + describe('Variant #2 - hexadecimal numeric string items', () => { + const controlFlowStorageRegExp: RegExp = new RegExp( + `var ${hexadecimalVariableMatch} *= *\\{` + + `${hexadecimalVariableMatch} *: *'0x0', *` + + `${hexadecimalVariableMatch} *: *'0x1' *` + + `\\};` + ); + + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var ${hexadecimalVariableMatch} *= *${controlFlowStorageCallMatch} *\\+ *${controlFlowStorageCallMatch};` + ); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/input-1.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayIndexesType: [StringArrayIndexesType.HexadecimalNumericString], + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should add `control flow storage` node to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageRegExp); + }); + + it('should add calls to `control flow storage` node to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageCallRegExp); + }); + }); + + describe('Variant #3 - two scopes for `control flow storage` node', () => { + const expectedAppendToScopeThreshold: number = 0.5; + + const samplesCount: number = 1000; + const delta: number = 0.1; + + const regExp1: RegExp = new RegExp( + `\\(function\\(\\) *\\{ *${controlFlowStorageMatch}`, + ); + const regExp2: RegExp = new RegExp( + `function *${hexadecimalVariableMatch} *\\(${hexadecimalVariableMatch}\\) *\\{ *${controlFlowStorageMatch}`, + ); + + let appendToScopeThreshold1: number = 0; + let appendToScopeThreshold2: number = 0; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/input-2.js'); + + let obfuscatedCode: string, + totalValue1: number = 0, + totalValue2: number = 0; + + for (let i = 0; i < samplesCount; i++) { + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + + if (!regExp1.test(obfuscatedCode) && !regExp2.test(obfuscatedCode)) { + console.log(obfuscatedCode); + } + + if (regExp1.test(obfuscatedCode)) { + totalValue1++; + } + + if (regExp2.test(obfuscatedCode)) { + totalValue2++; + } + } + + appendToScopeThreshold1 = totalValue1 / samplesCount; + appendToScopeThreshold2 = totalValue2 / samplesCount; + }); + + it('should add `control flow storage` node to the obfuscated code in one of the scopes', () => { + assert.closeTo(appendToScopeThreshold1, expectedAppendToScopeThreshold, delta); + assert.closeTo(appendToScopeThreshold2, expectedAppendToScopeThreshold, delta); + }); + }); + + describe('Variant #4 - single `control flow storage` node with four items', () => { + const regexp: RegExp = new RegExp( + `var ${hexadecimalVariableMatch} *= *\\{` + + `${hexadecimalVariableMatch} *: *0x0, *` + + `${hexadecimalVariableMatch} *: *0x1, *` + + `${hexadecimalVariableMatch} *: *0x2, *` + + `${hexadecimalVariableMatch} *: *0x3 *` + + `\\};` + ); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/multiple-items.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should add `control flow storage` node with multiple items to the obfuscated code', () => { + assert.match(obfuscatedCode, regexp); + }); + }); + }); + + describe('Variant #2 - negative cases', function () { + describe('Variant #1 - string array call in the root block scope', () => { + const stringArrayCallsRegExp: RegExp = new RegExp( + `var test *= *${stringArrayVariableMatch}\\(0x0\\) *\\+ *${stringArrayVariableMatch}\\(0x1\\);` + ); + const controlFlowStorageRegExp: RegExp = new RegExp(controlFlowStorageMatch); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/root-block-scope-1.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + console.log(obfuscatedCode); + }); + + it('shouldn\'t add control flow storage node', () => { + assert.notMatch(obfuscatedCode, controlFlowStorageRegExp); + assert.match(obfuscatedCode, stringArrayCallsRegExp); + }); + }); + + describe('Variant #2 - threshold is `0`', () => { + const stringArrayCallsRegExp: RegExp = new RegExp( + `var ${hexadecimalVariableMatch} *= *${stringArrayVariableMatch}\\(0x0\\) *\\+ *${stringArrayVariableMatch}\\(0x1\\);` + ); + const controlFlowStorageRegExp: RegExp = new RegExp(controlFlowStorageMatch); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/input-1.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 0 + } + ).getObfuscatedCode(); + + console.log(obfuscatedCode); + }); + + it('shouldn\'t add control flow storage node', () => { + assert.notMatch(obfuscatedCode, controlFlowStorageRegExp); + assert.match(obfuscatedCode, stringArrayCallsRegExp); + }); + }); + }); + }); + + describe('Variant #2 - mangled number generator', () => { + describe('Variant #1 - single control flow storage', () => { + const controlFlowStorageRegExp: RegExp = new RegExp( + `var d *= *\\{` + + `c *: *0x0, *` + + `e *: *0x1 *` + + `\\};` + ); + const controlFlowStorageCallRegExp: RegExp = new RegExp( + `var c *= *b\\(d.c\\) *\\+ *b\\(d.e\\);` + ); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/input-1.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should add `control flow storage` node to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageRegExp); + }); + + it('should add calls to `control flow storage` node to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageCallRegExp); + }); + }); + + describe('Variant #2 - multiple control flow storages', () => { + const controlFlowStorageRegExp1: RegExp = new RegExp( + `var d *= *\\{` + + `c *: *0x0, *` + + `e *: *0x1 *` + + `\\};` + ); + const controlFlowStorageCallRegExp1: RegExp = new RegExp( + `var c *= *b\\(d.c\\) *\\+ *b\\(d.e\\);` + ); + + const controlFlowStorageRegExp2: RegExp = new RegExp( + `var e *= *\\{` + + `c *: *0x0, *` + + `f *: *0x1 *` + + `\\};` + ); + const controlFlowStorageCallRegExp2: RegExp = new RegExp( + `var c *= *b\\(e.c\\) *\\+ *b\\(e.f\\);` + ); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/multiple-storages.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('Match #1: should add `control flow storage` №1 and its calls to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageRegExp1); + assert.match(obfuscatedCode, controlFlowStorageCallRegExp1); + }); + + it('Match #2: should add `control flow storage` №2 and its calls to the obfuscated code', () => { + assert.match(obfuscatedCode, controlFlowStorageRegExp2); + assert.match(obfuscatedCode, controlFlowStorageCallRegExp2); + }); + }); + }); + + describe('Variant #3 - prevailing kind of variables', () => { + describe('Variant #1 - `var` kind', () => { + const regexp: RegExp = new RegExp(`var ${hexadecimalVariableMatch} *= *\\{`); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should use correct kind of variables for `control flow storage`', () => { + assert.match(obfuscatedCode, regexp); + }); + }); + + describe('Variant #2 - `const` kind', () => { + const regexp: RegExp = new RegExp(`const ${hexadecimalVariableMatch} *= *\\{`); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should use correct kind of variables for `control flow storage`', () => { + assert.match(obfuscatedCode, regexp); + }); + }); + + describe('Variant #3 - `let` kind', () => { + const regexp: RegExp = new RegExp(`const ${hexadecimalVariableMatch} *= *\\{`); + + let obfuscatedCode: string; + + before(() => { + const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js'); + + obfuscatedCode = JavaScriptObfuscator.obfuscate( + code, + { + ...NO_ADDITIONAL_NODES_PRESET, + stringArray: true, + stringArrayThreshold: 1, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1 + } + ).getObfuscatedCode(); + }); + + it('should use correct kind of variables for `control flow storage`', () => { + assert.match(obfuscatedCode, regexp); + }); + }); + }); + }); +}); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/input-1.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/input-1.js new file mode 100644 index 000000000..adce632d6 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/input-1.js @@ -0,0 +1,3 @@ +(function () { + var variable = 'foo' + 'bar'; +})(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/input-2.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/input-2.js new file mode 100644 index 000000000..71a3a03bc --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/input-2.js @@ -0,0 +1,7 @@ +(function () { + function foo (arg) { + function bar () { + var variable = 'foo' + 'bar'; + } + } +})(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/multiple-items.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/multiple-items.js new file mode 100644 index 000000000..439183550 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/multiple-items.js @@ -0,0 +1,4 @@ +(function () { + var test1 = 'foo' + 'bar'; + var test2 = 'baz' + 'bark'; +})(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/multiple-storages.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/multiple-storages.js new file mode 100644 index 000000000..713a388f2 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/multiple-storages.js @@ -0,0 +1,7 @@ +(function () { + var variable1 = 'foo' + 'bar'; +})(); + +(function () { + var variable2 = 'foo' + 'bar'; +})(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js new file mode 100644 index 000000000..1af340196 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-const.js @@ -0,0 +1,3 @@ +(function () { + const variable = 'foo' + 'bar'; +})(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-let.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-let.js new file mode 100644 index 000000000..380b978d6 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-let.js @@ -0,0 +1,3 @@ +(function () { + let variable = 'foo' + 'bar'; +})(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js new file mode 100644 index 000000000..adce632d6 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/prevailing-kind-of-variables-var.js @@ -0,0 +1,3 @@ +(function () { + var variable = 'foo' + 'bar'; +})(); diff --git a/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/root-block-scope-1.js b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/root-block-scope-1.js new file mode 100644 index 000000000..eb90b5ea6 --- /dev/null +++ b/test/functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/fixtures/root-block-scope-1.js @@ -0,0 +1 @@ +var test = 'foo' + 'bar'; diff --git a/test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/conditional-comment-obfuscating-guard/ConditionalCommentObfuscatingGuard.spec.ts b/test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/conditional-comment-obfuscating-guard/ConditionalCommentObfuscatingGuard.spec.ts index c1277a4e9..56b32117e 100644 --- a/test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/conditional-comment-obfuscating-guard/ConditionalCommentObfuscatingGuard.spec.ts +++ b/test/functional-tests/node-transformers/preparing-transformers/obfuscating-guards/conditional-comment-obfuscating-guard/ConditionalCommentObfuscatingGuard.spec.ts @@ -209,7 +209,7 @@ describe('ConditionalCommentObfuscatingGuard', () => { }); describe('Variant #6: `disable` and `enable` conditional comments with control flow flattening', () => { - const obfuscatedVariableDeclarationRegExp: RegExp = /var _0x([a-f0-9]){5,6} *= *_0x([a-f0-9]){5,6}\['[a-zA-Z0-9]{1,5}'];/; + const obfuscatedVariableDeclarationRegExp: RegExp = /var _0x([a-f0-9]){5,6} *= *_0x([a-f0-9]){5,6}\['\w{5}'];/; const ignoredVariableDeclarationRegExp: RegExp = /var bar *= *'bar';/; let obfuscatedCode: string; 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 1c26cd53c..c3176699c 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 @@ -160,6 +160,8 @@ describe('StringArrayRotateFunctionTransformer', function () { splitStrings: true, splitStringsChunkLength: 3, stringArray: true, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1, stringArrayEncoding: [ StringArrayEncoding.None, StringArrayEncoding.Base64, diff --git a/test/functional-tests/options/OptionsNormalizer.spec.ts b/test/functional-tests/options/OptionsNormalizer.spec.ts index 5ace148b1..d7f4ea2af 100644 --- a/test/functional-tests/options/OptionsNormalizer.spec.ts +++ b/test/functional-tests/options/OptionsNormalizer.spec.ts @@ -740,6 +740,8 @@ describe('OptionsNormalizer', () => { optionsPreset = getNormalizedOptions({ ...getDefaultOptions(), stringArray: false, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1, stringArrayEncoding: [StringArrayEncoding.Rc4], stringArrayIndexShift: true, stringArrayRotate: true, @@ -752,6 +754,8 @@ describe('OptionsNormalizer', () => { expectedOptionsPreset = { ...getDefaultOptions(), stringArray: false, + stringArrayCallsTransform: false, + stringArrayCallsTransformThreshold: 0, stringArrayEncoding: [StringArrayEncoding.None], stringArrayIndexShift: false, stringArrayRotate: false, @@ -767,6 +771,26 @@ describe('OptionsNormalizer', () => { }); }); + describe('stringArrayCallsTransformRule', () => { + before(() => { + optionsPreset = getNormalizedOptions({ + ...getDefaultOptions(), + stringArrayCallsTransform: false, + stringArrayCallsTransformThreshold: 1 + }); + + expectedOptionsPreset = { + ...getDefaultOptions(), + stringArrayCallsTransform: false, + stringArrayCallsTransformThreshold: 0 + }; + }); + + it('should normalize options preset', () => { + assert.deepEqual(optionsPreset, expectedOptionsPreset); + }); + }); + describe('stringArrayEncodingRule', () => { before(() => { optionsPreset = getNormalizedOptions({ @@ -807,4 +831,4 @@ describe('OptionsNormalizer', () => { }); }); }); -}); +}); \ No newline at end of file diff --git a/test/index.spec.ts b/test/index.spec.ts index 903c6b43d..b10694e20 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -49,6 +49,7 @@ import './unit-tests/utils/EscapeSequenceEncoder.spec'; import './unit-tests/utils/LevelledTopologicalSorter.spec'; import './unit-tests/utils/NumberUtils.spec'; import './unit-tests/utils/RandomGenerator.spec'; +import './unit-tests/utils/SetUtils.spec'; import './unit-tests/utils/StringUtils.spec'; import './unit-tests/utils/Utils.spec'; @@ -89,6 +90,7 @@ import './functional-tests/node-transformers/control-flow-transformers/control-f import './functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/logical-expression-control-flow-replacer/LogicalExpressionControlFlowReplacer.spec'; import './functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/StringLiteralControlFlowReplacer.spec'; import './functional-tests/node-transformers/control-flow-transformers/function-control-flow-transformer/FunctionControlFlowTransformer.spec'; +import './functional-tests/node-transformers/control-flow-transformers/string-array-control-flow-transformer/StringArrayControlFlowTransformer.spec'; import './functional-tests/node-transformers/converting-transformers/boolean-literal-transformer/BooleanLiteralTransformer.spec'; import './functional-tests/node-transformers/converting-transformers/class-field-transformer/ClassFieldTransformer.spec'; import './functional-tests/node-transformers/converting-transformers/export-specifier-transformer/ExportSpecifierTransformer.spec'; diff --git a/test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts b/test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts index 120aa484f..f2af0051c 100644 --- a/test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts +++ b/test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts @@ -38,6 +38,8 @@ describe('JavaScriptObfuscator runtime eval', function () { splitStrings: true, splitStringsChunkLength: 3, stringArray: true, + stringArrayCallsTransform: true, + stringArrayCallsTransformThreshold: 1, stringArrayEncoding: [ StringArrayEncoding.None, StringArrayEncoding.Base64, @@ -57,7 +59,7 @@ describe('JavaScriptObfuscator runtime eval', function () { unicodeEscapeSequence: true }; - this.timeout(200000); + this.timeout(600000); const options: Partial[] = [ { diff --git a/test/unit-tests/generators/identifier-names-generators/DictionarylIdentifierNamesGenerator.spec.ts b/test/unit-tests/generators/identifier-names-generators/DictionarylIdentifierNamesGenerator.spec.ts index 9995f1641..69a362119 100644 --- a/test/unit-tests/generators/identifier-names-generators/DictionarylIdentifierNamesGenerator.spec.ts +++ b/test/unit-tests/generators/identifier-names-generators/DictionarylIdentifierNamesGenerator.spec.ts @@ -224,4 +224,39 @@ describe('DictionaryIdentifierNamesGenerator', () => { }); }); }); + + describe('generateForLabel', () => { + const label1: string = 'label1'; + const label2: string = 'label2'; + + const dictionaryNames1: string[] = []; + const dictionaryNames2: string[] = []; + + let identifierNamesGenerator: IIdentifierNamesGenerator; + + before(() => { + const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(); + + inversifyContainerFacade.load('', '', { + identifiersPrefix: 'foo', + identifiersDictionary: ['a', 'b', 'c'] + }); + identifierNamesGenerator = inversifyContainerFacade.getNamed( + ServiceIdentifiers.IIdentifierNamesGenerator, + IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator + ); + + dictionaryNames1.push(identifierNamesGenerator.generateForLabel(label1)); + dictionaryNames1.push(identifierNamesGenerator.generateForLabel(label1)); + dictionaryNames1.push(identifierNamesGenerator.generateForLabel(label1)); + + dictionaryNames2.push(identifierNamesGenerator.generateForLabel(label2)); + dictionaryNames2.push(identifierNamesGenerator.generateForLabel(label2)); + dictionaryNames2.push(identifierNamesGenerator.generateForLabel(label2)); + }); + + it('should return different dictionary names for different labels', () => { + assert.notDeepEqual(dictionaryNames1, dictionaryNames2); + }); + }); }); diff --git a/test/unit-tests/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.spec.ts b/test/unit-tests/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.spec.ts index 665926d5b..3800b7716 100644 --- a/test/unit-tests/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.spec.ts +++ b/test/unit-tests/generators/identifier-names-generators/HexadecimalIdentifierNamesGenerator.spec.ts @@ -84,4 +84,46 @@ describe('HexadecimalIdentifierNamesGenerator', () => { assert.match(hexadecimalIdentifierName, regExp); }) }); + + describe('generateForLabel', () => { + const label1: string = 'label1'; + const label2: string = 'label2'; + const regExp: RegExp = /^_0x(\w){4,6}$/; + + let identifierNamesGenerator: IIdentifierNamesGenerator, + hexadecimalIdentifierName1: string, + hexadecimalIdentifierName2: string; + + before(() => { + const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(); + + inversifyContainerFacade.load('', '', { + identifiersPrefix: 'foo' + }); + identifierNamesGenerator = inversifyContainerFacade.getNamed( + ServiceIdentifiers.IIdentifierNamesGenerator, + IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator + ); + + identifierNamesGenerator.generateForLabel(label1) + identifierNamesGenerator.generateForLabel(label1) + hexadecimalIdentifierName1 = identifierNamesGenerator.generateForLabel(label1); + + identifierNamesGenerator.generateForLabel(label2) + identifierNamesGenerator.generateForLabel(label2) + hexadecimalIdentifierName2 = identifierNamesGenerator.generateForLabel(label2); + }); + + it('should return valid hexadecimal name 1', () => { + assert.match(hexadecimalIdentifierName1, regExp); + }) + + it('should return valid hexadecimal name 2', () => { + assert.match(hexadecimalIdentifierName2, regExp); + }) + + it('should generate different hexadecimal names for different labels', () => { + assert.notEqual(hexadecimalIdentifierName1, hexadecimalIdentifierName2); + }) + }); }); diff --git a/test/unit-tests/generators/identifier-names-generators/MangledShuffledlIdentifierNamesGenerator.spec.ts b/test/unit-tests/generators/identifier-names-generators/MangledShuffledlIdentifierNamesGenerator.spec.ts index c5ca688c1..da739b3e9 100644 --- a/test/unit-tests/generators/identifier-names-generators/MangledShuffledlIdentifierNamesGenerator.spec.ts +++ b/test/unit-tests/generators/identifier-names-generators/MangledShuffledlIdentifierNamesGenerator.spec.ts @@ -114,6 +114,40 @@ describe('MangledShuffledIdentifierNamesGenerator', () => { }); }); + describe('generateForLabel', () => { + const label1: string = 'label1'; + const label2: string = 'label2'; + + const mangledNames1: string[] = []; + const mangledNames2: string[] = []; + + let identifierNamesGenerator: IIdentifierNamesGenerator; + + before(() => { + const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(); + + inversifyContainerFacade.load('', '', { + identifiersPrefix: 'foo' + }); + identifierNamesGenerator = inversifyContainerFacade.getNamed( + ServiceIdentifiers.IIdentifierNamesGenerator, + IdentifierNamesGenerator.MangledShuffledIdentifierNamesGenerator + ); + + mangledNames1.push(identifierNamesGenerator.generateForLabel(label1)); + mangledNames1.push(identifierNamesGenerator.generateForLabel(label1)); + mangledNames1.push(identifierNamesGenerator.generateForLabel(label1)); + + mangledNames2.push(identifierNamesGenerator.generateForLabel(label2)); + mangledNames2.push(identifierNamesGenerator.generateForLabel(label2)); + mangledNames2.push(identifierNamesGenerator.generateForLabel(label2)); + }); + + it('should return the same mangled names set for different labels', () => { + assert.deepEqual(mangledNames1, mangledNames2); + }) + }); + describe('isIncrementedMangledName', function () { this.timeout(60000); diff --git a/test/unit-tests/generators/identifier-names-generators/MangledlIdentifierNamesGenerator.spec.ts b/test/unit-tests/generators/identifier-names-generators/MangledlIdentifierNamesGenerator.spec.ts index 808344028..ab536a446 100644 --- a/test/unit-tests/generators/identifier-names-generators/MangledlIdentifierNamesGenerator.spec.ts +++ b/test/unit-tests/generators/identifier-names-generators/MangledlIdentifierNamesGenerator.spec.ts @@ -171,6 +171,46 @@ describe('MangledIdentifierNamesGenerator', () => { }); }); + describe('generateForLabel', () => { + const label1: string = 'label1'; + const label2: string = 'label2'; + + const mangledNames1: string[] = []; + const mangledNames2: string[] = []; + + const expectedMangledNames1: string[] = ['a', 'b', 'c'] + const expectedMangledNames2: string[] = ['a', 'b'] + + let identifierNamesGenerator: IIdentifierNamesGenerator; + + before(() => { + const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(); + + inversifyContainerFacade.load('', '', { + identifiersPrefix: 'foo' + }); + identifierNamesGenerator = inversifyContainerFacade.getNamed( + ServiceIdentifiers.IIdentifierNamesGenerator, + IdentifierNamesGenerator.MangledIdentifierNamesGenerator + ); + + mangledNames1.push(identifierNamesGenerator.generateForLabel(label1)); + mangledNames1.push(identifierNamesGenerator.generateForLabel(label1)); + mangledNames1.push(identifierNamesGenerator.generateForLabel(label1)); + + mangledNames2.push(identifierNamesGenerator.generateForLabel(label2)); + mangledNames2.push(identifierNamesGenerator.generateForLabel(label2)); + }); + + it('should return valid mangled names for label 1', () => { + assert.deepEqual(mangledNames1, expectedMangledNames1); + }) + + it('should return valid mangled names for label 2', () => { + assert.deepEqual(mangledNames2, expectedMangledNames2); + }) + }); + describe('isIncrementedMangledName', function () { this.timeout(60000); diff --git a/test/unit-tests/utils/SetUtils.spec.ts b/test/unit-tests/utils/SetUtils.spec.ts new file mode 100644 index 000000000..5aafc635f --- /dev/null +++ b/test/unit-tests/utils/SetUtils.spec.ts @@ -0,0 +1,87 @@ +import 'reflect-metadata'; + +import { assert } from 'chai'; + +import { ServiceIdentifiers } from '../../../src/container/ServiceIdentifiers'; + +import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade'; +import { ISetUtils } from '../../../src/interfaces/utils/ISetUtils'; + +import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade'; + +describe('SetUtils', () => { + let setUtils: ISetUtils; + + before(() => { + const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade(); + + inversifyContainerFacade.load('', '', {}); + setUtils = inversifyContainerFacade.get(ServiceIdentifiers.ISetUtils); + }); + + describe('getLastElement', () => { + describe('empty set', () => { + const set: Set = new Set(); + const expectedLastElement: undefined = undefined; + + let lastElement: string | undefined; + + before(() => { + lastElement = setUtils.getLastElement(set); + }); + + it('should return undefined if set is empty', () => { + assert.equal(lastElement, expectedLastElement); + }); + }); + + describe('set length: `1`', () => { + const set: Set = new Set(['foo']); + const expectedLastElement: string = 'foo'; + + let lastElement: string | undefined; + + before(() => { + lastElement = setUtils.getLastElement(set); + }); + + it('should return first element for set with length: `1`', () => { + assert.equal(lastElement, expectedLastElement); + }); + }); + + describe('set length: `3`', () => { + const set: Set = new Set(['foo', 'bar', 'baz']); + const expectedLastElement: string = 'baz'; + + let lastElement: string | undefined; + + before(() => { + lastElement = setUtils.getLastElement(set); + }); + + it('should return last element for set with length: `3`', () => { + assert.equal(lastElement, expectedLastElement); + }); + }); + + describe('changed set with length: `2`', () => { + const set: Set = new Set(['foo', 'bar', 'baz']); + set.add('bark'); + set.delete('bark'); + set.delete('bar'); + + const expectedLastElement: string = 'baz'; + + let lastElement: string | undefined; + + before(() => { + lastElement = setUtils.getLastElement(set); + }); + + it('should return last element for changed set with length: `2`', () => { + assert.equal(lastElement, expectedLastElement); + }); + }); + }); +});