mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Added stringArrayCallsTransform and stringArrayCallsTransformThreshold options
This commit is contained in:
@@ -2,6 +2,7 @@ Change Log
|
||||
|
||||
v2.19.0
|
||||
---
|
||||
* **New options**: `stringArrayCallsTransform` and `stringArrayCallsTransformThreshold`
|
||||
* Fixed very rare cases when `rotateStringArray` couldn't rotate array properly
|
||||
* Improved `selfDefending` option
|
||||
* Installed `npm` package now has `types` directory and doesn't have `src` directory
|
||||
|
||||
@@ -381,10 +381,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,
|
||||
stringArrayWrappersCount: 1,
|
||||
stringArrayWrappersChainedCalls: true,
|
||||
@@ -443,8 +445,10 @@ Following options are available for the JS Obfuscator:
|
||||
--split-strings <boolean>
|
||||
--split-strings-chunk-length <number>
|
||||
--string-array <boolean>
|
||||
--string-array-indexes-type '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-string]
|
||||
--string-array-calls-transform <boolean>
|
||||
--string-array-calls-transform-threshold <number>
|
||||
--string-array-encoding '<list>' (comma separated) [none, base64, rc4]
|
||||
--string-array-indexes-type '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-string]
|
||||
--string-array-index-shift <boolean>
|
||||
--string-array-wrappers-count <number>
|
||||
--string-array-wrappers-chained-calls <boolean>
|
||||
@@ -1111,10 +1115,24 @@ 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 variables depending on [`stringArrayCallsTransformThreshold`](#stringarraycallstransformthreshold) value. For large source code, the internal threshold also could be applied.
|
||||
|
||||
### `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: `[]`
|
||||
|
||||
##### :warning: `stringArray` option must be enabled
|
||||
##### :warning: [`stringArray`](#stringarray) option must be enabled
|
||||
|
||||
This option can slow down your script.
|
||||
|
||||
@@ -1163,6 +1181,17 @@ Type: `boolean` Default: `true`
|
||||
|
||||
Enables additional index shift for all string array calls
|
||||
|
||||
### `stringArrayThreshold`
|
||||
Type: `number` Default: `0.8` Min: `0` Max: `1`
|
||||
|
||||
##### :warning: [`stringArray`](#stringarray) option must be enabled
|
||||
|
||||
You can use this setting to adjust the probability (from 0 to 1) that a string literal will be inserted into the `stringArray`.
|
||||
|
||||
This setting is especially useful for large code size because it repeatedly calls to the `string array` and can slow down your code.
|
||||
|
||||
`stringArrayThreshold: 0` equals to `stringArray: false`.
|
||||
|
||||
### `stringArrayWrappersCount`
|
||||
Type: `number` Default: `1`
|
||||
|
||||
@@ -1340,17 +1369,6 @@ function d (c, g) {
|
||||
}
|
||||
test();
|
||||
```
|
||||
|
||||
### `stringArrayThreshold`
|
||||
Type: `number` Default: `0.8` Min: `0` Max: `1`
|
||||
|
||||
##### :warning: [`stringArray`](#stringarray) option must be enabled
|
||||
|
||||
You can use this setting to adjust the probability (from 0 to 1) that a string literal will be inserted into the `stringArray`.
|
||||
|
||||
This setting is especially useful for large code size because it repeatedly calls to the `string array` and can slow down your code.
|
||||
|
||||
`stringArrayThreshold: 0` equals to `stringArray: false`.
|
||||
|
||||
### `target`
|
||||
Type: `string` Default: `browser`
|
||||
@@ -1417,9 +1435,9 @@ Allows to enable/disable string conversion to unicode escape sequence.
|
||||
Unicode escape sequence increases code size greatly and strings easily can be reverted to their original view. Recommended to enable this option only for small source code.
|
||||
|
||||
## Preset Options
|
||||
### High obfuscation, low performance
|
||||
### High obfuscation
|
||||
|
||||
Performance will 50-100% slower than without obfuscation
|
||||
The performance will be much slower than without obfuscation
|
||||
|
||||
```javascript
|
||||
{
|
||||
@@ -1442,6 +1460,8 @@ Performance will 50-100% slower than without obfuscation
|
||||
splitStrings: true,
|
||||
splitStringsChunkLength: 5,
|
||||
stringArray: true,
|
||||
stringArrayCallsTransform: true,
|
||||
stringArrayCallsTransformThreshold: 1,
|
||||
stringArrayEncoding: ['rc4'],
|
||||
stringArrayIndexShift: true,
|
||||
stringArrayWrappersCount: 5,
|
||||
@@ -1454,9 +1474,9 @@ Performance will 50-100% slower than without obfuscation
|
||||
}
|
||||
```
|
||||
|
||||
### Medium obfuscation, optimal performance
|
||||
### Medium obfuscation
|
||||
|
||||
Performance will 30-35% slower than without obfuscation
|
||||
The performance will be slower than without obfuscation
|
||||
|
||||
```javascript
|
||||
{
|
||||
@@ -1479,6 +1499,8 @@ Performance will 30-35% slower than without obfuscation
|
||||
splitStrings: true,
|
||||
splitStringsChunkLength: 10,
|
||||
stringArray: true,
|
||||
stringArrayCallsTransform: true,
|
||||
stringArrayCallsTransformThreshold: 0.75,
|
||||
stringArrayEncoding: ['base64'],
|
||||
stringArrayIndexShift: true,
|
||||
stringArrayWrappersCount: 2,
|
||||
@@ -1491,9 +1513,9 @@ Performance will 30-35% slower than without obfuscation
|
||||
}
|
||||
```
|
||||
|
||||
### Low obfuscation, High performance
|
||||
### Low obfuscation
|
||||
|
||||
Performance will slightly slower than without obfuscation
|
||||
The performance will be at a relatively normal level
|
||||
|
||||
```javascript
|
||||
{
|
||||
@@ -1513,6 +1535,8 @@ Performance will slightly slower than without obfuscation
|
||||
simplify: true,
|
||||
splitStrings: false,
|
||||
stringArray: true,
|
||||
stringArrayCallsTransform: true,
|
||||
stringArrayCallsTransformThreshold: 0.5,
|
||||
stringArrayEncoding: [],
|
||||
stringArrayIndexShift: true,
|
||||
stringArrayWrappersCount: 1,
|
||||
@@ -1524,7 +1548,7 @@ Performance will slightly slower than without obfuscation
|
||||
}
|
||||
```
|
||||
|
||||
### Default preset, High performance
|
||||
### Default preset
|
||||
|
||||
```javascript
|
||||
{
|
||||
@@ -1544,6 +1568,8 @@ Performance will slightly slower than without obfuscation
|
||||
simplify: true,
|
||||
splitStrings: false,
|
||||
stringArray: true,
|
||||
stringArrayCallsTransform: true,
|
||||
stringArrayCallsTransformThreshold: 0.5,
|
||||
stringArrayEncoding: [],
|
||||
stringArrayIndexShift: true,
|
||||
stringArrayWrappersCount: 1,
|
||||
|
||||
@@ -90,6 +90,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
|
||||
NodeTransformer.ScopeIdentifiersTransformer,
|
||||
NodeTransformer.ScopeThroughIdentifiersTransformer,
|
||||
NodeTransformer.SplitStringTransformer,
|
||||
NodeTransformer.StringArrayCallsTransformer,
|
||||
NodeTransformer.StringArrayRotateFunctionTransformer,
|
||||
NodeTransformer.StringArrayScopeCallsWrapperTransformer,
|
||||
NodeTransformer.StringArrayTransformer,
|
||||
|
||||
@@ -371,9 +371,19 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
|
||||
)
|
||||
.option(
|
||||
'--string-array <boolean>',
|
||||
'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 the call to the string array',
|
||||
BooleanSanitizer
|
||||
)
|
||||
.option(
|
||||
'--string-array-calls-transform <boolean>',
|
||||
'Enables the transformation of calls to the string array',
|
||||
BooleanSanitizer
|
||||
)
|
||||
.option(
|
||||
'--string-array-calls-transform-threshold <number>',
|
||||
'The probability that that calls to the string array will be transformed',
|
||||
parseFloat
|
||||
)
|
||||
.option(
|
||||
'--string-array-encoding <list> (comma separated, without whitespaces)',
|
||||
'Encodes each string in strings array using base64 or rc4 (this option can slow down your code speed). ' +
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCu
|
||||
import { DeadCodeInjectionCustomNode } from '../../../enums/custom-nodes/DeadCodeInjectionCustomNode';
|
||||
import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
|
||||
import { StringArrayCustomNode } from '../../../enums/custom-nodes/StringArrayCustomNode';
|
||||
import { StringArrayIndexNode } from '../../../enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode';
|
||||
|
||||
import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode';
|
||||
import { BlockStatementControlFlowFlatteningNode } from '../../../custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode';
|
||||
@@ -22,7 +23,7 @@ import { ObjectExpressionVariableDeclarationHostNode } from '../../../custom-nod
|
||||
import { StringArrayCallNode } from '../../../custom-nodes/string-array-nodes/StringArrayCallNode';
|
||||
import { StringArrayHexadecimalNumberIndexNode } from '../../../custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumberIndexNode';
|
||||
import { StringArrayHexadecimalNumericStringIndexNode } from '../../../custom-nodes/string-array-nodes/string-array-index-nodes/StringArrayHexadecimalNumericStringIndexNode';
|
||||
import { StringArrayIndexNode } from '../../../enums/custom-nodes/string-array-index-nodes/StringArrayIndexNode';
|
||||
import { StringArrayIndexHostNode } from '../../../custom-nodes/string-array-nodes/StringArrayIndexHostNode';
|
||||
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';
|
||||
@@ -81,6 +82,10 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
|
||||
.toConstructor(StringArrayCallNode)
|
||||
.whenTargetNamed(StringArrayCustomNode.StringArrayCallNode);
|
||||
|
||||
bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
|
||||
.toConstructor(StringArrayIndexHostNode)
|
||||
.whenTargetNamed(StringArrayCustomNode.StringArrayIndexHostNode);
|
||||
|
||||
bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
|
||||
.toConstructor(StringArrayScopeCallsWrapperFunctionNode)
|
||||
.whenTargetNamed(StringArrayCustomNode.StringArrayScopeCallsWrapperFunctionNode);
|
||||
|
||||
@@ -5,12 +5,17 @@ import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTra
|
||||
|
||||
import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
|
||||
|
||||
import { StringArrayCallsTransformer } from '../../../node-transformers/string-array-transformers/StringArrayCallsTransformer';
|
||||
import { StringArrayRotateFunctionTransformer } from '../../../node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer';
|
||||
import { StringArrayScopeCallsWrapperTransformer } from '../../../node-transformers/string-array-transformers/StringArrayScopeCallsWrapperTransformer';
|
||||
import { StringArrayTransformer } from '../../../node-transformers/string-array-transformers/StringArrayTransformer';
|
||||
|
||||
export const stringArrayTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
// strings transformers
|
||||
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
|
||||
.to(StringArrayCallsTransformer)
|
||||
.whenTargetNamed(NodeTransformer.StringArrayCallsTransformer);
|
||||
|
||||
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
|
||||
.to(StringArrayRotateFunctionTransformer)
|
||||
.whenTargetNamed(NodeTransformer.StringArrayRotateFunctionTransformer);
|
||||
|
||||
@@ -105,7 +105,7 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
|
||||
const stringArrayCallIndexNode: ESTree.Expression = this.stringArrayIndexNodeFactory(stringArrayIndexNodeName)
|
||||
.getNode(normalizedIndex);
|
||||
|
||||
NodeMetadata.set(stringArrayCallIndexNode, { replacedLiteral: true });
|
||||
NodeMetadata.set(stringArrayCallIndexNode, { stringArrayCallLiteralNode: true });
|
||||
|
||||
const hexadecimalNode: ESTree.Expression = isPositive
|
||||
? stringArrayCallIndexNode
|
||||
@@ -126,7 +126,7 @@ export abstract class AbstractStringArrayCallNode extends AbstractCustomNode {
|
||||
protected getRc4KeyLiteralNode (decodeKey: string): ESTree.Literal {
|
||||
const rc4KeyLiteralNode: ESTree.Literal = NodeFactory.literalNode(decodeKey);
|
||||
|
||||
NodeMetadata.set(rc4KeyLiteralNode, { replacedLiteral: true });
|
||||
NodeMetadata.set(rc4KeyLiteralNode, { stringArrayCallLiteralNode: true });
|
||||
|
||||
return rc4KeyLiteralNode;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
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';
|
||||
import { TStringArrayIndexNodeFactory } from '../../types/container/custom-nodes/string-array-index-nodes/TStringArrayIndexNodeFactory';
|
||||
|
||||
import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
|
||||
import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
||||
import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
|
||||
|
||||
import { initializable } from '../../decorators/Initializable';
|
||||
|
||||
import { AbstractStringArrayCallNode } from './AbstractStringArrayCallNode';
|
||||
import { NodeFactory } from '../../node/NodeFactory';
|
||||
|
||||
@injectable()
|
||||
export class StringArrayIndexHostNode extends AbstractStringArrayCallNode {
|
||||
/**
|
||||
* @type {Literal}
|
||||
*/
|
||||
@initializable()
|
||||
private stringArrayCallIndexNode!: ESTree.Literal;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
@initializable()
|
||||
private variableName!: string;
|
||||
|
||||
/**
|
||||
* @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
|
||||
* @param {TStringArrayIndexNodeFactory} stringArrayIndexNodeFactory
|
||||
* @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
|
||||
* @param {IStringArrayStorage} stringArrayStorage
|
||||
* @param {IArrayUtils} arrayUtils
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
*/
|
||||
public constructor (
|
||||
@inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
|
||||
identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
|
||||
@inject(ServiceIdentifiers.Factory__IStringArrayIndexNode)
|
||||
stringArrayIndexNodeFactory: TStringArrayIndexNodeFactory,
|
||||
@inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
|
||||
@inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
|
||||
@inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils,
|
||||
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions
|
||||
) {
|
||||
super(
|
||||
identifierNamesGeneratorFactory,
|
||||
stringArrayIndexNodeFactory,
|
||||
customCodeHelperFormatter,
|
||||
stringArrayStorage,
|
||||
arrayUtils,
|
||||
randomGenerator,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Literal} stringArrayCallIndexNode
|
||||
* @param {string} variableName
|
||||
*/
|
||||
public initialize (
|
||||
stringArrayCallIndexNode: ESTree.Literal,
|
||||
variableName: string
|
||||
): void {
|
||||
this.stringArrayCallIndexNode = stringArrayCallIndexNode;
|
||||
this.variableName = variableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {TStatement[]}
|
||||
*/
|
||||
protected getNodeStructure (): TStatement[] {
|
||||
const indexHostVariableNode: ESTree.VariableDeclaration = NodeFactory.variableDeclarationNode(
|
||||
[
|
||||
NodeFactory.variableDeclaratorNode(
|
||||
NodeFactory.identifierNode(this.variableName),
|
||||
this.stringArrayCallIndexNode
|
||||
)
|
||||
],
|
||||
'const'
|
||||
);
|
||||
|
||||
return [indexHostVariableNode];
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -14,7 +14,7 @@ declare module 'estree' {
|
||||
}
|
||||
|
||||
export interface LiteralNodeMetadata extends BaseNodeMetadata {
|
||||
replacedLiteral?: boolean;
|
||||
stringArrayCallLiteralNode?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export enum StringArrayCustomNode {
|
||||
StringArrayCallNode = 'StringArrayCallNode',
|
||||
StringArrayIndexHostNode = 'StringArrayIndexHostNode',
|
||||
StringArrayScopeCallsWrapperFunctionNode = 'StringArrayScopeCallsWrapperFunctionNode',
|
||||
StringArrayScopeCallsWrapperVariableNode = 'StringArrayScopeCallsWrapperVariableNode'
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ export enum NodeTransformer {
|
||||
ScopeIdentifiersTransformer = 'ScopeIdentifiersTransformer',
|
||||
ScopeThroughIdentifiersTransformer = 'ScopeThroughIdentifiersTransformer',
|
||||
SplitStringTransformer = 'SplitStringTransformer',
|
||||
StringArrayCallsTransformer = 'StringArrayCallsTransformer',
|
||||
StringArrayTransformer = 'StringArrayTransformer',
|
||||
StringArrayRotateFunctionTransformer = 'StringArrayRotateFunctionTransformer',
|
||||
StringArrayScopeCallsWrapperTransformer = 'StringArrayScopeCallsWrapperTransformer',
|
||||
|
||||
@@ -51,6 +51,8 @@ export interface IOptions {
|
||||
readonly splitStringsChunkLength: number;
|
||||
readonly stringArray: boolean;
|
||||
readonly stringArrayEncoding: TStringArrayEncoding[];
|
||||
readonly stringArrayCallsTransform: boolean;
|
||||
readonly stringArrayCallsTransformThreshold: number;
|
||||
readonly stringArrayIndexesType: TStringArrayIndexesType[];
|
||||
readonly stringArrayIndexShift: boolean;
|
||||
readonly stringArrayWrappersChainedCalls: boolean;
|
||||
|
||||
@@ -24,6 +24,13 @@ export interface IArrayUtils {
|
||||
*/
|
||||
getLastElement <T> (array: T[]): T | undefined;
|
||||
|
||||
/**
|
||||
* @param {T[]} array
|
||||
* @param {number} index
|
||||
* @returns {T | undefined}
|
||||
*/
|
||||
getLastElementByIndex <T> (array: T[], index: number): T | undefined;
|
||||
|
||||
/**
|
||||
* @param array
|
||||
* @param times
|
||||
|
||||
@@ -23,6 +23,8 @@ import { NodeUtils } from '../../node/NodeUtils';
|
||||
*/
|
||||
@injectable()
|
||||
export class SplitStringTransformer extends AbstractNodeTransformer {
|
||||
private static readonly maxStringLengthForSecondPass: number = 30000;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
@@ -80,14 +82,14 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
|
||||
* @returns {IVisitor | null}
|
||||
*/
|
||||
public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null {
|
||||
if (!this.options.splitStrings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (nodeTransformationStage) {
|
||||
case NodeTransformationStage.Converting:
|
||||
return {
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
|
||||
if (!this.options.splitStrings) {
|
||||
return;
|
||||
}
|
||||
|
||||
leave: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
|
||||
if (parentNode && NodeGuards.isLiteralNode(node)) {
|
||||
return this.transformNode(node, parentNode);
|
||||
}
|
||||
@@ -113,48 +115,55 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
|
||||
}
|
||||
|
||||
// pass #1: split string on a large chunks with length of `firstPassChunkLength`
|
||||
const firstPassChunksNode: ESTree.Node = this.transformLiteralNodeByChunkLength(
|
||||
const [firstPassChunksNode, stringChunks]: [ESTree.Node, string[]] = this.transformLiteralNodeByChunkLength(
|
||||
literalNode,
|
||||
parentNode,
|
||||
SplitStringTransformer.firstPassChunkLength
|
||||
);
|
||||
|
||||
// pass #2: split large chunks on a chunks with length of `splitStringsChunkLength`
|
||||
const stringChunksCount: number = stringChunks.length;
|
||||
const isLargeString: boolean = SplitStringTransformer.maxStringLengthForSecondPass
|
||||
/ SplitStringTransformer.firstPassChunkLength
|
||||
<= stringChunksCount;
|
||||
const minSecondPathChunkLength = isLargeString
|
||||
? Math.floor(stringChunksCount / this.options.splitStringsChunkLength)
|
||||
: this.options.splitStringsChunkLength;
|
||||
|
||||
// pass #2: split large chunks on a chunks with length of min length === `splitStringsChunkLength`
|
||||
const secondPassChunksNode: ESTree.Node = estraverse.replace(firstPassChunksNode, {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
|
||||
if (parentNode && NodeGuards.isLiteralNode(node)) {
|
||||
enter: (node: ESTree.Node) => {
|
||||
if (NodeGuards.isLiteralNode(node)) {
|
||||
return this.transformLiteralNodeByChunkLength(
|
||||
node,
|
||||
parentNode,
|
||||
this.options.splitStringsChunkLength
|
||||
);
|
||||
minSecondPathChunkLength
|
||||
)[0];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
NodeUtils.parentizeNode(secondPassChunksNode, parentNode);
|
||||
NodeUtils.parentizeAst(secondPassChunksNode);
|
||||
|
||||
return secondPassChunksNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Literal} literalNode
|
||||
* @param {Node} parentNode
|
||||
* @param {number} chunkLength
|
||||
* @returns {Node}
|
||||
* @returns {[resultNode: Node, stringChunks: string[]]}
|
||||
*/
|
||||
private transformLiteralNodeByChunkLength (
|
||||
literalNode: ESTree.Literal,
|
||||
parentNode: ESTree.Node,
|
||||
chunkLength: number
|
||||
): ESTree.Node {
|
||||
): [resultNode: ESTree.Node, stringChunks: string[]] {
|
||||
if (!NodeLiteralUtils.isStringLiteralNode(literalNode)) {
|
||||
return literalNode;
|
||||
return [literalNode, []];
|
||||
}
|
||||
|
||||
const valueLength: number = stringz.length(literalNode.value);
|
||||
|
||||
if (chunkLength >= valueLength) {
|
||||
return literalNode;
|
||||
return [literalNode, []];
|
||||
}
|
||||
|
||||
const stringChunks: string[] = SplitStringTransformer.chunkString(
|
||||
@@ -163,13 +172,10 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
|
||||
chunkLength
|
||||
);
|
||||
|
||||
const binaryExpressionNode: ESTree.BinaryExpression =
|
||||
this.transformStringChunksToBinaryExpressionNode(stringChunks);
|
||||
|
||||
NodeUtils.parentizeAst(binaryExpressionNode);
|
||||
NodeUtils.parentizeNode(binaryExpressionNode, parentNode);
|
||||
|
||||
return binaryExpressionNode;
|
||||
return [
|
||||
this.transformStringChunksToBinaryExpressionNode(stringChunks),
|
||||
stringChunks
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,7 +65,7 @@ export class MetadataTransformer extends AbstractNodeTransformer {
|
||||
NodeMetadata.set(node, { ignoredNode: false });
|
||||
|
||||
if (NodeGuards.isLiteralNode(node)) {
|
||||
NodeMetadata.set(node, { replacedLiteral: false });
|
||||
NodeMetadata.set(node, { stringArrayCallLiteralNode: false });
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
+12
-11
@@ -14,6 +14,7 @@ import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
||||
import { NodeGuards } from '../../node/NodeGuards';
|
||||
import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
|
||||
import { RenamePropertiesMode } from '../../enums/node-transformers/rename-properties-transformers/RenamePropertiesMode';
|
||||
import { NodeUtils } from '../../node/NodeUtils';
|
||||
|
||||
@injectable()
|
||||
export class RenamePropertiesTransformer extends AbstractNodeTransformer {
|
||||
@@ -106,20 +107,20 @@ export class RenamePropertiesTransformer extends AbstractNodeTransformer {
|
||||
* @returns {Node}
|
||||
*/
|
||||
public transformNode (node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node {
|
||||
let propertyNode: ESTree.Node | null = null;
|
||||
|
||||
if (NodeGuards.isPropertyNode(node)) {
|
||||
return this.transformPropertyNode(node);
|
||||
propertyNode = this.transformPropertyNode(node);
|
||||
} else if (NodeGuards.isPropertyDefinitionNode(node)) {
|
||||
propertyNode = this.transformPropertyDefinitionNode(node);
|
||||
} else if (NodeGuards.isMemberExpressionNode(node)) {
|
||||
propertyNode = this.transformMemberExpressionNode(node);
|
||||
} else if (NodeGuards.isMethodDefinitionNode(node)) {
|
||||
propertyNode = this.transformMethodDefinitionNode(node);
|
||||
}
|
||||
|
||||
if (NodeGuards.isPropertyDefinitionNode(node)) {
|
||||
return this.transformPropertyDefinitionNode(node);
|
||||
}
|
||||
|
||||
if (NodeGuards.isMemberExpressionNode(node)) {
|
||||
return this.transformMemberExpressionNode(node);
|
||||
}
|
||||
|
||||
if (NodeGuards.isMethodDefinitionNode(node)) {
|
||||
return this.transformMethodDefinitionNode(node);
|
||||
if (propertyNode) {
|
||||
NodeUtils.parentizeNode(propertyNode, parentNode);
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import { inject, injectable, } from 'inversify';
|
||||
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
|
||||
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
|
||||
import { TInitialData } from '../../types/TInitialData';
|
||||
import { TNodeWithLexicalScopeStatements } from '../../types/node/TNodeWithLexicalScopeStatements';
|
||||
import { TStatement } from '../../types/node/TStatement';
|
||||
import { TStringArrayCustomNodeFactory } from '../../types/container/custom-nodes/TStringArrayCustomNodeFactory';
|
||||
|
||||
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 { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
||||
|
||||
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
|
||||
import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
|
||||
import { StringArrayCustomNode } from '../../enums/custom-nodes/StringArrayCustomNode';
|
||||
|
||||
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
||||
import { NodeAppender } from '../../node/NodeAppender';
|
||||
import { NodeFactory } from '../../node/NodeFactory';
|
||||
import { NodeGuards } from '../../node/NodeGuards';
|
||||
import { NodeMetadata } from '../../node/NodeMetadata';
|
||||
import { NodeUtils } from '../../node/NodeUtils';
|
||||
import { StringArrayIndexHostNode } from '../../custom-nodes/string-array-nodes/StringArrayIndexHostNode';
|
||||
|
||||
@injectable()
|
||||
export class StringArrayCallsTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
private static readonly lexicalScopeBodyLengthThreshold: number = 20000;
|
||||
|
||||
/**
|
||||
* @type {NodeTransformer[]}
|
||||
*/
|
||||
public override readonly runAfter: NodeTransformer[] = [
|
||||
NodeTransformer.StringArrayTransformer
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {IIdentifierNamesGenerator}
|
||||
*/
|
||||
private readonly identifierNamesGenerator: IIdentifierNamesGenerator;
|
||||
|
||||
/**
|
||||
* @type {TStringArrayCustomNodeFactory}
|
||||
*/
|
||||
private readonly stringArrayTransformerCustomNodeFactory: TStringArrayCustomNodeFactory;
|
||||
|
||||
/**
|
||||
* @type {TNodeWithLexicalScopeStatements[]}
|
||||
*/
|
||||
private readonly visitedLexicalScopeNodesStack: TNodeWithLexicalScopeStatements[] = [];
|
||||
|
||||
/**
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
* @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
|
||||
* @param {TStringArrayCustomNodeFactory} stringArrayTransformerCustomNodeFactory
|
||||
*/
|
||||
public constructor (
|
||||
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions,
|
||||
@inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
|
||||
identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
|
||||
@inject(ServiceIdentifiers.Factory__IStringArrayCustomNode)
|
||||
stringArrayTransformerCustomNodeFactory: TStringArrayCustomNodeFactory
|
||||
) {
|
||||
super(randomGenerator, options);
|
||||
|
||||
this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
|
||||
this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
|
||||
this.stringArrayTransformerCustomNodeFactory = stringArrayTransformerCustomNodeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NodeTransformationStage} nodeTransformationStage
|
||||
* @returns {IVisitor | null}
|
||||
*/
|
||||
public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null {
|
||||
if (!this.options.stringArrayCallsTransform) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (nodeTransformationStage) {
|
||||
case NodeTransformationStage.StringArray:
|
||||
return {
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
|
||||
if (parentNode && NodeGuards.isNodeWithLexicalScopeStatements(node, parentNode)) {
|
||||
this.onLexicalScopeNodeEnter(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
},
|
||||
leave: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
|
||||
if (parentNode && NodeGuards.isNodeWithLexicalScopeStatements(node, parentNode)) {
|
||||
this.onLexicalScopeNodeLeave();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
if (
|
||||
parentNode
|
||||
&& NodeGuards.isLiteralNode(node)
|
||||
&& NodeMetadata.isStringArrayCallLiteralNode(node)
|
||||
) {
|
||||
return this.transformNode(node, parentNode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Literal} literalNode
|
||||
* @param {Node} parentNode
|
||||
* @returns {Literal | Identifier}
|
||||
*/
|
||||
public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Literal | ESTree.Identifier {
|
||||
const randomLexicalScopeNode: TNodeWithLexicalScopeStatements = this.randomGenerator
|
||||
.getRandomGenerator()
|
||||
.pickone(this.visitedLexicalScopeNodesStack);
|
||||
|
||||
if (!randomLexicalScopeNode) {
|
||||
return literalNode;
|
||||
}
|
||||
|
||||
const randomNumber: number = this.randomGenerator.getMathRandom();
|
||||
const isOptionThresholdPassed: boolean = randomNumber <= this.options.stringArrayCallsTransformThreshold;
|
||||
const isLargeLexicalScopeBodyLengthThresholdPassed: boolean = randomNumber > (
|
||||
randomLexicalScopeNode.body.length / StringArrayCallsTransformer.lexicalScopeBodyLengthThreshold
|
||||
);
|
||||
|
||||
if (!isOptionThresholdPassed || !isLargeLexicalScopeBodyLengthThresholdPassed) {
|
||||
return literalNode;
|
||||
}
|
||||
|
||||
const hostVariableName: string = this.identifierNamesGenerator.generateNext();
|
||||
const hostVariableNode: ESTree.VariableDeclaration = this.getStringArrayIndexHostNode(
|
||||
literalNode,
|
||||
hostVariableName
|
||||
);
|
||||
NodeUtils.parentizeAst(hostVariableNode);
|
||||
|
||||
NodeAppender.prepend(randomLexicalScopeNode, [hostVariableNode]);
|
||||
|
||||
const identifierNode: ESTree.Identifier = NodeFactory.identifierNode(hostVariableName);
|
||||
NodeUtils.parentizeNode(identifierNode, parentNode);
|
||||
|
||||
return identifierNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Literal} stringArrayCallIndexNode
|
||||
* @param {string} variableName
|
||||
* @returns {VariableDeclaration}
|
||||
*/
|
||||
private getStringArrayIndexHostNode (
|
||||
stringArrayCallIndexNode: ESTree.Literal,
|
||||
variableName: string
|
||||
): ESTree.VariableDeclaration {
|
||||
const stringArrayCallCustomNode: ICustomNode<TInitialData<StringArrayIndexHostNode>> =
|
||||
this.stringArrayTransformerCustomNodeFactory(StringArrayCustomNode.StringArrayIndexHostNode);
|
||||
|
||||
stringArrayCallCustomNode.initialize(
|
||||
stringArrayCallIndexNode,
|
||||
variableName
|
||||
);
|
||||
|
||||
const statementNode: TStatement = stringArrayCallCustomNode.getNode()[0];
|
||||
|
||||
if (!NodeGuards.isVariableDeclarationNode(statementNode)) {
|
||||
throw new Error('`stringArrayIndexHostNode.getNode()[0]` should returns array with `isVariableDeclarationNode` node');
|
||||
}
|
||||
|
||||
return statementNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TNodeWithLexicalScopeStatements} lexicalScopeBodyNode
|
||||
*/
|
||||
private onLexicalScopeNodeEnter (lexicalScopeBodyNode: TNodeWithLexicalScopeStatements): void {
|
||||
this.visitedLexicalScopeNodesStack.push(lexicalScopeBodyNode);
|
||||
}
|
||||
|
||||
private onLexicalScopeNodeLeave (): void {
|
||||
this.visitedLexicalScopeNodesStack.pop();
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,11 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
|
||||
this.prepareNode(node);
|
||||
}
|
||||
|
||||
if (parentNode && NodeGuards.isLiteralNode(node) && !NodeMetadata.isReplacedLiteral(node)) {
|
||||
if (
|
||||
parentNode
|
||||
&& NodeGuards.isLiteralNode(node)
|
||||
&& !NodeMetadata.isStringArrayCallLiteralNode(node)
|
||||
) {
|
||||
return this.transformNode(node, parentNode);
|
||||
}
|
||||
}
|
||||
@@ -187,16 +191,19 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
|
||||
const cacheKey: string = this.literalNodesCacheStorage.buildKey(literalValue, stringArrayStorageItemData);
|
||||
const useCachedValue: boolean = this.literalNodesCacheStorage.shouldUseCachedValue(cacheKey, stringArrayStorageItemData);
|
||||
|
||||
let resultNode: ESTree.Node;
|
||||
|
||||
if (useCachedValue) {
|
||||
return <ESTree.Node>this.literalNodesCacheStorage.get(cacheKey);
|
||||
const nodeFromCache: ESTree.Node = <ESTree.Node>this.literalNodesCacheStorage.get(cacheKey);
|
||||
|
||||
resultNode = NodeUtils.clone(nodeFromCache);
|
||||
} else {
|
||||
resultNode = stringArrayStorageItemData
|
||||
? this.getStringArrayCallNode(stringArrayStorageItemData)
|
||||
: literalNode;
|
||||
this.literalNodesCacheStorage.set(cacheKey, resultNode);
|
||||
}
|
||||
|
||||
const resultNode: ESTree.Node = stringArrayStorageItemData
|
||||
? this.getStringArrayCallNode(stringArrayStorageItemData)
|
||||
: literalNode;
|
||||
|
||||
this.literalNodesCacheStorage.set(cacheKey, resultNode);
|
||||
|
||||
NodeUtils.parentizeNode(resultNode, parentNode);
|
||||
|
||||
return resultNode;
|
||||
|
||||
@@ -63,7 +63,7 @@ export class NodeFactory {
|
||||
public static binaryExpressionNode (
|
||||
operator: ESTree.BinaryOperator,
|
||||
left: ESTree.Expression,
|
||||
right: ESTree.Expression,
|
||||
right: ESTree.Expression
|
||||
): ESTree.BinaryExpression {
|
||||
return {
|
||||
type: NodeType.BinaryExpression,
|
||||
|
||||
@@ -43,7 +43,10 @@ export class NodeMetadata {
|
||||
* @param {Node} literalNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public static isReplacedLiteral (literalNode: ESTree.Literal): boolean {
|
||||
return NodeMetadata.get<ESTree.LiteralNodeMetadata, 'replacedLiteral'>(literalNode, 'replacedLiteral') === true;
|
||||
public static isStringArrayCallLiteralNode (literalNode: ESTree.Literal): boolean {
|
||||
return NodeMetadata.get<
|
||||
ESTree.LiteralNodeMetadata,
|
||||
'stringArrayCallLiteralNode'
|
||||
>(literalNode, 'stringArrayCallLiteralNode') === true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +340,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[]}
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,7 @@ import { SplitStringsChunkLengthRule } from './normalizer-rules/SplitStringsChun
|
||||
import { StringArrayRule } from './normalizer-rules/StringArrayRule';
|
||||
import { StringArrayEncodingRule } from './normalizer-rules/StringArrayEncodingRule';
|
||||
import { StringArrayWrappersChainedCallsRule } from './normalizer-rules/StringArrayWappersChainedCalls';
|
||||
import { StringArrayCallsTransformRule } from './normalizer-rules/StringArrayCallsTransform';
|
||||
|
||||
@injectable()
|
||||
export class OptionsNormalizer implements IOptionsNormalizer {
|
||||
@@ -39,6 +40,7 @@ export class OptionsNormalizer implements IOptionsNormalizer {
|
||||
SourceMapBaseUrlRule,
|
||||
SourceMapFileNameRule,
|
||||
SplitStringsChunkLengthRule,
|
||||
StringArrayCallsTransformRule,
|
||||
StringArrayRule,
|
||||
StringArrayEncodingRule,
|
||||
StringArrayWrappersChainedCallsRule,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -15,6 +15,8 @@ export const StringArrayRule: TOptionsNormalizerRule = (options: IOptions): IOpt
|
||||
rotateStringArray: false,
|
||||
shuffleStringArray: false,
|
||||
stringArray: false,
|
||||
stringArrayCallsTransform: false,
|
||||
stringArrayCallsTransformThreshold: 0,
|
||||
stringArrayEncoding: [
|
||||
StringArrayEncoding.None
|
||||
],
|
||||
|
||||
@@ -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
|
||||
],
|
||||
|
||||
@@ -13,6 +13,7 @@ export const HIGH_OBFUSCATION_PRESET: TInputOptions = Object.freeze({
|
||||
debugProtectionInterval: true,
|
||||
optionsPreset: OptionsPreset.HighObfuscation,
|
||||
splitStringsChunkLength: 5,
|
||||
stringArrayCallsTransformThreshold: 1,
|
||||
stringArrayEncoding: [
|
||||
StringArrayEncoding.Rc4
|
||||
],
|
||||
|
||||
@@ -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
|
||||
],
|
||||
|
||||
@@ -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
|
||||
],
|
||||
|
||||
@@ -43,9 +43,7 @@ export class VisitedLexicalScopeNodesStackStorage extends ArrayStorage <TNodeWit
|
||||
* @returns {TNodeWithLexicalScopeStatements | undefined}
|
||||
*/
|
||||
public getPenultimateElement (): TNodeWithLexicalScopeStatements | undefined {
|
||||
const storageLength: number = this.getLength();
|
||||
|
||||
return this.get(storageLength - 2) ?? undefined;
|
||||
return this.arrayUtils.getLastElementByIndex(this.getStorage(), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
-1
@@ -85,9 +85,18 @@ export class ArrayUtils implements IArrayUtils {
|
||||
* @returns {T | undefined}
|
||||
*/
|
||||
public getLastElement <T> (array: T[]): T | undefined {
|
||||
return this.getLastElementByIndex(array, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T[]} array
|
||||
* @param {number} index
|
||||
* @returns {T | undefined}
|
||||
*/
|
||||
public getLastElementByIndex <T> (array: T[], index: number): T | undefined {
|
||||
const arrayLength: number = array.length;
|
||||
|
||||
return array[arrayLength - 1] ?? undefined;
|
||||
return array[arrayLength - 1 - index] ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+38
-11
@@ -1,25 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
import { readFileAsString } from '../helpers/readFileAsString';
|
||||
import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
|
||||
import { StringArrayEncoding } from '../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
|
||||
|
||||
(function () {
|
||||
const JavaScriptObfuscator: any = require('../../index');
|
||||
const code: string = readFileAsString(__dirname + '/../functional-tests/javascript-obfuscator/fixtures/custom-nodes-identifier-names-collision.js');
|
||||
|
||||
let obfuscationResult = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
|
||||
`
|
||||
class Test {
|
||||
constructor () {
|
||||
let test = {}
|
||||
}
|
||||
|
||||
static methodA = () => {
|
||||
console.log('method_A');
|
||||
}
|
||||
|
||||
methodB () {
|
||||
console.log('method_B');
|
||||
console.log('method_C');
|
||||
console.log('method_D');
|
||||
console.log('method_E');
|
||||
|
||||
Test.methodA();
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new Test();
|
||||
|
||||
Test.methodA();
|
||||
instance.methodB();
|
||||
`,
|
||||
{
|
||||
identifierNamesGenerator: 'mangled',
|
||||
compact: false,
|
||||
simplify: false,
|
||||
stringArray: true,
|
||||
seed: 429105580
|
||||
stringArrayThreshold: 1,
|
||||
stringArrayCallsTransform: true,
|
||||
stringArrayCallsTransformThreshold: 1,
|
||||
stringArrayWrappersCount: 2,
|
||||
stringArrayWrappersParametersMaxCount: 5,
|
||||
stringArrayWrappersType: StringArrayWrappersType.Function,
|
||||
stringArrayEncoding: [StringArrayEncoding.Rc4]
|
||||
}
|
||||
);
|
||||
|
||||
let obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
|
||||
let identifierNamesCache = obfuscationResult.getIdentifierNamesCache();
|
||||
).getObfuscatedCode();
|
||||
|
||||
console.log(obfuscatedCode);
|
||||
console.log(eval(obfuscatedCode));
|
||||
console.log(identifierNamesCache);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -741,6 +741,8 @@ describe('OptionsNormalizer', () => {
|
||||
...getDefaultOptions(),
|
||||
shuffleStringArray: true,
|
||||
stringArray: false,
|
||||
stringArrayCallsTransform: true,
|
||||
stringArrayCallsTransformThreshold: 1,
|
||||
stringArrayEncoding: [StringArrayEncoding.Rc4],
|
||||
stringArrayIndexShift: true,
|
||||
stringArrayWrappersChainedCalls: true,
|
||||
@@ -753,6 +755,8 @@ describe('OptionsNormalizer', () => {
|
||||
...getDefaultOptions(),
|
||||
shuffleStringArray: false,
|
||||
stringArray: false,
|
||||
stringArrayCallsTransform: false,
|
||||
stringArrayCallsTransformThreshold: 0,
|
||||
stringArrayEncoding: [StringArrayEncoding.None],
|
||||
stringArrayIndexShift: false,
|
||||
stringArrayWrappersChainedCalls: 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({
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('NodeMetadata', () => {
|
||||
describe('set', () => {
|
||||
const expectedMetadata: ESTree.LiteralNodeMetadata = {
|
||||
ignoredNode: true,
|
||||
replacedLiteral: true
|
||||
stringArrayCallLiteralNode: true
|
||||
};
|
||||
|
||||
let node: ESTree.Literal;
|
||||
@@ -20,7 +20,7 @@ describe('NodeMetadata', () => {
|
||||
node = NodeFactory.literalNode('foo');
|
||||
NodeMetadata.set(node, {
|
||||
ignoredNode: true,
|
||||
replacedLiteral: true
|
||||
stringArrayCallLiteralNode: true
|
||||
})
|
||||
});
|
||||
|
||||
@@ -38,8 +38,11 @@ describe('NodeMetadata', () => {
|
||||
before(() => {
|
||||
node = NodeFactory.literalNode('foo');
|
||||
node.metadata = {};
|
||||
node.metadata.replacedLiteral = true;
|
||||
value = NodeMetadata.get<ESTree.LiteralNodeMetadata, 'replacedLiteral'>(node, 'replacedLiteral');
|
||||
node.metadata.stringArrayCallLiteralNode = true;
|
||||
value = NodeMetadata.get<ESTree.LiteralNodeMetadata, 'stringArrayCallLiteralNode'>(
|
||||
node,
|
||||
'stringArrayCallLiteralNode'
|
||||
);
|
||||
});
|
||||
|
||||
it('should get metadata value of the node', () => {
|
||||
@@ -92,8 +95,8 @@ describe('NodeMetadata', () => {
|
||||
before(() => {
|
||||
node = NodeFactory.literalNode('foo');
|
||||
node.metadata = {};
|
||||
node.metadata.replacedLiteral = true;
|
||||
value = NodeMetadata.isReplacedLiteral(node);
|
||||
node.metadata.stringArrayCallLiteralNode = true;
|
||||
value = NodeMetadata.isStringArrayCallLiteralNode(node);
|
||||
});
|
||||
|
||||
it('should return metadata value', () => {
|
||||
|
||||
@@ -215,6 +215,68 @@ describe('ArrayUtils', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLastElementByIndex', () => {
|
||||
describe('empty array', () => {
|
||||
const array: string[] = [];
|
||||
const expectedLastElement: undefined = undefined;
|
||||
|
||||
let lastElement: string | undefined;
|
||||
|
||||
before(() => {
|
||||
lastElement = arrayUtils.getLastElementByIndex(array, 1);
|
||||
});
|
||||
|
||||
it('should return undefined if array is empty', () => {
|
||||
assert.equal(lastElement, expectedLastElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('array length: `1` and index is out of array boundary', () => {
|
||||
const array: string[] = ['foo'];
|
||||
const expectedLastElement: undefined = undefined;
|
||||
|
||||
let lastElement: string | undefined;
|
||||
|
||||
before(() => {
|
||||
lastElement = arrayUtils.getLastElementByIndex(array, 2);
|
||||
});
|
||||
|
||||
it('should return undefined', () => {
|
||||
assert.equal(lastElement, expectedLastElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('array length: `3` and index is `0`', () => {
|
||||
const array: string[] = ['foo', 'bar', 'baz'];
|
||||
const expectedLastElement: string = 'baz';
|
||||
|
||||
let lastElement: string | undefined;
|
||||
|
||||
before(() => {
|
||||
lastElement = arrayUtils.getLastElementByIndex(array, 0);
|
||||
});
|
||||
|
||||
it('should return element with a correct index', () => {
|
||||
assert.equal(lastElement, expectedLastElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('array length: `3` and index is `1`', () => {
|
||||
const array: string[] = ['foo', 'bar', 'baz'];
|
||||
const expectedLastElement: string = 'bar';
|
||||
|
||||
let lastElement: string | undefined;
|
||||
|
||||
before(() => {
|
||||
lastElement = arrayUtils.getLastElementByIndex(array, 1);
|
||||
});
|
||||
|
||||
it('should return element with a correct index', () => {
|
||||
assert.equal(lastElement, expectedLastElement);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rotate', () => {
|
||||
let array: number[],
|
||||
rotatedArray: number[];
|
||||
|
||||
Reference in New Issue
Block a user