forceTransformedStrings option

This commit is contained in:
sanex
2020-09-24 00:39:07 +03:00
parent 8fdb22b31d
commit a5d7f7418c
40 changed files with 658 additions and 145 deletions
+4
View File
@@ -1,5 +1,9 @@
Change Log
v2.4.0
---
* **New option:** `forceTransformedStrings` allows force transform strings even if by `stringArrayThreshold` (or possible other thresholds in the future) they shouldn't be transformed
v2.3.1
---
* Fixed a rare bug with `identifierNamesGenerator: 'mangled'` option that causes wrong identifier names generation
+19
View File
@@ -339,6 +339,7 @@ Following options are available for the JS Obfuscator:
debugProtectionInterval: false,
disableConsoleOutput: false,
domainLock: [],
forceTransformedStrings: [],
identifierNamesGenerator: 'hexadecimal',
identifiersDictionary: [],
identifiersPrefix: '',
@@ -391,6 +392,7 @@ Following options are available for the JS Obfuscator:
--disable-console-output <boolean>
--domain-lock '<list>' (comma separated)
--exclude '<list>' (comma separated)
--force-transformed-strings '<list>' (comma separated)
--identifier-names-generator <string> [dictionary, hexadecimal, mangled, mangled-shuffled]
--identifiers-dictionary '<list>' (comma separated)
--identifiers-prefix <string>
@@ -656,6 +658,23 @@ Type: `string[]` Default: `[]`
A file names or globs which indicates files to exclude from obfuscation.
### `forceTransformedStrings`
Type: `string[]` Default: `[]`
Enables force transformation of string literals, which being matched by passed RegExp patterns.
##### :warning: This option affects only strings that shouldn't be transformed by [`stringArrayThreshold`](#stringArrayThreshold) (or possible other thresholds in the future)
Example:
```ts
{
forceTransformedStrings: [
'some-important-value',
'some-string_\d'
]
}
```
### `identifierNamesGenerator`
Type: `string` Default: `hexadecimal`
+7 -7
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "2.3.1",
"version": "2.4.0",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
@@ -11,8 +11,8 @@ import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-a
import { IStringArrayStorageItemData } from '../../interfaces/storages/string-array-transformers/IStringArrayStorageItem';
import { NodeGuards } from '../../node/NodeGuards';
import { NodeMetadata } from '../../node/NodeMetadata';
import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
import { NodeMetadata } from '../../node/NodeMetadata';
/**
* Adds values of literal nodes to the string array storage
@@ -99,7 +99,7 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
* @param {Node} parentNode
*/
private analyzeLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void {
if (typeof literalNode.value !== 'string') {
if (!NodeLiteralUtils.isStringLiteralNode(literalNode)) {
return;
}
@@ -107,7 +107,7 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
return;
}
if (!this.shouldAddValueToStringArray(literalNode.value)) {
if (!this.shouldAddValueToStringArray(literalNode)) {
return;
}
@@ -118,11 +118,17 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
}
/**
* @param {string} value
* @param {(SimpleLiteral & {value: string})} literalNode
* @returns {boolean}
*/
private shouldAddValueToStringArray (value: string): boolean {
return value.length >= StringArrayStorageAnalyzer.minimumLengthForStringArray
private shouldAddValueToStringArray (literalNode: ESTree.Literal & {value: string}): boolean {
const isForceObfuscatedNode: boolean = NodeMetadata.isForceObfuscatedNode(literalNode);
if (isForceObfuscatedNode) {
return true;
}
return literalNode.value.length >= StringArrayStorageAnalyzer.minimumLengthForStringArray
&& this.randomGenerator.getMathRandom() <= this.options.stringArrayThreshold;
}
}
+5
View File
@@ -230,6 +230,11 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
'A filename or glob which indicates files to exclude from obfuscation',
ArraySanitizer
)
.option(
'--force-transformed-strings <list> (comma separated, without whitespaces)',
'Enables force transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
ArraySanitizer
)
.option(
'--identifier-names-generator <string>',
'Sets identifier names generator. ' +
@@ -12,6 +12,7 @@ import { BlackListObfuscatingGuard } from '../../../node-transformers/preparing-
import { ConditionalCommentObfuscatingGuard } from '../../../node-transformers/preparing-transformers/obfuscating-guards/ConditionalCommentObfuscatingGuard';
import { CustomCodeHelpersTransformer } from '../../../node-transformers/preparing-transformers/CustomCodeHelpersTransformer';
import { EvalCallExpressionTransformer } from '../../../node-transformers/preparing-transformers/EvalCallExpressionTransformer';
import { ForceTransformedStringObfuscatingGuard } from '../../../node-transformers/preparing-transformers/obfuscating-guards/ForceTransformedStringObfuscatingGuard';
import { MetadataTransformer } from '../../../node-transformers/preparing-transformers/MetadataTransformer';
import { ObfuscatingGuardsTransformer } from '../../../node-transformers/preparing-transformers/ObfuscatingGuardsTransformer';
import { ParentificationTransformer } from '../../../node-transformers/preparing-transformers/ParentificationTransformer';
@@ -40,6 +41,10 @@ export const preparingTransformersModule: interfaces.ContainerModule = new Conta
.to(ParentificationTransformer)
.whenTargetNamed(NodeTransformer.ParentificationTransformer);
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
.to(VariablePreserveTransformer)
.whenTargetNamed(NodeTransformer.VariablePreserveTransformer);
// obfuscating guards
bind<IObfuscatingGuard>(ServiceIdentifiers.INodeGuard)
.to(BlackListObfuscatingGuard)
@@ -51,15 +56,16 @@ export const preparingTransformersModule: interfaces.ContainerModule = new Conta
.inSingletonScope()
.whenTargetNamed(ObfuscatingGuard.ConditionalCommentObfuscatingGuard);
bind<IObfuscatingGuard>(ServiceIdentifiers.INodeGuard)
.to(ForceTransformedStringObfuscatingGuard)
.inSingletonScope()
.whenTargetNamed(ObfuscatingGuard.ForceTransformedStringObfuscatingGuard);
bind<IObfuscatingGuard>(ServiceIdentifiers.INodeGuard)
.to(ReservedStringObfuscatingGuard)
.inSingletonScope()
.whenTargetNamed(ObfuscatingGuard.ReservedStringObfuscatingGuard);
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
.to(VariablePreserveTransformer)
.whenTargetNamed(NodeTransformer.VariablePreserveTransformer);
// obfuscating guards factory
bind<IObfuscatingGuard>(ServiceIdentifiers.Factory__INodeGuard)
.toFactory<IObfuscatingGuard>(InversifyContainerFacade
+1
View File
@@ -6,6 +6,7 @@ import * as eslintScope from 'eslint-scope';
declare module 'estree' {
export interface BaseNodeMetadata {
forceObfuscatedNode?: boolean;
ignoredNode?: boolean;
}
@@ -1,5 +1,6 @@
export enum ObfuscatingGuard {
BlackListObfuscatingGuard = 'BlackListObfuscatingGuard',
ConditionalCommentObfuscatingGuard = 'ConditionalCommentObfuscatingGuard',
ForceTransformedStringObfuscatingGuard = 'ForceTransformedStringObfuscatingGuard',
ReservedStringObfuscatingGuard = 'ReservedStringObfuscatingGuard'
}
+5
View File
@@ -0,0 +1,5 @@
export enum ObfuscatingGuardResult {
ForceObfuscated = 'ForceObfuscated',
Ignored = 'Ignored',
Obfuscated = 'Obfuscated'
}
@@ -1,9 +1,9 @@
import { TNodeGuard } from '../../../../types/node/TNodeGuard';
import { TObfuscatingGuard } from '../../../../types/node/TObfuscatingGuard';
export interface IObfuscatingGuard {
/**
* @param {Node} node
* @returns {boolean}
*/
check: TNodeGuard;
check: TObfuscatingGuard;
}
+1
View File
@@ -18,6 +18,7 @@ export interface IOptions {
readonly debugProtectionInterval: boolean;
readonly disableConsoleOutput: boolean;
readonly domainLock: string[];
readonly forceTransformedStrings: string[];
readonly identifierNamesGenerator: TypeFromEnum<typeof IdentifierNamesGenerator>;
readonly identifiersDictionary: string[];
readonly identifiersPrefix: string;
@@ -16,6 +16,7 @@ import { ControlFlowCustomNode } from '../../../enums/custom-nodes/ControlFlowCu
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';
@@ -55,7 +56,7 @@ export class StringLiteralControlFlowReplacer extends AbstractControlFlowReplace
return literalNode;
}
if (typeof literalNode.value !== 'string' || literalNode.value.length < 3) {
if (!NodeLiteralUtils.isStringLiteralNode(literalNode) || literalNode.value.length < 3) {
return literalNode;
}
@@ -140,7 +140,7 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
parentNode: ESTree.Node,
chunkLength: number
): ESTree.Node {
if (typeof literalNode.value !== 'string') {
if (!NodeLiteralUtils.isStringLiteralNode(literalNode)) {
return literalNode;
}
@@ -11,8 +11,9 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
import { ObfuscatingGuard } from '../../enums/node-transformers/preparing-transformers/obfuscating-guards/ObfuscatingGuard';
import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
import { ObfuscatingGuard } from '../../enums/node-transformers/preparing-transformers/obfuscating-guards/ObfuscatingGuard';
import { ObfuscatingGuardResult } from '../../enums/node/ObfuscatingGuardResult';
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeGuards } from '../../node/NodeGuards';
@@ -29,6 +30,7 @@ export class ObfuscatingGuardsTransformer extends AbstractNodeTransformer {
private static readonly obfuscatingGuardsList: ObfuscatingGuard[] = [
ObfuscatingGuard.BlackListObfuscatingGuard,
ObfuscatingGuard.ConditionalCommentObfuscatingGuard,
ObfuscatingGuard.ForceTransformedStringObfuscatingGuard,
ObfuscatingGuard.ReservedStringObfuscatingGuard
];
@@ -84,13 +86,37 @@ export class ObfuscatingGuardsTransformer extends AbstractNodeTransformer {
* @returns {Node}
*/
public transformNode (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node {
const obfuscationAllowed: boolean = this.obfuscatingGuards
.every((nodeGuard: IObfuscatingGuard) => nodeGuard.check(node));
const obfuscatingGuardResults: ObfuscatingGuardResult[] = this.obfuscatingGuards
.map((obfuscatingGuard: IObfuscatingGuard) => obfuscatingGuard.check(node));
NodeMetadata.set(node, {
ignoredNode: !(NodeGuards.isProgramNode(node) || obfuscationAllowed)
});
this.setNodeMetadata(node, obfuscatingGuardResults);
return node;
}
/**
* @param {Node} node
* @param {ObfuscatingGuardResult[]} obfuscatingGuardResults
*/
private setNodeMetadata (node: ESTree.Node, obfuscatingGuardResults: ObfuscatingGuardResult[]): void {
let ignoredNode: boolean = false;
let forceObfuscatedNode: boolean = false;
for (const obfuscatingGuardResult of obfuscatingGuardResults) {
if (obfuscatingGuardResult === ObfuscatingGuardResult.Ignored) {
ignoredNode = true;
forceObfuscatedNode = false;
break;
}
if (obfuscatingGuardResult === ObfuscatingGuardResult.ForceObfuscated) {
forceObfuscatedNode = true;
}
}
NodeMetadata.set(node, {
ignoredNode: ignoredNode && !NodeGuards.isProgramNode(node),
forceObfuscatedNode: forceObfuscatedNode && !NodeGuards.isProgramNode(node)
});
}
}
@@ -2,10 +2,10 @@ import { injectable } from 'inversify';
import * as ESTree from 'estree';
import { TNodeGuard } from '../../../types/node/TNodeGuard';
import { IObfuscatingGuard } from '../../../interfaces/node-transformers/preparing-transformers/obfuscating-guards/IObfuscatingGuard';
import { ObfuscatingGuardResult } from '../../../enums/node/ObfuscatingGuardResult';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
@@ -13,7 +13,7 @@ export class BlackListObfuscatingGuard implements IObfuscatingGuard {
/**
* @type {((node: Node) => boolean)[]}
*/
private static readonly blackListGuards: TNodeGuard[] = [
private static readonly blackListGuards: ((node: ESTree.Node) => boolean)[] = [
NodeGuards.isUseStrictOperator
];
@@ -27,16 +27,16 @@ export class BlackListObfuscatingGuard implements IObfuscatingGuard {
}
/**
* @returns {boolean}
* @param node
* @param {Node} node
* @returns {ObfuscatingGuardResult}
*/
public check (node: ESTree.Node): boolean {
public check (node: ESTree.Node): ObfuscatingGuardResult {
for (let i: number = 0; i < this.blackListGuardsLength; i++) {
if (BlackListObfuscatingGuard.blackListGuards[i](node)) {
return false;
return ObfuscatingGuardResult.Ignored;
}
}
return true;
return ObfuscatingGuardResult.Obfuscated;
}
}
@@ -4,6 +4,8 @@ import * as ESTree from 'estree';
import { IObfuscatingGuard } from '../../../interfaces/node-transformers/preparing-transformers/obfuscating-guards/IObfuscatingGuard';
import { ObfuscatingGuardResult } from '../../../enums/node/ObfuscatingGuardResult';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
@@ -33,21 +35,21 @@ export class ConditionalCommentObfuscatingGuard implements IObfuscatingGuard {
}
/**
* @returns {boolean}
* @param node
* @param {Node} node
* @returns {ObfuscatingGuardResult}
*/
public check (node: ESTree.Node): boolean {
if (!NodeGuards.isNodeWithComments(node)) {
return this.obfuscationAllowed;
public check (node: ESTree.Node): ObfuscatingGuardResult {
if (NodeGuards.isNodeWithComments(node)) {
const leadingComments: ESTree.Comment[] | undefined = node.leadingComments;
if (leadingComments) {
this.obfuscationAllowed = this.checkComments(leadingComments);
}
}
const leadingComments: ESTree.Comment[] | undefined = node.leadingComments;
if (leadingComments) {
this.obfuscationAllowed = this.checkComments(leadingComments);
}
return this.obfuscationAllowed;
return this.obfuscationAllowed
? ObfuscatingGuardResult.Obfuscated
: ObfuscatingGuardResult.Ignored;
}
/**
@@ -0,0 +1,58 @@
import { inject, injectable } from 'inversify';
import * as ESTree from 'estree';
import { IObfuscatingGuard } from '../../../interfaces/node-transformers/preparing-transformers/obfuscating-guards/IObfuscatingGuard';
import { IOptions } from '../../../interfaces/options/IOptions';
import { ObfuscatingGuardResult } from '../../../enums/node/ObfuscatingGuardResult';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
export class ForceTransformedStringObfuscatingGuard implements IObfuscatingGuard {
/**
* @type {IOptions}
*/
private readonly options: IOptions;
/**
* @param {IOptions} options
*/
public constructor (
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
this.options = options;
}
/**
* @param {Node} node
* @returns {ObfuscatingGuardResult}
*/
public check (node: ESTree.Node): ObfuscatingGuardResult {
if (
this.options.forceTransformedStrings.length
&& NodeGuards.isLiteralNode(node)
&& typeof node.value === 'string'
) {
return !this.isForceTransformedString(node.value)
? ObfuscatingGuardResult.Obfuscated
: ObfuscatingGuardResult.ForceObfuscated;
}
return ObfuscatingGuardResult.Obfuscated;
}
/**
* @param {string} value
* @returns {boolean}
*/
private isForceTransformedString (value: string): boolean {
return this.options.forceTransformedStrings
.some((forceTransformedString: string) => {
return new RegExp(forceTransformedString, 'g').exec(value) !== null;
});
}
}
@@ -5,6 +5,8 @@ import * as ESTree from 'estree';
import { IObfuscatingGuard } from '../../../interfaces/node-transformers/preparing-transformers/obfuscating-guards/IObfuscatingGuard';
import { IOptions } from '../../../interfaces/options/IOptions';
import { ObfuscatingGuardResult } from '../../../enums/node/ObfuscatingGuardResult';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import { NodeGuards } from '../../../node/NodeGuards';
@@ -26,19 +28,21 @@ export class ReservedStringObfuscatingGuard implements IObfuscatingGuard {
}
/**
* @returns {boolean}
* @param node
* @param {Node} node
* @returns {ObfuscatingGuardResult}
*/
public check (node: ESTree.Node): boolean {
public check (node: ESTree.Node): ObfuscatingGuardResult {
if (
this.options.reservedStrings.length
&& NodeGuards.isLiteralNode(node)
&& typeof node.value === 'string'
) {
return !this.isReservedString(node.value);
return !this.isReservedString(node.value)
? ObfuscatingGuardResult.Obfuscated
: ObfuscatingGuardResult.Ignored;
}
return true;
return ObfuscatingGuardResult.Obfuscated;
}
/**
@@ -191,7 +191,10 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
* @returns {NodeGuards}
*/
public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Node {
if (typeof literalNode.value !== 'string' || NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
if (
!NodeLiteralUtils.isStringLiteralNode(literalNode)
|| NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)
) {
return literalNode;
}
@@ -413,7 +416,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
literalNode: ESTree.Literal,
parentNode: ESTree.Node
): ESTree.Literal {
if (typeof literalNode.value !== 'string') {
if (!NodeLiteralUtils.isStringLiteralNode(literalNode)) {
return literalNode;
}
+8
View File
@@ -3,6 +3,14 @@ import * as ESTree from 'estree';
import { NodeGuards } from './NodeGuards';
export class NodeLiteralUtils {
/**
* @param {Literal} literalNode
* @returns {literalNode is (SimpleLiteral & {value: string})}
*/
public static isStringLiteralNode (literalNode: ESTree.Literal): literalNode is ESTree.Literal & {value: string} {
return typeof literalNode.value === 'string';
}
/**
* @param {Literal} literalNode
* @param {Node} parentNode
+8
View File
@@ -20,6 +20,14 @@ export class NodeMetadata {
: undefined;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isForceObfuscatedNode (node: ESTree.Node): boolean {
return NodeMetadata.get(node, 'forceObfuscatedNode') === true;
}
/**
* @param {Node} node
* @returns {boolean}
+10
View File
@@ -128,6 +128,16 @@ export class Options implements IOptions {
])
public readonly domainLock!: string[];
/**
* @type {string[]}
*/
@IsArray()
@ArrayUnique()
@IsString({
each: true
})
public readonly forceTransformedStrings!: string[];
/**
* @type {IdentifierNamesGenerator}
*/
-2
View File
@@ -17,7 +17,6 @@ import { SourceMapFileNameRule } from './normalizer-rules/SourceMapFileNameRule'
import { SplitStringsChunkLengthRule } from './normalizer-rules/SplitStringsChunkLengthRule';
import { StringArrayRule } from './normalizer-rules/StringArrayRule';
import { StringArrayEncodingRule } from './normalizer-rules/StringArrayEncodingRule';
import { StringArrayThresholdRule } from './normalizer-rules/StringArrayThresholdRule';
import { StringArrayWrappersChainedCallsRule } from './normalizer-rules/StringArrayWappersChainedCalls';
@injectable()
@@ -38,7 +37,6 @@ export class OptionsNormalizer implements IOptionsNormalizer {
SplitStringsChunkLengthRule,
StringArrayRule,
StringArrayEncodingRule,
StringArrayThresholdRule,
StringArrayWrappersChainedCallsRule,
];
@@ -1,27 +0,0 @@
import { TOptionsNormalizerRule } from '../../types/options/TOptionsNormalizerRule';
import { IOptions } from '../../interfaces/options/IOptions';
import { StringArrayEncoding } from '../../enums/node-transformers/string-array-transformers/StringArrayEncoding';
/**
* @param {IOptions} options
* @returns {IOptions}
*/
export const StringArrayThresholdRule: TOptionsNormalizerRule = (options: IOptions): IOptions => {
if (options.stringArrayThreshold === 0) {
options = {
...options,
rotateStringArray: false,
stringArray: false,
stringArrayEncoding: [
StringArrayEncoding.None
],
stringArrayWrappersChainedCalls: false,
stringArrayWrappersCount: 0,
stringArrayThreshold: 0
};
}
return options;
};
+1
View File
@@ -19,6 +19,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
disableConsoleOutput: false,
domainLock: [],
exclude: [],
forceTransformedStrings: [],
identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
identifiersPrefix: '',
identifiersDictionary: [],
+1
View File
@@ -17,6 +17,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
disableConsoleOutput: false,
domainLock: [],
exclude: [],
forceTransformedStrings: [],
identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
identifiersPrefix: '',
identifiersDictionary: [],
-3
View File
@@ -1,3 +0,0 @@
import * as ESTree from 'estree';
export type TNodeGuard = (node: ESTree.Node) => boolean;
+5
View File
@@ -0,0 +1,5 @@
import * as ESTree from 'estree';
import { ObfuscatingGuardResult } from '../../enums/node/ObfuscatingGuardResult';
export type TObfuscatingGuard = (node: ESTree.Node) => ObfuscatingGuardResult;
+2 -1
View File
@@ -18,9 +18,10 @@ import { StringArrayWrappersType } from '../../src/enums/node-transformers/strin
{
...NO_ADDITIONAL_NODES_PRESET,
compact: false,
forceTransformedStrings: ['foo'],
identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
stringArray: true,
stringArrayThreshold: 1,
stringArrayThreshold: 0,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersCount: 1,
stringArrayWrappersType: StringArrayWrappersType.Function
@@ -689,7 +689,188 @@ describe('StringArrayTransformer', function () {
});
});
describe('Variant #16: object expression key literal', () => {
describe('Variant #16: `forceTransformedStrings` option is enabled', () => {
describe('Variant #1: base `forceTransformedStrings` values', () => {
describe('Variant #1: single force transformed string value', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/force-transformed-strings-option-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
forceTransformedStrings: ['bar'],
stringArray: true,
stringArrayThreshold: 0
}
).getObfuscatedCode();
});
it('match #1: should not transform string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should transform force transformed string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
describe('Variant #2: two force transformed string values', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\('0x0'\);/;
const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x1'\);/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/force-transformed-strings-option-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
forceTransformedStrings: ['foo', 'bar'],
stringArray: true,
stringArrayThreshold: 0
}
).getObfuscatedCode();
});
it('match #1: should transform force transformed string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should transform force transformed string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
});
describe('Variant #2: RegExp `forceTransformedStrings` values', () => {
describe('Variant #1: single force transformed string value', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo'/;
const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/force-transformed-strings-option-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
forceTransformedStrings: ['ar$'],
stringArray: true,
stringArrayThreshold: 0
}
).getObfuscatedCode();
});
it('match #1: should not transform string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should transform force transformed string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
describe('Variant #2: two force transformed string values', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\('0x0'\);/;
const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x1'\);/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/force-transformed-strings-option-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
forceTransformedStrings: ['^fo', '.ar'],
stringArray: true,
stringArrayThreshold: 1
}
).getObfuscatedCode();
});
it('match #1: should transform force transformed string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should transform force transformed string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
});
describe('Variant #3: `unicodeEscapeSequence` option is disabled', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/force-transformed-strings-option-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
forceTransformedStrings: ['bar'],
unicodeEscapeSequence: false
}
).getObfuscatedCode();
});
it('match #1: should not encode force transformed string with unicode escape sequence', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should not encode force transformed string with unicode escape sequence', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
describe('Variant #4: `stringArray` option is disabled', () => {
describe('Variant #1: base case', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/force-transformed-strings-option-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
forceTransformedStrings: ['foo', 'bar'],
stringArray: false,
stringArrayThreshold: 0
}
).getObfuscatedCode();
});
it('match #1: should not transform string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should not transform string', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
});
});
describe('Variant #17: object expression key literal', () => {
describe('Variant #1: base key literal', () => {
const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['bar'];/;
const objectExpressionRegExp: RegExp = /var test *= *{'foo' *: *_0x([a-f0-9]){4}\('0x0'\)};/;
@@ -747,7 +928,7 @@ describe('StringArrayTransformer', function () {
});
});
describe('Variant #17: import declaration source literal', () => {
describe('Variant #18: import declaration source literal', () => {
const importDeclarationRegExp: RegExp = /import *{ *bar *} *from *'foo';/;
let obfuscatedCode: string;
@@ -770,7 +951,7 @@ describe('StringArrayTransformer', function () {
});
});
describe('Variant #18: export all declaration source literal', () => {
describe('Variant #19: export all declaration source literal', () => {
const exportAllDeclarationRegExp: RegExp = /export *\* *from *'foo';/;
let obfuscatedCode: string;
@@ -793,7 +974,7 @@ describe('StringArrayTransformer', function () {
});
});
describe('Variant #19: export named declaration source literal', () => {
describe('Variant #20: export named declaration source literal', () => {
const exportNamedDeclarationRegExp: RegExp = /export *{ *bar *} *from *'foo';/;
let obfuscatedCode: string;
@@ -587,34 +587,6 @@ describe('OptionsNormalizer', () => {
});
});
describe('stringArrayThresholdRule', () => {
before(() => {
optionsPreset = getNormalizedOptions({
...getDefaultOptions(),
rotateStringArray: true,
shuffleStringArray: true,
stringArray: true,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersCount: 5,
stringArrayThreshold: 0
});
expectedOptionsPreset = {
...getDefaultOptions(),
rotateStringArray: false,
shuffleStringArray: false,
stringArray: false,
stringArrayWrappersChainedCalls: false,
stringArrayWrappersCount: 0,
stringArrayThreshold: 0
};
});
it('should normalize options preset', () => {
assert.deepEqual(optionsPreset, expectedOptionsPreset);
});
});
describe('stringArrayWrappersChainedCallsRule', () => {
before(() => {
optionsPreset = getNormalizedOptions({
@@ -213,7 +213,7 @@ describe('StringArrayStorageAnalyzer', () => {
});
});
describe('Analyzes of the AST tree with ignored nodes', () => {
describe('Analyzes of the AST tree with ignored string literal nodes', () => {
const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');
NodeMetadata.set(literalNode2, {ignoredNode: true});
@@ -254,6 +254,90 @@ describe('StringArrayStorageAnalyzer', () => {
});
});
describe('Analyzes of the AST tree with force obfuscated string literal nodes', () => {
describe('Variant #1: Force obfuscate string when threshold is `0`', () => {
const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');
NodeMetadata.set(literalNode2, {forceObfuscatedNode: true});
const expectedStringArrayStorageItemData1: undefined = undefined;
const expectedStringArrayStorageItemData2: IStringArrayStorageItemData = {
encodedValue: 'bar',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'bar'
};
let stringArrayStorageItemData1: IStringArrayStorageItemData | undefined;
let stringArrayStorageItemData2: IStringArrayStorageItemData | undefined;
before(() => {
stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
stringArrayThreshold: 0
});
const astTree: ESTree.Program = NodeFactory.programNode([
NodeFactory.expressionStatementNode(literalNode1),
NodeFactory.expressionStatementNode(literalNode2)
]);
stringArrayStorageAnalyzer.analyze(astTree);
stringArrayStorageItemData1 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode1);
stringArrayStorageItemData2 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode2);
});
it('Variant #1: should return correct string array storage item data for literal node #1', () => {
assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
});
it('Variant #2: should return correct string array storage item data for literal node #1', () => {
assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
});
});
describe('Variant #2: Force obfuscate string when string value shorter than allowed length', () => {
const literalNode1: ESTree.Literal = NodeFactory.literalNode('a');
const literalNode2: ESTree.Literal = NodeFactory.literalNode('b');
NodeMetadata.set(literalNode2, {forceObfuscatedNode: true});
const expectedStringArrayStorageItemData1: undefined = undefined;
const expectedStringArrayStorageItemData2: IStringArrayStorageItemData = {
encodedValue: 'b',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'b'
};
let stringArrayStorageItemData1: IStringArrayStorageItemData | undefined;
let stringArrayStorageItemData2: IStringArrayStorageItemData | undefined;
before(() => {
stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
stringArrayThreshold: 1
});
const astTree: ESTree.Program = NodeFactory.programNode([
NodeFactory.expressionStatementNode(literalNode1),
NodeFactory.expressionStatementNode(literalNode2)
]);
stringArrayStorageAnalyzer.analyze(astTree);
stringArrayStorageItemData1 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode1);
stringArrayStorageItemData2 = stringArrayStorageAnalyzer.getItemDataForLiteralNode(literalNode2);
});
it('Variant #1: should return correct string array storage item data for literal node #1', () => {
assert.deepEqual(stringArrayStorageItemData1, expectedStringArrayStorageItemData1);
});
it('Variant #2: should return correct string array storage item data for literal node #1', () => {
assert.deepEqual(stringArrayStorageItemData2, expectedStringArrayStorageItemData2);
});
});
});
/**
* This test covers rare case when with random value inside `shouldAddValueToStringArray` was `0`
* that trigger positive check for method.
@@ -174,6 +174,7 @@ describe('MangledIdentifierNamesGenerator', () => {
describe('isIncrementedMangledName', function () {
this.timeout(60000);
const samplesCount: number = 1000000;
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
inversifyContainerFacade.load('', '', {});
@@ -182,29 +183,29 @@ describe('MangledIdentifierNamesGenerator', () => {
IdentifierNamesGenerator.MangledIdentifierNamesGenerator
);
let isSuccessComparison: boolean = true;
let mangledName: string = '';
let prevMangledName: string = '9';
for (let sample = 0; sample <= 10000; sample++) {
describe(`Variant #${sample + 1}`, () => {
let resultNormal: boolean;
let resultReversed: boolean;
for (let sample = 0; sample <= samplesCount; sample++) {
let resultNormal: boolean;
let resultReversed: boolean;
mangledName = identifierNamesGenerator.generateNext();
resultNormal = MangledIdentifierNamesGenerator.isIncrementedMangledName(mangledName, prevMangledName);
resultReversed = MangledIdentifierNamesGenerator.isIncrementedMangledName(prevMangledName, mangledName);
mangledName = identifierNamesGenerator.generateNext();
resultNormal = MangledIdentifierNamesGenerator.isIncrementedMangledName(mangledName, prevMangledName);
resultReversed = MangledIdentifierNamesGenerator.isIncrementedMangledName(prevMangledName, mangledName);
it(`Variant #1: should compare mangled names: ${mangledName}, ${prevMangledName}`, () => {
assert.isTrue(resultNormal);
});
if (!resultNormal || resultReversed) {
isSuccessComparison = false;
break;
}
it(`Variant #2: should compare mangled names: ${prevMangledName}, ${mangledName}`, () => {
assert.isFalse(resultReversed);
});
prevMangledName = mangledName;
});
prevMangledName = mangledName;
}
it('should correctly compare mangled names', () => {
assert.isTrue(isSuccessComparison);
});
});
describe('isValidIdentifierName', () => {
@@ -12,23 +12,34 @@ import { INodeTransformer } from '../../../../src/interfaces/node-transformers/I
import { NodeTransformer } from '../../../../src/enums/node-transformers/NodeTransformer';
import { NodeFactory } from '../../../../src/node/NodeFactory';
import { NodeUtils } from '../../../../src/node/NodeUtils';
import { NodeMetadata } from '../../../../src/node/NodeMetadata';
import { NodeUtils } from '../../../../src/node/NodeUtils';
describe('ObfuscatingGuardsTransformer', () => {
describe('transformNode', () => {
const forceObfuscatedString: string = 'important string';
const ignoredAndForceObfuscatedString: string = 'important ignored string';
let inversifyContainerFacade: IInversifyContainerFacade,
obfuscatingGuardsTransformer: INodeTransformer;
before(() => {
inversifyContainerFacade = new InversifyContainerFacade();
inversifyContainerFacade.load('', '', {});
inversifyContainerFacade.load('', '', {
forceTransformedStrings: [
forceObfuscatedString,
ignoredAndForceObfuscatedString
],
reservedStrings: [
ignoredAndForceObfuscatedString
]
});
obfuscatingGuardsTransformer = inversifyContainerFacade
.getNamed(ServiceIdentifiers.INodeTransformer, NodeTransformer.ObfuscatingGuardsTransformer);
});
describe('Variant #1: valid node', () => {
describe('Variant #1: allowed node', () => {
const identifier: ESTree.Identifier = NodeFactory.identifierNode('foo');
const expectedResult: ESTree.Identifier = NodeUtils.clone(identifier);
@@ -38,7 +49,10 @@ describe('ObfuscatingGuardsTransformer', () => {
before(() => {
identifier.parentNode = identifier;
NodeMetadata.set(expectedResult, { ignoredNode: false });
NodeMetadata.set(expectedResult, {
forceObfuscatedNode: false,
ignoredNode: false
});
result = <ESTree.Identifier>obfuscatingGuardsTransformer.transformNode(identifier, identifier);
});
@@ -48,7 +62,7 @@ describe('ObfuscatingGuardsTransformer', () => {
});
});
describe('Variant #2: invalid node', () => {
describe('Variant #2: ignored node', () => {
const expressionStatement: ESTree.ExpressionStatement = NodeFactory.directiveNode(
NodeFactory.literalNode('use strict'),
'use strict'
@@ -63,7 +77,10 @@ describe('ObfuscatingGuardsTransformer', () => {
expressionStatement.expression.parentNode = expressionStatement;
expectedResult.parentNode = expectedResult;
NodeMetadata.set(expectedResult, { ignoredNode: true });
NodeMetadata.set(expectedResult, {
forceObfuscatedNode: false,
ignoredNode: true
});
result = <ESTree.ExpressionStatement>obfuscatingGuardsTransformer
.transformNode(expressionStatement, expressionStatement);
@@ -73,5 +90,55 @@ describe('ObfuscatingGuardsTransformer', () => {
assert.deepEqual(result, expectedResult);
});
});
describe('Variant #3: force obfuscated node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode(forceObfuscatedString);
const expectedResult: ESTree.Literal = NodeUtils.clone(literalNode);
let result: ESTree.Literal;
before(() => {
literalNode.parentNode = literalNode;
expectedResult.parentNode = expectedResult;
NodeMetadata.set(expectedResult, {
forceObfuscatedNode: true,
ignoredNode: false
});
result = <ESTree.Literal>obfuscatingGuardsTransformer
.transformNode(literalNode, literalNode);
});
it('should add `forceObfuscatedNode` property with `true` value to given node', () => {
assert.deepEqual(result, expectedResult);
});
});
describe('Variant #4: ignored node and force obfuscated node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode(ignoredAndForceObfuscatedString);
const expectedResult: ESTree.Literal = NodeUtils.clone(literalNode);
let result: ESTree.Literal;
before(() => {
literalNode.parentNode = literalNode;
expectedResult.parentNode = expectedResult;
NodeMetadata.set(expectedResult, {
forceObfuscatedNode: false,
ignoredNode: true
});
result = <ESTree.Literal>obfuscatingGuardsTransformer
.transformNode(literalNode, literalNode);
});
it('should add `ignoredNode` property with `true` value to given node', () => {
assert.deepEqual(result, expectedResult);
});
});
});
});
@@ -6,6 +6,50 @@ import { NodeFactory } from '../../../../src/node/NodeFactory';
import { NodeLiteralUtils } from '../../../../src/node/NodeLiteralUtils';
describe('NodeLiteralUtils', () => {
describe('isStringLiteralNode', () => {
describe('Variant #1: string literal node', () => {
let result: boolean;
before(() => {
const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
result = NodeLiteralUtils.isStringLiteralNode(literalNode);
});
it('should return `true` for string literal node', () => {
assert.isTrue(result);
});
});
describe('Variant #2: number literal node', () => {
let result: boolean;
before(() => {
const literalNode: ESTree.Literal = NodeFactory.literalNode(123);
result = NodeLiteralUtils.isStringLiteralNode(literalNode);
});
it('should return `false` for number literal node', () => {
assert.isFalse(result);
});
});
describe('Variant #3: boolean literal node', () => {
let result: boolean;
before(() => {
const literalNode: ESTree.Literal = NodeFactory.literalNode(true);
result = NodeLiteralUtils.isStringLiteralNode(literalNode);
});
it('should return `false` for boolean literal node', () => {
assert.isFalse(result);
});
});
});
describe('isProhibitedLiteralNode', () => {
describe('String literal node', () => {
describe('Variant #1: base string literal node', () => {
@@ -47,6 +47,24 @@ describe('NodeMetadata', () => {
});
});
describe('isForceObfuscatedNode', () => {
const expectedValue: boolean = true;
let node: ESTree.Identifier,
value: boolean | undefined;
before(() => {
node = NodeFactory.identifierNode('foo');
node.metadata = {};
node.metadata.forceObfuscatedNode = true;
value = NodeMetadata.isForceObfuscatedNode(node);
});
it('should return metadata value', () => {
assert.equal(value, expectedValue);
});
});
describe('isIgnoredNode', () => {
const expectedValue: boolean = true;