Merge pull request #520 from javascript-obfuscator/object-expression-keys-transformer-rework

Object expression keys transformer rework
This commit is contained in:
Timofey Kachalov
2020-01-25 23:49:58 +03:00
committed by GitHub
50 changed files with 1160 additions and 735 deletions
+1
View File
@@ -6,6 +6,7 @@ v0.24.0
* Dynamic import and `import.meta` support. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/505
* Now usage of some browser-related options with `target: 'node'` will cause a validation error
* **CLI:** a file path will be displayed on obfuscation error. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/513
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/512
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/496
* **Internal:** switched from `awesome-typescript-loader` on `ts-loader`
+6 -6
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": "0.24.0-rc.0",
"version": "0.24.0-rc.1",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
@@ -12,6 +12,7 @@ import { IStringArrayStorageItemData } from '../../interfaces/storages/string-ar
import { NodeGuards } from '../../node/NodeGuards';
import { NodeMetadata } from '../../node/NodeMetadata';
import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
/**
* Adds values of literal nodes to the string array storage
@@ -67,7 +68,11 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
}
estraverse.traverse(astTree, {
enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | void => {
if (!parentNode) {
return;
}
if (NodeMetadata.isIgnoredNode(node)) {
return estraverse.VisitorOption.Skip;
}
@@ -76,7 +81,7 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
return;
}
this.analyzeLiteralNode(node);
this.analyzeLiteralNode(node, parentNode);
}
});
}
@@ -91,12 +96,17 @@ export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
/**
* @param {Literal} literalNode
* @param {Node} parentNode
*/
private analyzeLiteralNode (literalNode: ESTree.Literal): void {
private analyzeLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void {
if (typeof literalNode.value !== 'string') {
return;
}
if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
return;
}
if (!this.shouldAddValueToStringArray(literalNode.value)) {
return;
}
+2 -2
View File
@@ -12,7 +12,7 @@ export enum ServiceIdentifiers {
Factory__IObfuscatedCode = 'Factory<IObfuscatedCode>',
Factory__IObfuscatingReplacer = 'Factory<IObfuscatingReplacer>',
Factory__IObjectExpressionKeysTransformerCustomNode = 'Factory<IObjectExpressionKeysTransformerCustomNode>',
Factory__IPropertiesExtractor = 'Factory<IPropertiesExtractor>',
Factory__IObjectExpressionExtractor = 'Factory<IObjectExpressionExtractor>',
Factory__TControlFlowStorage = 'Factory<TControlFlowStorage>',
IArrayUtils = 'IArrayUtils',
ICalleeDataExtractor = 'ICalleeDataExtractor',
@@ -37,7 +37,7 @@ export enum ServiceIdentifiers {
IOptionsNormalizer = 'IOptionsNormalizer',
IObfuscatingReplacer = 'IObfuscatingReplacer',
IPrevailingKindOfVariablesAnalyzer = 'IPrevailingKindOfVariablesAnalyzer',
IPropertiesExtractor = 'IPropertiesExtractor',
IObjectExpressionExtractor = 'IObjectExpressionExtractor',
IRandomGenerator = 'IRandomGenerator',
ISourceCode = 'ISourceCode',
ISourceMapCorrector = 'ISourceMapCorrector',
@@ -18,7 +18,7 @@ import { DomainLockCustomNodeGroup } from '../../../custom-nodes/domain-lock-nod
import { SelfDefendingCustomNodeGroup } from '../../../custom-nodes/self-defending-nodes/group/SelfDefendingCustomNodeGroup';
import { StringArrayCustomNodeGroup } from '../../../custom-nodes/string-array-nodes/group/StringArrayCustomNodeGroup';
import { BasePropertiesExtractorObjectExpressionHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode';
import { ObjectExpressionVariableDeclarationHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode';
import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode';
import { BlockStatementControlFlowFlatteningNode } from '../../../custom-nodes/control-flow-flattening-nodes/BlockStatementControlFlowFlatteningNode';
import { BlockStatementDeadCodeInjectionNode } from '../../../custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode';
@@ -127,8 +127,8 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
// object expression keys transformer nodes
bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
.toConstructor(BasePropertiesExtractorObjectExpressionHostNode)
.whenTargetNamed(ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode);
.toConstructor(ObjectExpressionVariableDeclarationHostNode)
.whenTargetNamed(ObjectExpressionKeysTransformerCustomNode.ObjectExpressionVariableDeclarationHostNode);
// node groups
bind<ICustomNodeGroup>(ServiceIdentifiers.ICustomNodeGroup)
@@ -3,20 +3,19 @@ import { InversifyContainerFacade } from '../../InversifyContainerFacade';
import { ServiceIdentifiers } from '../../ServiceIdentifiers';
import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTransformer';
import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
import { PropertiesExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
import { ObjectExpressionExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor';
import { AssignmentExpressionPropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/AssignmentExpressionPropertiesExtractor';
import { BasePropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/BasePropertiesExtractor';
import { ObjectExpressionToVariableDeclarationExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor';
import { MemberExpressionTransformer } from '../../../node-transformers/converting-transformers/MemberExpressionTransformer';
import { MethodDefinitionTransformer } from '../../../node-transformers/converting-transformers/MethodDefinitionTransformer';
import { ObjectExpressionKeysTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionKeysTransformer';
import { ObjectExpressionTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionTransformer';
import { SplitStringTransformer } from '../../../node-transformers/converting-transformers/SplitStringTransformer';
import { TemplateLiteralTransformer } from '../../../node-transformers/converting-transformers/TemplateLiteralTransformer';
import { VariableDeclaratorPropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/VariableDeclaratorPropertiesExtractor';
import { BasePropertiesExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor';
export const convertingTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
// converting transformers
@@ -44,23 +43,19 @@ export const convertingTransformersModule: interfaces.ContainerModule = new Cont
.to(TemplateLiteralTransformer)
.whenTargetNamed(NodeTransformer.TemplateLiteralTransformer);
// properties extractors
bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
.to(AssignmentExpressionPropertiesExtractor)
.whenTargetNamed(PropertiesExtractor.AssignmentExpressionPropertiesExtractor);
// object expression extractors
bind<IObjectExpressionExtractor>(ServiceIdentifiers.IObjectExpressionExtractor)
.to(ObjectExpressionToVariableDeclarationExtractor)
.whenTargetNamed(ObjectExpressionExtractor.ObjectExpressionToVariableDeclarationExtractor);
bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
bind<IObjectExpressionExtractor>(ServiceIdentifiers.IObjectExpressionExtractor)
.to(BasePropertiesExtractor)
.whenTargetNamed(PropertiesExtractor.BasePropertiesExtractor);
.whenTargetNamed(ObjectExpressionExtractor.BasePropertiesExtractor);
bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
.to(VariableDeclaratorPropertiesExtractor)
.whenTargetNamed(PropertiesExtractor.VariableDeclaratorPropertiesExtractor);
// properties extractor factory
bind<IPropertiesExtractor>(ServiceIdentifiers.Factory__IPropertiesExtractor)
.toFactory<IPropertiesExtractor>(InversifyContainerFacade
.getCacheFactory<PropertiesExtractor, IPropertiesExtractor>(
ServiceIdentifiers.IPropertiesExtractor
// object expression extractor factory
bind<IObjectExpressionExtractor>(ServiceIdentifiers.Factory__IObjectExpressionExtractor)
.toFactory<IObjectExpressionExtractor>(InversifyContainerFacade
.getCacheFactory<ObjectExpressionExtractor, IObjectExpressionExtractor>(
ServiceIdentifiers.IObjectExpressionExtractor
));
});
@@ -1,6 +1,8 @@
import { inject, injectable, } from 'inversify';
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { TIdentifierNamesGeneratorFactory } from '../../types/container/generators/TIdentifierNamesGeneratorFactory';
import { TStatement } from '../../types/node/TStatement';
@@ -12,7 +14,12 @@ import { AbstractCustomNode } from '../AbstractCustomNode';
import { NodeFactory } from '../../node/NodeFactory';
@injectable()
export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCustomNode {
export class ObjectExpressionVariableDeclarationHostNode extends AbstractCustomNode {
/**
* @ type {Property}
*/
private properties!: ESTree.Property[];
/**
* @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
* @param {ICustomNodeFormatter} customNodeFormatter
@@ -29,7 +36,9 @@ export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCus
super(identifierNamesGeneratorFactory, customNodeFormatter, randomGenerator, options);
}
public initialize (): void {}
public initialize (properties: ESTree.Property[]): void {
this.properties = properties;
}
/**
* @param {string} nodeTemplate
@@ -42,7 +51,7 @@ export class BasePropertiesExtractorObjectExpressionHostNode extends AbstractCus
NodeFactory.identifierNode(
this.identifierNamesGenerator.generate()
),
NodeFactory.objectExpressionNode([])
NodeFactory.objectExpressionNode(this.properties)
)
],
'const'
@@ -1,4 +1,4 @@
export enum ObjectExpressionKeysTransformerCustomNode {
BasePropertiesExtractorObjectExpressionHostNode =
'BasePropertiesExtractorObjectExpressionHostNode'
ObjectExpressionVariableDeclarationHostNode =
'ObjectExpressionVariableDeclarationHostNode'
}
@@ -0,0 +1,4 @@
export enum ObjectExpressionExtractor {
BasePropertiesExtractor = 'BasePropertiesExtractor',
ObjectExpressionToVariableDeclarationExtractor = 'ObjectExpressionToVariableDeclarationExtractor'
}
@@ -1,5 +0,0 @@
export enum PropertiesExtractor {
AssignmentExpressionPropertiesExtractor = 'AssignmentExpressionPropertiesExtractor',
BasePropertiesExtractor = 'BasePropertiesExtractor',
VariableDeclaratorPropertiesExtractor = 'VariableDeclaratorPropertiesExtractor'
}
@@ -0,0 +1,15 @@
import * as ESTree from 'estree';
import { IObjectExpressionExtractorResult } from './IObjectExpressionExtractorResult';
export interface IObjectExpressionExtractor {
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {IObjectExpressionExtractorResult}
*/
extract (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): IObjectExpressionExtractorResult;
}
@@ -0,0 +1,18 @@
import * as ESTree from 'estree';
export interface IObjectExpressionExtractorResult {
/**
* @type {Node}
*/
nodeToReplace: ESTree.Node;
/**
* @type {Statement}
*/
objectExpressionHostStatement: ESTree.Statement;
/**
* @type {ObjectExpression}
*/
objectExpressionNode: ESTree.ObjectExpression;
}
@@ -1,13 +0,0 @@
import * as ESTree from 'estree';
export interface IPropertiesExtractor {
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Node} hostNode
* @returns {Node}
*/
extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.Node
): ESTree.Node;
}
@@ -1,51 +1,164 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
import * as estraverse from 'estraverse';
import * as ESTree from 'estree';
import { TPropertiesExtractorFactory } from '../../types/container/node-transformers/TPropertiesExtractorFactory';
import { TObjectExpressionExtractorFactory } from '../../types/container/node-transformers/TObjectExpressionExtractorFactory';
import { IOptions } from '../../interfaces/options/IOptions';
import { IPropertiesExtractor } from '../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
import { NodeType } from '../../enums/node/NodeType';
import { PropertiesExtractor } from '../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeGuards } from '../../node/NodeGuards';
import { NodeStatementUtils } from '../../node/NodeStatementUtils';
import { ObjectExpressionExtractor } from '../../enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor';
@injectable()
export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
/**
* @type {Map<string, PropertiesExtractor>}
* @type {ObjectExpressionExtractor[]}
*/
private static readonly propertiesExtractorsMap: Map <string, PropertiesExtractor> = new Map([
[NodeType.AssignmentExpression, PropertiesExtractor.AssignmentExpressionPropertiesExtractor],
[NodeType.VariableDeclarator, PropertiesExtractor.VariableDeclaratorPropertiesExtractor]
]);
private static readonly objectExpressionExtractorNames: ObjectExpressionExtractor[] = [
ObjectExpressionExtractor.ObjectExpressionToVariableDeclarationExtractor,
ObjectExpressionExtractor.BasePropertiesExtractor
];
/**
* @type {TPropertiesExtractorFactory}
* @type {TObjectExpressionExtractorFactory}
*/
private readonly propertiesExtractorFactory: TPropertiesExtractorFactory;
private readonly objectExpressionExtractorFactory: TObjectExpressionExtractorFactory;
/**
* @param {TPropertiesExtractorFactory} propertiesExtractorFactory
* @param {TObjectExpressionExtractorFactory} objectExpressionExtractorFactory
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.Factory__IPropertiesExtractor)
propertiesExtractorFactory: TPropertiesExtractorFactory,
@inject(ServiceIdentifiers.Factory__IObjectExpressionExtractor)
objectExpressionExtractorFactory: TObjectExpressionExtractorFactory,
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
this.propertiesExtractorFactory = propertiesExtractorFactory;
this.objectExpressionExtractorFactory = objectExpressionExtractorFactory;
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {boolean}
*/
private static isProhibitedHostStatement (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): boolean {
return ObjectExpressionKeysTransformer.isProhibitedVariableDeclarationHostStatement(objectExpressionNode, hostStatement)
|| ObjectExpressionKeysTransformer.isProhibitedFunctionHostStatement(objectExpressionNode, hostStatement);
}
/**
* Fix of https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
* If object expression is placed inside any expression inside variable declaration with 2+ declarators
* - should mark host node as prohibited
*
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {boolean}
*/
private static isProhibitedVariableDeclarationHostStatement (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): boolean {
if (!NodeGuards.isVariableDeclarationNode(hostStatement) || !hostStatement.declarations.length) {
return false;
}
return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
objectExpressionNode,
hostStatement.declarations
);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {boolean}
*/
private static isProhibitedFunctionHostStatement (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): boolean {
if (!NodeGuards.isFunctionNode(hostStatement) || !hostStatement.params.length) {
return false;
}
const hostNode: ESTree.Node | undefined = objectExpressionNode.parentNode;
if (!hostNode || !NodeGuards.isAssignmentPatternNode(hostNode)) {
return false;
}
return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
objectExpressionNode,
hostStatement.params
);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Node[]} nodesToSearch
* @returns {boolean}
*/
private static isReferencedIdentifierName (
objectExpressionNode: ESTree.ObjectExpression,
nodesToSearch: ESTree.Node[],
): boolean {
if (nodesToSearch.length === 1) {
return false;
}
const identifierNamesSet: string[] = [];
let isReferencedIdentifierName: boolean = false;
let isCurrentNode: boolean = false;
// should mark node as prohibited if identifier of node is referenced somewhere inside other nodes
for (const nodeToSearch of nodesToSearch) {
const identifierNamesSetForCurrentNode: string[] = [];
estraverse.traverse(nodeToSearch, {
enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
if (node === objectExpressionNode) {
isCurrentNode = true;
}
if (!NodeGuards.isIdentifierNode(node)) {
return;
}
if (!isCurrentNode) {
identifierNamesSetForCurrentNode.push(node.name);
} else if (identifierNamesSet.includes(node.name)) {
isReferencedIdentifierName = true;
return estraverse.VisitorOption.Break;
}
}
});
if (isCurrentNode || isReferencedIdentifierName) {
break;
} else {
identifierNamesSet.push(...identifierNamesSetForCurrentNode);
}
}
return isReferencedIdentifierName;
}
/**
@@ -57,28 +170,22 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
return null;
}
if (transformationStage !== TransformationStage.Converting) {
return null;
}
switch (transformationStage) {
case TransformationStage.Converting:
return {
leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
if (
parentNode
&& NodeGuards.isObjectExpressionNode(node)
) {
return this.transformNode(node, parentNode);
}
}
};
return {
enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
if (
parentNode
&& NodeGuards.isObjectExpressionNode(node)
) {
return this.transformNode(node, parentNode);
}
},
leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
if (
parentNode
&& NodeGuards.isObjectExpressionNode(node)
) {
return this.transformNodeWithBaseExtractor(node, parentNode);
}
}
};
default:
return null;
}
}
/**
@@ -89,9 +196,10 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
* };
*
* on:
* var object = {};
* object['foo'] = 1;
* object['bar'] = 2;
* var _0xabc123 = {};
* _0xabc123['foo'] = 1;
* _0xabc123['bar'] = 2;
* var object = _0xabc123;
*
* @param {ObjectExpression} objectExpressionNode
* @param {Node} parentNode
@@ -102,43 +210,51 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
return objectExpressionNode;
}
const propertiesExtractorName: PropertiesExtractor | undefined = ObjectExpressionKeysTransformer
.propertiesExtractorsMap
.get(parentNode.type);
const hostStatement: ESTree.Statement = NodeStatementUtils.getRootStatementOfNode(objectExpressionNode);
if (!propertiesExtractorName) {
if (ObjectExpressionKeysTransformer.isProhibitedHostStatement(objectExpressionNode, hostStatement)) {
return objectExpressionNode;
}
const propertiesExtractor: IPropertiesExtractor = this.propertiesExtractorFactory(propertiesExtractorName);
return propertiesExtractor.extract(objectExpressionNode, parentNode);
return this.applyObjectExpressionKeysExtractorsRecursive(
ObjectExpressionKeysTransformer.objectExpressionExtractorNames,
objectExpressionNode,
hostStatement
);
}
/**
* replaces:
* return {
* foo: 1,
* bar: 2
* };
*
* on:
* var object = {};
* object['foo'] = 1;
* object['bar'] = 2;
* return object;
*
* @param {ObjectExpressionExtractor[]} objectExpressionExtractorNames
* @param {ObjectExpression} objectExpressionNode
* @param {Node} parentNode
* @returns {NodeGuards}
* @param {Statement} hostStatement
* @returns {Node}
*/
public transformNodeWithBaseExtractor (objectExpressionNode: ESTree.ObjectExpression, parentNode: ESTree.Node): ESTree.Node {
if (!objectExpressionNode.properties.length) {
private applyObjectExpressionKeysExtractorsRecursive (
objectExpressionExtractorNames: ObjectExpressionExtractor[],
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): ESTree.Node {
const newObjectExpressionExtractorNames: ObjectExpressionExtractor[] = [...objectExpressionExtractorNames];
const objectExpressionExtractor: ObjectExpressionExtractor | undefined =
newObjectExpressionExtractorNames.shift();
if (!objectExpressionExtractor) {
return objectExpressionNode;
}
const propertiesExtractor: IPropertiesExtractor = this.propertiesExtractorFactory(PropertiesExtractor.BasePropertiesExtractor);
const {
nodeToReplace,
objectExpressionHostStatement: newObjectExpressionHostStatement,
objectExpressionNode: newObjectExpressionNode
} = this.objectExpressionExtractorFactory(objectExpressionExtractor)
.extract(objectExpressionNode, hostStatement);
return propertiesExtractor.extract(objectExpressionNode, parentNode);
this.applyObjectExpressionKeysExtractorsRecursive(
newObjectExpressionExtractorNames,
newObjectExpressionNode,
newObjectExpressionHostStatement
);
return nodeToReplace;
}
}
@@ -12,7 +12,6 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeFactory } from '../../node/NodeFactory';
import { NodeGuards } from '../../node/NodeGuards';
import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
/**
* replaces:
@@ -24,23 +23,14 @@ import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEn
@injectable()
export class ObjectExpressionTransformer extends AbstractNodeTransformer {
/**
* @type {IEscapeSequenceEncoder}
*/
private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
/**
* @param {IEscapeSequenceEncoder} escapeSequenceEncoder
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
this.escapeSequenceEncoder = escapeSequenceEncoder;
}
/**
@@ -93,7 +83,7 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
return;
}
property.key = NodeFactory.literalNode(this.getPropertyKeyValue(property.key.value));
property.key = NodeFactory.literalNode(property.key.value);
}
/**
@@ -108,16 +98,6 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
return;
}
property.key = NodeFactory.literalNode(this.getPropertyKeyValue(property.key.name));
}
/**
* @param {string} inputValue
* @returns {string}
*/
private getPropertyKeyValue (inputValue: string): string {
return this.options.unicodeEscapeSequence
? this.escapeSequenceEncoder.encode(inputValue, true)
: inputValue;
property.key = NodeFactory.literalNode(property.key.name);
}
}
@@ -1,13 +1,11 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import { injectable } from 'inversify';
import * as ESTree from 'estree';
import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
import { IObjectExpressionExtractorResult } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractorResult';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
import { NodeAppender } from '../../../node/NodeAppender';
import { NodeFactory } from '../../../node/NodeFactory';
@@ -15,48 +13,12 @@ import { NodeGuards } from '../../../node/NodeGuards';
import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
@injectable()
export abstract class AbstractPropertiesExtractor implements IPropertiesExtractor {
/**
* @type {Map<ESTree.ObjectExpression, TNodeWithStatements>}
*/
protected readonly cachedHostNodesWithStatementsMap: Map <ESTree.ObjectExpression, TNodeWithStatements> = new Map();
/**
* @type {Map<ESTree.ObjectExpression, ESTree.Statement>}
*/
protected readonly cachedHostStatementsMap: Map <ESTree.ObjectExpression, ESTree.Statement> = new Map();
/**
* @type {IOptions}
*/
protected readonly options: IOptions;
/**
* @type {IRandomGenerator}
*/
protected readonly randomGenerator: IRandomGenerator;
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
this.randomGenerator = randomGenerator;
this.options = options;
}
export class BasePropertiesExtractor implements IObjectExpressionExtractor {
/**
* @param {Property} propertyNode
* @returns {string | null}
*/
protected static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
if (!propertyNode.key) {
return null;
}
private static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
const propertyKeyNode: ESTree.Expression = propertyNode.key;
if (
@@ -80,7 +42,7 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
* @param {Node} node
* @returns {propertyValueNode is Pattern}
*/
protected static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
private static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
return !node
|| NodeGuards.isObjectPatternNode(node)
|| NodeGuards.isArrayPatternNode(node)
@@ -89,22 +51,82 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
}
/**
* extracts object expression properties:
* var _0xabc123 = {
* foo: 1,
* bar: 2
* };
*
* to:
* var _0xabc123 = {};
* _0xabc123['foo'] = 1;
* _0xabc123['bar'] = 2;
*
* @param {ObjectExpression} objectExpressionNode
* @param {Node} hostNode
* @returns {Node}
* @param {Statement} hostStatement
* @returns {IObjectExpressionExtractorResult}
*/
public abstract extract (
public extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.Node
): ESTree.Node;
hostStatement: ESTree.Statement
): IObjectExpressionExtractorResult {
const hostNode: ESTree.Node | undefined = objectExpressionNode.parentNode;
if (
hostNode
&& NodeGuards.isVariableDeclaratorNode(hostNode)
&& NodeGuards.isIdentifierNode(hostNode.id)
) {
return this.transformObjectExpressionNode(objectExpressionNode, hostStatement, hostNode.id);
}
return {
nodeToReplace: objectExpressionNode,
objectExpressionHostStatement: hostStatement,
objectExpressionNode: objectExpressionNode
};
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @param {Expression} memberExpressionHostNode
* @returns {IObjectExpressionExtractorResult}
*/
private transformObjectExpressionNode (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement,
memberExpressionHostNode: ESTree.Expression
): IObjectExpressionExtractorResult {
const properties: ESTree.Property[] = objectExpressionNode.properties;
const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
.extractPropertiesToExpressionStatements(
properties,
hostStatement,
memberExpressionHostNode
);
const hostNodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
NodeAppender.insertAfter(hostNodeWithStatements, expressionStatements, hostStatement);
return {
nodeToReplace: objectExpressionNode,
objectExpressionHostStatement: hostStatement,
objectExpressionNode: objectExpressionNode
};
}
/**
* @param {Property[]} properties
* @param {Statement} hostStatement
* @param {Expression} memberExpressionHostNode
* @returns {[ExpressionStatement[] , number[]]}
* @returns {[ExpressionStatement[], number[]]}
*/
protected extractPropertiesToExpressionStatements (
private extractPropertiesToExpressionStatements (
properties: ESTree.Property[],
hostStatement: ESTree.Statement,
memberExpressionHostNode: ESTree.Expression
): [ESTree.ExpressionStatement[], number[]] {
const propertiesLength: number = properties.length;
@@ -116,14 +138,14 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
const propertyValue: ESTree.Expression | ESTree.Pattern = property.value;
// invalid property nodes
if (AbstractPropertiesExtractor.isProhibitedPattern(propertyValue)) {
if (BasePropertiesExtractor.isProhibitedPattern(propertyValue)) {
continue;
}
/**
* Stage 1: extract property node key names
*/
const propertyKeyName: string | null = AbstractPropertiesExtractor.getPropertyNodeKeyName(property);
const propertyKeyName: string | null = BasePropertiesExtractor.getPropertyNodeKeyName(property);
if (!propertyKeyName) {
continue;
@@ -147,7 +169,7 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
* Stage 3: recursively processing nested object expressions
*/
if (NodeGuards.isObjectExpressionNode(property.value)) {
this.transformObjectExpressionNode(property.value, memberExpressionNode);
this.transformObjectExpressionNode(property.value, hostStatement, memberExpressionNode);
}
/**
@@ -164,74 +186,11 @@ export abstract class AbstractPropertiesExtractor implements IPropertiesExtracto
* @param {ObjectExpression} objectExpressionNode
* @param {number[]} removablePropertyIds
*/
protected filterExtractedObjectExpressionProperties (
private filterExtractedObjectExpressionProperties (
objectExpressionNode: ESTree.ObjectExpression,
removablePropertyIds: number[]
): void {
objectExpressionNode.properties = objectExpressionNode.properties
.filter((property: ESTree.Property, index: number) => !removablePropertyIds.includes(index));
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Expression} memberExpressionHostNode
* @returns {Node}
*/
protected transformObjectExpressionNode (
objectExpressionNode: ESTree.ObjectExpression,
memberExpressionHostNode: ESTree.Expression
): ESTree.Node {
const properties: ESTree.Property[] = objectExpressionNode.properties;
const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
.extractPropertiesToExpressionStatements(properties, memberExpressionHostNode);
const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
const hostNodeWithStatements: TNodeWithStatements = this.getHostNodeWithStatements(
objectExpressionNode,
hostStatement
);
this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
NodeAppender.insertAfter(hostNodeWithStatements, expressionStatements, hostStatement);
return objectExpressionNode;
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {TNodeWithStatements}
*/
protected getHostNodeWithStatements (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): TNodeWithStatements {
if (this.cachedHostNodesWithStatementsMap.has(objectExpressionNode)) {
return <TNodeWithStatements>this.cachedHostNodesWithStatementsMap.get(objectExpressionNode);
}
const nodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
this.cachedHostNodesWithStatementsMap.set(objectExpressionNode, nodeWithStatements);
return nodeWithStatements;
}
/**
* Returns host statement of object expression node
*
* @param {NodeGuards} objectExpressionNode
* @returns {Node}
*/
protected getHostStatement (objectExpressionNode: ESTree.ObjectExpression): ESTree.Statement {
if (this.cachedHostStatementsMap.has(objectExpressionNode)) {
return <ESTree.Statement>this.cachedHostStatementsMap.get(objectExpressionNode);
}
const hostStatement: ESTree.Statement = NodeStatementUtils.getRootStatementOfNode(objectExpressionNode);
this.cachedHostStatementsMap.set(objectExpressionNode, hostStatement);
return hostStatement;
}
}
@@ -0,0 +1,148 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
import { TObjectExpressionKeysTransformerCustomNodeFactory } from '../../../types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory';
import { IObjectExpressionExtractorResult } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractorResult';
import { TStatement } from '../../../types/node/TStatement';
import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
import { TInitialData } from '../../../types/TInitialData';
import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
import { ObjectExpressionVariableDeclarationHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/ObjectExpressionVariableDeclarationHostNode';
import { NodeAppender } from '../../../node/NodeAppender';
import { NodeGuards } from '../../../node/NodeGuards';
import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
import { NodeUtils } from '../../../node/NodeUtils';
@injectable()
export class ObjectExpressionToVariableDeclarationExtractor implements IObjectExpressionExtractor {
/**
* @type {TObjectExpressionKeysTransformerCustomNodeFactory}
*/
private readonly objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory;
/**
* @param {TObjectExpressionKeysTransformerCustomNodeFactory} objectExpressionKeysTransformerCustomNodeFactory
*/
constructor (
@inject(ServiceIdentifiers.Factory__IObjectExpressionKeysTransformerCustomNode)
objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory,
) {
this.objectExpressionKeysTransformerCustomNodeFactory = objectExpressionKeysTransformerCustomNodeFactory;
}
/**
* extracts object expression:
* var object = {
* foo: 1,
* bar: 2
* };
*
* to:
* var _0xabc123 = {
* foo: 1,
* bar: 2
* };
* var object = _0xabc123;
*
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {IObjectExpressionExtractorResult}
*/
public extract (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): IObjectExpressionExtractorResult {
return this.transformObjectExpressionToVariableDeclaration(
objectExpressionNode,
hostStatement
);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {Node}
*/
private transformObjectExpressionToVariableDeclaration (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): IObjectExpressionExtractorResult {
const properties: ESTree.Property[] = objectExpressionNode.properties;
const newObjectExpressionHostStatement: ESTree.VariableDeclaration = this.getObjectExpressionHostNode(properties);
const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostStatement);
const newObjectExpressionNode: ESTree.ObjectExpression = this.getObjectExpressionNode(newObjectExpressionHostStatement);
const statementsToInsert: TStatement[] = [newObjectExpressionHostStatement];
const hostNodeWithStatements: TNodeWithStatements = NodeStatementUtils.getScopeOfNode(hostStatement);
NodeAppender.insertBefore(hostNodeWithStatements, statementsToInsert, hostStatement);
NodeUtils.parentizeAst(newObjectExpressionHostStatement);
NodeUtils.parentizeNode(newObjectExpressionHostStatement, hostNodeWithStatements);
return {
nodeToReplace: newObjectExpressionIdentifier,
objectExpressionHostStatement: newObjectExpressionHostStatement,
objectExpressionNode: newObjectExpressionNode
};
}
/**
* @param {Property[]} properties
* @returns {VariableDeclaration}
*/
private getObjectExpressionHostNode (properties: ESTree.Property[]): ESTree.VariableDeclaration {
const variableDeclarationHostNodeCustomNode: ICustomNode<TInitialData<ObjectExpressionVariableDeclarationHostNode>> =
this.objectExpressionKeysTransformerCustomNodeFactory(
ObjectExpressionKeysTransformerCustomNode.ObjectExpressionVariableDeclarationHostNode
);
variableDeclarationHostNodeCustomNode.initialize(properties);
const statementNode: TStatement = variableDeclarationHostNodeCustomNode.getNode()[0];
if (
!statementNode
|| !NodeGuards.isVariableDeclarationNode(statementNode)
) {
throw new Error(`\`objectExpressionHostCustomNode.getNode()[0]\` should returns array with \`VariableDeclaration\` node`);
}
return statementNode;
}
/**
* @param {VariableDeclaration} objectExpressionHostNode
* @returns {Identifier}
*/
private getObjectExpressionIdentifierNode (objectExpressionHostNode: ESTree.VariableDeclaration): ESTree.Identifier {
const newObjectExpressionIdentifierNode: ESTree.Pattern = objectExpressionHostNode.declarations[0].id;
if (!NodeGuards.isIdentifierNode(newObjectExpressionIdentifierNode)) {
throw new Error(`\`objectExpressionHostNode\` should contain \`VariableDeclarator\` node with \`Identifier\` id property`);
}
return newObjectExpressionIdentifierNode;
}
/**
* @param {VariableDeclaration} objectExpressionHostNode
* @returns {Identifier}
*/
private getObjectExpressionNode (objectExpressionHostNode: ESTree.VariableDeclaration): ESTree.ObjectExpression {
const newObjectExpressionNode: ESTree.Expression | null = objectExpressionHostNode.declarations[0].init ?? null;
if (!newObjectExpressionNode || !NodeGuards.isObjectExpressionNode(newObjectExpressionNode)) {
throw new Error(`\`objectExpressionHostNode\` should contain \`VariableDeclarator\` node with \`ObjectExpression\` init property`);
}
return newObjectExpressionNode;
}
}
@@ -1,81 +0,0 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
export class AssignmentExpressionPropertiesExtractor extends AbstractPropertiesExtractor {
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
}
/**
* @param {Node} node
* @returns {propertyValueNode is Pattern}
*/
private static isProhibitedHostParent (node: ESTree.Node): node is ESTree.Pattern {
if (NodeGuards.isMemberExpressionNode(node)) {
return true;
}
if (AssignmentExpressionPropertiesExtractor.isProhibitedStatementNode(node)) {
return true;
}
// statements without block statement
return NodeGuards.isExpressionStatementNode(node)
&& !!node.parentNode
&& AssignmentExpressionPropertiesExtractor.isProhibitedStatementNode(node.parentNode);
}
/**
* @param {Node} node
* @returns {boolean}
*/
private static isProhibitedStatementNode (node: ESTree.Node): boolean {
return NodeGuards.isIfStatementNode(node)
|| NodeGuards.isForStatementTypeNode(node)
|| NodeGuards.isWhileStatementNode(node);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {AssignmentExpression} hostNode
* @returns {Node}
*/
public extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.AssignmentExpression
): ESTree.Node {
const hostParentNode: ESTree.Node | undefined = hostNode.parentNode;
const leftNode: ESTree.MemberExpression | ESTree.Pattern = hostNode.left;
// left node shouldn't be as Pattern node
if (AbstractPropertiesExtractor.isProhibitedPattern(leftNode)) {
return objectExpressionNode;
}
// left node shouldn't be as prohibited node
if (hostParentNode && AssignmentExpressionPropertiesExtractor.isProhibitedHostParent(hostParentNode)) {
return objectExpressionNode;
}
return this.transformObjectExpressionNode(
objectExpressionNode,
leftNode
);
}
}
@@ -1,129 +0,0 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { TNodeWithStatements } from '../../../types/node/TNodeWithStatements';
import { TObjectExpressionKeysTransformerCustomNodeFactory } from '../../../types/container/custom-nodes/TObjectExpressionKeysTransformerCustomNodeFactory';
import { TStatement } from '../../../types/node/TStatement';
import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
import { TInitialData } from '../../../types/TInitialData';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { ObjectExpressionKeysTransformerCustomNode } from '../../../enums/custom-nodes/ObjectExpressionKeysTransformerCustomNode';
import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
import { BasePropertiesExtractorObjectExpressionHostNode } from '../../../custom-nodes/object-expression-keys-transformer-nodes/BasePropertiesExtractorObjectExpressionHostNode';
import { NodeAppender } from '../../../node/NodeAppender';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
export class BasePropertiesExtractor extends AbstractPropertiesExtractor {
/**
* @type {TObjectExpressionKeysTransformerCustomNodeFactory}
*/
private readonly objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory;
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
* @param {TObjectExpressionKeysTransformerCustomNodeFactory} objectExpressionKeysTransformerCustomNodeFactory
*/
constructor (
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions,
@inject(ServiceIdentifiers.Factory__IObjectExpressionKeysTransformerCustomNode)
objectExpressionKeysTransformerCustomNodeFactory: TObjectExpressionKeysTransformerCustomNodeFactory,
) {
super(randomGenerator, options);
this.objectExpressionKeysTransformerCustomNodeFactory = objectExpressionKeysTransformerCustomNodeFactory;
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Node} parentNode
* @returns {Node}
*/
public extract (
objectExpressionNode: ESTree.ObjectExpression,
parentNode: ESTree.Node
): ESTree.Node {
return this.transformObjectExpressionNode(
objectExpressionNode,
parentNode
);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Node} parentNode
* @returns {Node}
*/
protected transformObjectExpressionNode (
objectExpressionNode: ESTree.ObjectExpression,
parentNode: ESTree.Node
): ESTree.Node {
const newObjectExpressionHostNode: ESTree.VariableDeclaration = this.getObjectExpressionHostNode();
const newObjectExpressionIdentifier: ESTree.Identifier = this.getObjectExpressionIdentifierNode(newObjectExpressionHostNode);
const properties: ESTree.Property[] = objectExpressionNode.properties;
const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
.extractPropertiesToExpressionStatements(properties, newObjectExpressionIdentifier);
const statementsToInsert: TStatement[] = [
newObjectExpressionHostNode,
...expressionStatements
];
const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
const hostNodeWithStatements: TNodeWithStatements = this.getHostNodeWithStatements(
objectExpressionNode,
hostStatement
);
this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
NodeAppender.insertBefore(hostNodeWithStatements, statementsToInsert, hostStatement);
return newObjectExpressionIdentifier;
}
/**
* @returns {VariableDeclaration}
*/
private getObjectExpressionHostNode (): ESTree.VariableDeclaration {
const objectExpressionHostCustomNode: ICustomNode<TInitialData<BasePropertiesExtractorObjectExpressionHostNode>> =
this.objectExpressionKeysTransformerCustomNodeFactory(
ObjectExpressionKeysTransformerCustomNode.BasePropertiesExtractorObjectExpressionHostNode
);
objectExpressionHostCustomNode.initialize();
const statementNode: TStatement = objectExpressionHostCustomNode.getNode()[0];
if (
!statementNode
|| !NodeGuards.isVariableDeclarationNode(statementNode)
) {
throw new Error(`\`objectExpressionHostCustomNode.getNode()[0]\` should returns array with \`VariableDeclaration\` node`);
}
return statementNode;
}
/**
* @param {VariableDeclaration} objectExpressionHostNode
* @returns {Identifier}
*/
private getObjectExpressionIdentifierNode (objectExpressionHostNode: ESTree.VariableDeclaration): ESTree.Identifier {
const newObjectExpressionIdentifierNode: ESTree.Pattern = objectExpressionHostNode.declarations[0].id;
if (!NodeGuards.isIdentifierNode(newObjectExpressionIdentifierNode)) {
throw new Error(`\`objectExpressionHostNode\` should contain \`VariableDeclarator\` node with \`Identifier\` id property`);
}
return newObjectExpressionIdentifierNode;
}
}
@@ -1,118 +0,0 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import * as estraverse from 'estraverse';
import * as ESTree from 'estree';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
export class VariableDeclaratorPropertiesExtractor extends AbstractPropertiesExtractor {
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {VariableDeclarator} hostNode
* @returns {Node}
*/
public extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.VariableDeclarator
): ESTree.Node {
if (
!NodeGuards.isIdentifierNode(hostNode.id)
|| this.isProhibitedObjectExpressionNode(objectExpressionNode, hostNode.id)
) {
return objectExpressionNode;
}
return this.transformObjectExpressionNode(objectExpressionNode, hostNode.id);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @returns {VariableDeclarator}
*/
private getHostVariableDeclaratorNode (objectExpressionNode: ESTree.ObjectExpression): ESTree.VariableDeclarator | never {
const { parentNode } = objectExpressionNode;
if (!parentNode || !NodeGuards.isVariableDeclaratorNode(parentNode)) {
throw new Error('Cannot get `VariableDeclarator` node for `ObjectExpression` node');
}
return parentNode;
}
/**
* @param {VariableDeclarator} variableDeclaratorNode
* @returns {VariableDeclaration}
*/
private getHostVariableDeclarationNode (variableDeclaratorNode: ESTree.VariableDeclarator): ESTree.VariableDeclaration | never {
const { parentNode } = variableDeclaratorNode;
if (!parentNode || !NodeGuards.isVariableDeclarationNode(parentNode)) {
throw new Error('Cannot get `VariableDeclaration` node for `VariableDeclarator` node');
}
return parentNode;
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Identifier} memberExpressionHostNode
* @returns {boolean}
*/
private isProhibitedObjectExpressionNode (
objectExpressionNode: ESTree.ObjectExpression,
memberExpressionHostNode: ESTree.Identifier
): boolean {
const hostVariableDeclarator: ESTree.VariableDeclarator = this.getHostVariableDeclaratorNode(objectExpressionNode);
const hostVariableDeclaration: ESTree.VariableDeclaration = this.getHostVariableDeclarationNode(hostVariableDeclarator);
const { declarations } = hostVariableDeclaration;
const indexOfDeclarator: number = declarations.indexOf(hostVariableDeclarator);
const isLastDeclarator: boolean = indexOfDeclarator === (declarations.length - 1);
// avoid unnecessary checks
if (isLastDeclarator) {
return false;
}
const declaratorsAfterCurrentDeclarator: ESTree.VariableDeclarator[] = declarations.slice(indexOfDeclarator);
let isProhibitedObjectExpressionNode: boolean = false;
// should mark node as prohibited if that node using inside other variable declarators
declaratorsAfterCurrentDeclarator.forEach((variableDeclarator: ESTree.VariableDeclarator) => {
estraverse.traverse(variableDeclarator, {
enter: (node: ESTree.Node): estraverse.VisitorOption | ESTree.Node => {
if (
NodeGuards.isMemberExpressionNode(node)
&& NodeGuards.isIdentifierNode(node.object)
&& node.object.name === memberExpressionHostNode.name
) {
isProhibitedObjectExpressionNode = true;
return estraverse.VisitorOption.Break;
}
return node;
}
});
});
return isProhibitedObjectExpressionNode;
}
}
@@ -5,6 +5,7 @@ import * as ESTree from 'estree';
import { TLiteralObfuscatingReplacerFactory } from '../../types/container/node-transformers/TLiteralObfuscatingReplacerFactory';
import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
import { IOptions } from '../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
@@ -14,12 +15,19 @@ import { LiteralObfuscatingReplacer } from '../../enums/node-transformers/obfusc
import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeFactory } from '../../node/NodeFactory';
import { NodeGuards } from '../../node/NodeGuards';
import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
import { NodeMetadata } from '../../node/NodeMetadata';
import { NodeUtils } from '../../node/NodeUtils';
@injectable()
export class LiteralTransformer extends AbstractNodeTransformer {
/**
* @type {IEscapeSequenceEncoder}
*/
private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
/**
* @type {TLiteralObfuscatingReplacerFactory}
*/
@@ -35,18 +43,21 @@ export class LiteralTransformer extends AbstractNodeTransformer {
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
* @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
* @param {IEscapeSequenceEncoder} escapeSequenceEncoder
*/
constructor (
@inject(ServiceIdentifiers.Factory__IObfuscatingReplacer)
literalObfuscatingReplacerFactory: TLiteralObfuscatingReplacerFactory,
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions,
@inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer
@inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
@inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder
) {
super(randomGenerator, options);
this.literalObfuscatingReplacerFactory = literalObfuscatingReplacerFactory;
this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
this.escapeSequenceEncoder = escapeSequenceEncoder;
}
/**
@@ -68,6 +79,15 @@ export class LiteralTransformer extends AbstractNodeTransformer {
}
};
case TransformationStage.Finalizing:
return {
enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
if (parentNode && NodeGuards.isLiteralNode(node)) {
return this.encodeLiteralNodeToEscapeSequence(node, parentNode);
}
}
};
default:
return null;
}
@@ -83,7 +103,7 @@ export class LiteralTransformer extends AbstractNodeTransformer {
* @returns {NodeGuards}
*/
public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Node {
if (this.isProhibitedNode(literalNode, parentNode)) {
if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
return literalNode;
}
@@ -123,17 +143,18 @@ export class LiteralTransformer extends AbstractNodeTransformer {
/**
* @param {Literal} literalNode
* @param {Node} parentNode
* @returns {boolean}
* @returns {Literal}
*/
private isProhibitedNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): boolean {
if (NodeGuards.isPropertyNode(parentNode) && parentNode.key === literalNode) {
return true;
private encodeLiteralNodeToEscapeSequence (
literalNode: ESTree.Literal,
parentNode: ESTree.Node
): ESTree.Literal {
if (typeof literalNode.value !== 'string') {
return literalNode;
}
if (NodeGuards.isImportDeclarationNode(parentNode)) {
return true;
}
return false;
return NodeFactory.literalNode(
this.escapeSequenceEncoder.encode(literalNode.value, this.options.unicodeEscapeSequence)
);
}
}
@@ -3,7 +3,6 @@ import { ServiceIdentifiers } from '../../../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { IEscapeSequenceEncoder } from '../../../../interfaces/utils/IEscapeSequenceEncoder';
import { IInitializable } from '../../../../interfaces/IInitializable';
import { IOptions } from '../../../../interfaces/options/IOptions';
import { IStringArrayStorage } from '../../../../interfaces/storages/string-array-storage/IStringArrayStorage';
@@ -22,11 +21,6 @@ import { Utils } from '../../../../utils/Utils';
@injectable()
export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplacer implements IInitializable {
/**
* @type {IEscapeSequenceEncoder}
*/
private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
/**
* @type {Map<string, ESTree.Node>}
*/
@@ -51,20 +45,17 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
/**
* @param {IStringArrayStorage} stringArrayStorage
* @param {IStringArrayStorageAnalyzer} stringArrayStorageAnalyzer
* @param {IEscapeSequenceEncoder} escapeSequenceEncoder
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: IStringArrayStorage,
@inject(ServiceIdentifiers.IStringArrayStorageAnalyzer) stringArrayStorageAnalyzer: IStringArrayStorageAnalyzer,
@inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(options);
this.stringArrayStorage = stringArrayStorage;
this.stringArrayStorageAnalyzer = stringArrayStorageAnalyzer;
this.escapeSequenceEncoder = escapeSequenceEncoder;
}
/**
@@ -138,9 +129,7 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
* @returns {Node}
*/
private replaceWithLiteralNode (value: string): ESTree.Node {
return NodeFactory.literalNode(
this.escapeSequenceEncoder.encode(value, this.options.unicodeEscapeSequence)
);
return NodeFactory.literalNode(value);
}
/**
@@ -156,9 +145,7 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
];
if (decodeKey) {
callExpressionArgs.push(StringLiteralObfuscatingReplacer.getRc4KeyLiteralNode(
this.escapeSequenceEncoder.encode(decodeKey, this.options.unicodeEscapeSequence)
));
callExpressionArgs.push(StringLiteralObfuscatingReplacer.getRc4KeyLiteralNode(decodeKey));
}
const stringArrayIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(this.stringArrayStorageCallsWrapperName);
+17
View File
@@ -228,6 +228,23 @@ export class NodeFactory {
};
}
/**
* @param {(ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier)[]} specifiers
* @param {Literal} source
* @returns {ImportDeclaration}
*/
public static importDeclarationNode (
specifiers: (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[],
source: ESTree.Literal
): ESTree.ImportDeclaration {
return {
type: NodeType.ImportDeclaration,
specifiers,
source,
metadata: { ignoredNode: false }
};
}
/**
* @param {boolean | number | string} value
* @param {string} raw
-52
View File
@@ -80,14 +80,6 @@ export class NodeGuards {
return node.type === NodeType.CallExpression;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isCatchClauseNode (node: ESTree.Node): node is ESTree.CatchClause {
return node.type === NodeType.CatchClause;
}
/**
* @param {Node} node
* @returns {boolean}
@@ -132,42 +124,6 @@ export class NodeGuards {
&& !('directive' in node);
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isForStatementTypeNode (
node: ESTree.Node
): node is ESTree.ForStatement | ESTree.ForInStatement | ESTree.ForOfStatement {
return NodeGuards.isForStatementNode(node)
|| NodeGuards.isForInStatementNode(node)
|| NodeGuards.isForOfStatementNode(node);
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isForStatementNode (node: ESTree.Node): node is ESTree.ForStatement {
return node.type === NodeType.ForStatement;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isForInStatementNode (node: ESTree.Node): node is ESTree.ForInStatement {
return node.type === NodeType.ForInStatement;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isForOfStatementNode (node: ESTree.Node): node is ESTree.ForOfStatement {
return node.type === NodeType.ForOfStatement;
}
/**
* @param {Node} node
* @returns {boolean}
@@ -204,14 +160,6 @@ export class NodeGuards {
return node.type === NodeType.Identifier;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isIfStatementNode (node: ESTree.Node): node is ESTree.IfStatement {
return node.type === NodeType.IfStatement;
}
/**
* @param {Node} node
* @returns {boolean}
+22
View File
@@ -0,0 +1,22 @@
import * as ESTree from 'estree';
import { NodeGuards } from './NodeGuards';
export class NodeLiteralUtils {
/**
* @param {Literal} literalNode
* @param {Node} parentNode
* @returns {boolean}
*/
public static isProhibitedLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): boolean {
if (NodeGuards.isPropertyNode(parentNode) && !parentNode.computed && parentNode.key === literalNode) {
return true;
}
if (NodeGuards.isImportDeclarationNode(parentNode)) {
return true;
}
return false;
}
}
@@ -218,6 +218,7 @@ export class StringArrayStorage extends MapStorage <string, IStringArrayStorageI
return Array
.from(this.storage.values())
.map((stringArrayStorageItemData: IStringArrayStorageItemData) => {
// we have to encode here, because of possible errors during `parse` of StringArrayCustomNode
return `'${this.escapeSequenceEncoder.encode(
stringArrayStorageItemData.encodedValue,
this.options.unicodeEscapeSequence
@@ -0,0 +1,6 @@
import { IObjectExpressionExtractor } from '../../../interfaces/node-transformers/converting-transformers/object-expression-extractors/IObjectExpressionExtractor';
import { ObjectExpressionExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor';
export type TObjectExpressionExtractorFactory =
(objectExpressionExtractorName: ObjectExpressionExtractor) => IObjectExpressionExtractor;
@@ -1,5 +0,0 @@
import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { PropertiesExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
export type TPropertiesExtractorFactory = (propertiesExtractorName: PropertiesExtractor) => IPropertiesExtractor;
+5 -6
View File
@@ -7,15 +7,14 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
const foo = \`\${{
foo: 'bar',
baz: 'bark'
}.baz}\`;
console.log(foo);
var foo = {['foo']: 1};
`,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
compact: false,
unicodeEscapeSequence: true,
stringArray: true,
stringArrayThreshold: 1
}
).getObfuscatedCode();
@@ -39,10 +39,11 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #2: variable declaration without initialization', () => {
const match: string = `` +
`var *${variableMatch};` +
`${variableMatch} *= *{};` +
`var ${variableMatch};` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`${variableMatch}\\['baz'] *= *'bark';` +
`${variableMatch} *= *${variableMatch};` +
``;
const regExp: RegExp = new RegExp(match);
@@ -151,11 +152,14 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #6: nested objects #1', () => {
const match: string = `` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['baz'] *= *'bark';` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['inner1'] *= *${variableMatch};` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`${variableMatch}\\['inner'] *= *{};` +
`${variableMatch}\\['inner']\\['inner1'] *= *{};` +
`${variableMatch}\\['inner']\\['inner1']\\['baz'] *= *'bark';` +
`${variableMatch}\\['inner'] *= *${variableMatch};` +
`var object *= *${variableMatch};` +
``;
const regExp: RegExp = new RegExp(match);
@@ -180,14 +184,17 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #7: nested objects #2', () => {
const match: string = `` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['hawk'] *= *'geek';` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['baz'] *= *'bark';` +
`${variableMatch}\\['inner1'] *= *${variableMatch};` +
`${variableMatch}\\['cow'] *= *'bear';` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`${variableMatch}\\['inner'] *= *{};` +
`${variableMatch}\\['inner'] *= *${variableMatch};` +
`${variableMatch}\\['ball'] *= *'door';` +
`${variableMatch}\\['inner']\\['baz'] *= *'bark';` +
`${variableMatch}\\['inner']\\['inner1'] *= *{};` +
`${variableMatch}\\['inner']\\['cow'] *= *'bear';` +
`${variableMatch}\\['inner']\\['inner1']\\['hawk'] *= *'geek';` +
`var *object *= *${variableMatch};` +
``;
const regExp: RegExp = new RegExp(match);
@@ -251,6 +258,7 @@ describe('ObjectExpressionKeysTransformer', () => {
`${variableMatch}\\['\\w{5}'] *= *function *\\(${variableMatch}, *${variableMatch}\\) *{` +
`return *${variableMatch} *\\+ *${variableMatch};` +
`};` +
`var *${variableMatch} *= *${variableMatch};` +
`var *${variableMatch} *= *${variableMatch}\\['\\w{5}']\\(0x1, *0x2\\);` +
``;
const regExp: RegExp = new RegExp(match);
@@ -282,6 +290,7 @@ describe('ObjectExpressionKeysTransformer', () => {
`${variableMatch}\\['\\w{5}'] *= *function *\\(${variableMatch}, *${variableMatch}\\) *{` +
`return *${variableMatch} *\\+ *${variableMatch};` +
`};` +
`var *${variableMatch} *= *${variableMatch};` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *0x1;` +
`var *${variableMatch} *= *{};` +
@@ -367,6 +376,31 @@ describe('ObjectExpressionKeysTransformer', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #3', () => {
const match: string = `` +
`var *${variableMatch} *= *0x1, *${variableMatch} *= *{'foo' *: *${variableMatch}};` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-with-object-call-6.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('should transform object keys', () => {
assert.match(obfuscatedCode, regExp);
});
});
});
describe('Variant #12: assignment expression and member expression', () => {
@@ -452,15 +486,164 @@ describe('ObjectExpressionKeysTransformer', () => {
assert.match(obfuscatedCode, regExp);
});
});
// issue https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
describe('Variant #15: function default values', () => {
const match: string = `` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['value'] *= *0x1;` +
`function test *\\(${variableMatch} *= *0x1, *${variableMatch} *= *${variableMatch}\\) *{ *}` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/function-default-values.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('shouldn ignore default parameter object if it references other parameter', () => {
assert.match(obfuscatedCode, regExp);
});
});
// issue https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
describe('Variant #16: object expression inside inside variable declaration', () => {
describe('Without reference on other property', () => {
describe('Variant #1: Single variable declarator and object expression parent node is expression node', () => {
const match: string = `` +
`var *passthrough *= *${variableMatch} *=> *${variableMatch};` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *0x1;` +
`var foo *= *passthrough *\\(${variableMatch}\\);` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('should transform object expression keys', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #2: Multiple variable declarators and object expression parent node is variable declarator node', () => {
const match: string = `` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *0x1;` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['bar'] *= *0x2;` +
`var foo *= *${variableMatch}, *bar *= *${variableMatch};` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-2.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('should transform object expressions keys', () => {
assert.match(obfuscatedCode, regExp);
});
});
});
describe('With reference on other property', () => {
describe('Variant #1: Object expression parent node is variable declarator node', () => {
const match: string = `` +
`var *passthrough *= *${variableMatch} *=> *${variableMatch};` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *0x1;` +
`var foo *= *${variableMatch}, *bar *= *{'bar': *foo\\['foo']};` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-3.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('should transform first object expression keys and ignore second object expression keys', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #2: Object expression parent node is any expression node', () => {
const match: string = `` +
`var *passthrough *= *${variableMatch} *=> *${variableMatch};` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *0x1;` +
`var foo *= *${variableMatch}, *bar *= *passthrough *\\({ *'bar' *: *foo\\['foo'] *}\\);` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/object-expression-inside-variable-declaration-4.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('should transform first object expression keys and ignore second object expression keys', () => {
assert.match(obfuscatedCode, regExp);
});
});
});
});
});
describe('member expression as host of object expression', () => {
describe('Variant #1: simple', () => {
const match: string = `` +
`this\\['state'] *= *{};` +
`this\\['state']\\['foo'] *= *'bar';` +
`this\\['state']\\['baz'] *= *'bark';` +
``;
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`${variableMatch}\\['baz'] *= *'bark';` +
`this\\['state'] *= *${variableMatch};` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
@@ -484,10 +667,11 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #2: long members chain', () => {
const match: string = `` +
`this\\['state']\\['foo'] *= *{};` +
`this\\['state']\\['foo']\\['foo'] *= *'bar';` +
`this\\['state']\\['foo']\\['baz'] *= *'bark';` +
``;
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`${variableMatch}\\['baz'] *= *'bark';` +
`this\\['state']\\['foo'] *= *${variableMatch};` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
@@ -515,10 +699,11 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #1: with block statement', () => {
const match: string = `` +
`if *\\(!!\\[]\\) *{` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`var ${variableMatch} *= *${variableMatch};` +
`}` +
``;
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
@@ -606,8 +791,9 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #1: with block statement', () => {
const match: string = `` +
`for *\\(var *${variableMatch} *= *0x0; *${variableMatch} *< *0xa; *${variableMatch}\\+\\+\\) *{` +
`var *${variableMatch} *= *{};` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`var ${variableMatch} *= *${variableMatch};` +
`}` +
``;
const regExp: RegExp = new RegExp(match);
@@ -666,8 +852,9 @@ describe('ObjectExpressionKeysTransformer', () => {
const match: string = `` +
`var ${variableMatch} *= *{};` +
`for *\\(var *${variableMatch} in *${variableMatch}\\) *{` +
`${variableMatch} *= *{};` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['bar'] *= *'bar';` +
`${variableMatch} *= *${variableMatch};` +
`}` +
``;
const regExp: RegExp = new RegExp(match);
@@ -726,8 +913,9 @@ describe('ObjectExpressionKeysTransformer', () => {
const match: string = `` +
`var ${variableMatch} *= *\\[];` +
`for *\\(var *${variableMatch} of *${variableMatch}\\) *{` +
`${variableMatch} *= *{};` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['bar'] *= *'bar';` +
`${variableMatch} *= *${variableMatch};` +
`}` +
``;
const regExp: RegExp = new RegExp(match);
@@ -787,6 +975,7 @@ describe('ObjectExpressionKeysTransformer', () => {
`while *\\(!!\\[]\\) *{` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`var ${variableMatch} *= *${variableMatch};` +
`}` +
``;
const regExp: RegExp = new RegExp(match);
@@ -873,6 +1062,7 @@ describe('ObjectExpressionKeysTransformer', () => {
`try *{` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`var ${variableMatch} *= *${variableMatch};` +
`} *catch *\\(${variableMatch}\\) *{` +
`}` +
``;
@@ -903,6 +1093,7 @@ describe('ObjectExpressionKeysTransformer', () => {
`} *catch *\\(${variableMatch}\\) *{` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`var ${variableMatch} *= *${variableMatch};` +
`}` +
``;
const regExp: RegExp = new RegExp(match);
@@ -932,6 +1123,7 @@ describe('ObjectExpressionKeysTransformer', () => {
`case *!!\\[]:` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'bar';` +
`var ${variableMatch} *= *${variableMatch};` +
`}` +
``;
const regExp: RegExp = new RegExp(match);
@@ -960,6 +1152,7 @@ describe('ObjectExpressionKeysTransformer', () => {
const match: string = `` +
`const *${variableMatch} *= *{}; *` +
`${variableMatch}\\['foo'] *= *'foo'; *` +
`const ${variableMatch} *= *${variableMatch};` +
`const *${variableMatch} *= *${variableMatch}\\['foo'];` +
``;
const regExp: RegExp = new RegExp(match);
@@ -985,10 +1178,11 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #2', () => {
const match: string = `` +
`const *${variableMatch} *= *0x1, *` +
`${variableMatch} *= *{}; *` +
`const ${variableMatch} *= *{}; *` +
`${variableMatch}\\['foo'] *= *'foo'; *` +
`const *${variableMatch} *= *${variableMatch}\\['foo'];` +
`const ${variableMatch} *= *0x1, *` +
`${variableMatch} *= *${variableMatch}; *` +
`const ${variableMatch} *= *${variableMatch}\\['foo'];` +
``;
const regExp: RegExp = new RegExp(match);
@@ -1013,12 +1207,13 @@ describe('ObjectExpressionKeysTransformer', () => {
describe('Variant #3: two objects', () => {
const match: string = `` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'foo';` +
`var *${variableMatch} *= *{};` +
`${variableMatch}\\['bar'] *= *'bar';` +
`var *${variableMatch} *= *{}, *` +
`var *${variableMatch} *= *${variableMatch}, *` +
`${variableMatch} *= *${variableMatch}, *` +
`${variableMatch} *= *${variableMatch}\\['bar']; *` +
`${variableMatch}\\['foo'] *= *'foo';` +
`console\\['log']\\(${variableMatch}\\);` +
``;
const regExp: RegExp = new RegExp(match);
@@ -1176,5 +1371,60 @@ describe('ObjectExpressionKeysTransformer', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #3: function default value reference', () => {
const match: string = `` +
`function test *\\(${variableMatch} *= *0x1, *${variableMatch} *= *{'value' *: *${variableMatch}}\\) *{ *}` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/function-default-value-reference.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('shouldn ignore default parameter object if it references other parameter', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #4: member expression node as property key', () => {
const match: string = `` +
`var ${variableMatch} *= *{};` +
`${variableMatch}\\['foo'] *= *'test';` +
`var foo *= *${variableMatch};` +
`var ${variableMatch} *= *{\\[foo\\['foo']] *: *'1'};` +
`${variableMatch}\\['bar'] *= *'2';` +
`var bar *= *${variableMatch};` +
``;
const regExp: RegExp = new RegExp(match);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/computed-key-member-expression.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('shouldn ignore extraction of property with member expression key', () => {
assert.match(obfuscatedCode, regExp);
});
});
});
});
@@ -0,0 +1,7 @@
var foo = {
foo: 'test'
};
var bar = {
[foo.foo]: '1',
bar: '2'
};
@@ -0,0 +1 @@
function test (param1 = 1, param2 = {value: param1}) {}
@@ -0,0 +1 @@
function test (param1 = 1, param2 = {value: 1}) {}
@@ -1,10 +1,8 @@
(function(){
var object = {
foo: 'bar',
inner: {
inner1: {
baz: 'bark'
}
var object = {
foo: 'bar',
inner: {
inner1: {
baz: 'bark'
}
};
})();
}
};
@@ -1,14 +1,12 @@
(function(){
var object = {
foo: 'bar',
inner: {
baz: 'bark',
inner1: {
hawk: 'geek'
},
cow: 'bear',
var object = {
foo: 'bar',
inner: {
baz: 'bark',
inner1: {
hawk: 'geek'
},
ball: 'door',
};
})();
cow: 'bear',
},
ball: 'door',
};
@@ -0,0 +1,2 @@
var passthrough = (object) => object;
var foo = passthrough({foo: 1});
@@ -0,0 +1,3 @@
var passthrough = (object) => object;
var foo = {foo: 1},
bar = {bar: foo.foo};
@@ -0,0 +1,3 @@
var passthrough = (object) => object;
var foo = {foo: 1},
bar = passthrough({bar: foo.foo});
@@ -0,0 +1,4 @@
(function () {
var object = 1,
variable = {foo: object};
})();
@@ -165,6 +165,11 @@ describe('SplitStringTransformer', () => {
describe('Variant #10: Integration with `transformObjectKeys` option', () => {
it('should correctly transform string when `transformObjectKeys` option is enabled', () => {
const regExp: RegExp = new RegExp(`` +
`var _0x[a-f0-9]{4,6} *= *{};` +
`*_0x[a-f0-9]{4,6}\\['ab' *\\+ *'cd' *\\+ *'ef' *\\+ *'g'] *= *'ab' *\\+ *'cd' *\\+ *'ef' *\\+ *'g';` +
`var test *= *_0x[a-f0-9]{4,6};` +
``);
const code: string = readFileAsString(__dirname + '/fixtures/object-string-literal.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
@@ -179,7 +184,7 @@ describe('SplitStringTransformer', () => {
assert.match(
obfuscatedCode,
/^var *test *= *{}; *test\['ab' *\+ *'cd' *\+ *'ef' *\+ *'g'] *= *'ab' *\+ *'cd' *\+ *'ef' *\+ *'g';$/
regExp
);
});
});
@@ -135,7 +135,7 @@ describe('LiteralTransformer', () => {
describe('Variant #6: `unicodeEscapeSequence` and `stringArray` options are enabled', () => {
const stringArrayRegExp: RegExp = /^var *_0x([a-f0-9]){4} *= *\['\\x74\\x65\\x73\\x74'\];/;
const stringArrayCallRegExp: RegExp = /var *test *= *_0x([a-f0-9]){4}\('0x0'\);/;
const stringArrayCallRegExp: RegExp = /var *test *= *_0x([a-f0-9]){4}\('\\x30\\x78\\x30'\);/;
let obfuscatedCode: string;
@@ -157,7 +157,7 @@ describe('LiteralTransformer', () => {
assert.match(obfuscatedCode, stringArrayRegExp);
});
it('match #1: should replace literal node value with unicode escape sequence from string array', () => {
it('match #2: should replace literal node value with unicode escape sequence from string array', () => {
assert.match(obfuscatedCode, stringArrayCallRegExp);
});
});
@@ -518,6 +518,64 @@ describe('LiteralTransformer', () => {
});
});
});
describe('Variant #13: 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'\)};/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/object-expression-key-literal.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1
}
).getObfuscatedCode();
});
it('match #1: should not add object expression key literal to the string array', () => {
assert.match(obfuscatedCode, stringArrayRegExp);
});
it('match #2: should keep object expression key literal', () => {
assert.match(obfuscatedCode, objectExpressionRegExp);
});
});
describe('Variant #2: computed key literal', () => {
const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['foo', *'bar'];/;
const objectExpressionRegExp: RegExp = /var test *= *{\[_0x([a-f0-9]){4}\('0x0'\)] *: *_0x([a-f0-9]){4}\('0x1'\)};/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/object-expression-computed-key-literal.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1
}
).getObfuscatedCode();
});
it('match #1: should add object expression computed key literal to the string array', () => {
assert.match(obfuscatedCode, stringArrayRegExp);
});
it('match #2: should replace object expression computed key literal on call to the string array', () => {
assert.match(obfuscatedCode, objectExpressionRegExp);
});
});
});
});
describe('transformation of literal node with boolean value', () => {
+1
View File
@@ -28,6 +28,7 @@ import './unit-tests/node/node-appender/NodeAppender.spec';
import './unit-tests/node/node-guards/NodeGuards.spec';
import './unit-tests/node/node-metadata/NodeMetadata.spec';
import './unit-tests/node/node-lexical-scope-utils/NodeLexicalScopeUtils.spec';
import './unit-tests/node/node-literal-utils/NodeLiteralUtils.spec';
import './unit-tests/node/node-statement-utils/NodeStatementUtils.spec';
import './unit-tests/node/node-utils/NodeUtils.spec';
import './unit-tests/node-transformers/preparing-transformers/ObfuscatingGuardsTransformer.spec';
@@ -157,6 +157,55 @@ describe('StringArrayStorageAnalyzer', () => {
});
});
describe('Analyzes of the AST tree with prohibited string literal nodes', () => {
const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');
const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
encodedValue: 'foo',
decodeKey: null,
index: 0,
value: 'foo'
};
const expectedStringArrayStorageItemData2: undefined = undefined;
let stringArrayStorageItemData1: IStringArrayStorageItemData | undefined;
let stringArrayStorageItemData2: IStringArrayStorageItemData | undefined;
before(() => {
stringArrayStorageAnalyzer = getStringArrayStorageAnalyzer({
stringArrayThreshold: 1
});
const astTree: ESTree.Program = NodeFactory.programNode([
NodeFactory.expressionStatementNode(literalNode1),
NodeFactory.variableDeclarationNode([
NodeFactory.variableDeclaratorNode(
NodeFactory.identifierNode('bar'),
NodeFactory.objectExpressionNode([
NodeFactory.propertyNode(
literalNode2,
NodeFactory.literalNode(1)
)
])
)
])
]);
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('Analyzes of the AST tree with ignored nodes', () => {
const literalNode1: ESTree.Literal = NodeFactory.literalNode('foo');
const literalNode2: ESTree.Literal = NodeFactory.literalNode('bar');
@@ -0,0 +1,136 @@
import * as ESTree from 'estree';
import { assert } from 'chai';
import { NodeFactory } from '../../../../src/node/NodeFactory';
import { NodeLiteralUtils } from '../../../../src/node/NodeLiteralUtils';
describe('NodeLiteralUtils', () => {
describe('isProhibitedLiteralNode', () => {
describe('String literal node', () => {
describe('Variant #1: base string literal node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
let statementNode: ESTree.Statement;
before(() => {
statementNode = NodeFactory.expressionStatementNode(
literalNode
);
literalNode.parentNode = statementNode;
});
it('should return false for base string literal node', () => {
assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, statementNode), false);
});
});
describe('Variant #2: property literal node', () => {
describe('Variant #1: property key literal node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
let propertyNode: ESTree.Property;
before(() => {
propertyNode = NodeFactory.propertyNode(
literalNode,
NodeFactory.literalNode(1)
);
literalNode.parentNode = propertyNode;
});
it('should return false for property key literal node', () => {
assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), true);
});
});
describe('Variant #2: computed property key literal node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
let propertyNode: ESTree.Property;
before(() => {
propertyNode = NodeFactory.propertyNode(
literalNode,
NodeFactory.literalNode(1),
true
);
literalNode.parentNode = propertyNode;
});
it('should return false for computed property key literal node', () => {
assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), false);
});
});
describe('Variant #3: property value literal node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
let propertyNode: ESTree.Property;
before(() => {
propertyNode = NodeFactory.propertyNode(
NodeFactory.literalNode(1),
literalNode
);
literalNode.parentNode = propertyNode;
});
it('should return false for property value literal node', () => {
assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, propertyNode), false);
});
});
});
describe('Variant #3: import declaration node', () => {
describe('Variant #1: base import declaration literal node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode('foo');
let importDeclarationNode: ESTree.ImportDeclaration;
before(() => {
importDeclarationNode = NodeFactory.importDeclarationNode(
[],
literalNode
);
literalNode.parentNode = importDeclarationNode;
});
it('should return false for import declaration literal node', () => {
assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, importDeclarationNode), true);
});
});
});
});
describe('Number literal node', () => {
describe('Variant #1: base number literal node', () => {
const literalNode: ESTree.Literal = NodeFactory.literalNode(1);
let statementNode: ESTree.Statement;
before(() => {
statementNode = NodeFactory.expressionStatementNode(
literalNode
);
const blockStatementNode: ESTree.BlockStatement = NodeFactory.blockStatementNode([
statementNode
]);
statementNode.parentNode = blockStatementNode;
literalNode.parentNode = statementNode;
});
it('should return false for base number literal node', () => {
assert.equal(NodeLiteralUtils.isProhibitedLiteralNode(literalNode, statementNode), false);
});
});
});
});
});