mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
More stable way to preserve identifiers with scope identifiers traverser
This commit is contained in:
Vendored
+5
-5
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+11
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script src="test-obfuscated.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,6 +7,7 @@ import { convertingTransformersModule } from './modules/node-transformers/Conver
|
||||
import { customNodesModule } from './modules/custom-nodes/CustomNodesModule';
|
||||
import { finalizingTransformersModule } from './modules/node-transformers/FinalizingTransformersModule';
|
||||
import { generatorsModule } from './modules/generators/GeneratorsModule';
|
||||
import { nodeModule } from './modules/node/NodeModule';
|
||||
import { nodeTransformersModule } from './modules/node-transformers/NodeTransformersModule';
|
||||
import { obfuscatingTransformersModule } from './modules/node-transformers/ObfuscatingTransformersModule';
|
||||
import { optionsModule } from './modules/options/OptionsModule';
|
||||
@@ -196,6 +197,7 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
|
||||
this.container.load(customNodesModule);
|
||||
this.container.load(finalizingTransformersModule);
|
||||
this.container.load(generatorsModule);
|
||||
this.container.load(nodeModule);
|
||||
this.container.load(nodeTransformersModule);
|
||||
this.container.load(obfuscatingTransformersModule);
|
||||
this.container.load(optionsModule);
|
||||
|
||||
@@ -39,6 +39,7 @@ export enum ServiceIdentifiers {
|
||||
IPrevailingKindOfVariablesAnalyzer = 'IPrevailingKindOfVariablesAnalyzer',
|
||||
IObjectExpressionExtractor = 'IObjectExpressionExtractor',
|
||||
IRandomGenerator = 'IRandomGenerator',
|
||||
IScopeIdentifiersTraverser = 'IScopeIdentifiersTraverser',
|
||||
ISourceCode = 'ISourceCode',
|
||||
ISourceMapCorrector = 'ISourceMapCorrector',
|
||||
IScopeAnalyzer = 'IScopeAnalyzer',
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ContainerModule, interfaces } from 'inversify';
|
||||
import { ServiceIdentifiers } from '../../ServiceIdentifiers';
|
||||
|
||||
import { IScopeIdentifiersTraverser } from '../../../interfaces/node/IScopeIdentifiersTraverser';
|
||||
|
||||
import { ScopeIdentifiersTraverser } from '../../../node/ScopeIdentifiersTraverser';
|
||||
|
||||
export const nodeModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
// scope identifiers traverser
|
||||
bind<IScopeIdentifiersTraverser>(ServiceIdentifiers.IScopeIdentifiersTraverser)
|
||||
.to(ScopeIdentifiersTraverser)
|
||||
.inSingletonScope();
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { TScopeIdentifiersTraverserCallback } from '../../types/node/TScopeIdentifiersTraverserCallback';
|
||||
|
||||
export interface IScopeIdentifiersTraverser {
|
||||
/**
|
||||
* @param {Program} programNode
|
||||
* @param {Node | null} parentNode
|
||||
* @param {TScopeIdentifiersTraverserCallback} callback
|
||||
*/
|
||||
traverse (
|
||||
programNode: ESTree.Program,
|
||||
parentNode: ESTree.Node | null,
|
||||
callback: TScopeIdentifiersTraverserCallback
|
||||
): void;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as eslintScope from 'eslint-scope';
|
||||
|
||||
import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
|
||||
|
||||
export interface IScopeIdentifiersTraverserCallbackData {
|
||||
isGlobalDeclaration: boolean;
|
||||
rootScope: eslintScope.Scope;
|
||||
variable: eslintScope.Variable;
|
||||
variableLexicalScopeNode: TNodeWithLexicalScope;
|
||||
variableScope: eslintScope.Scope;
|
||||
}
|
||||
@@ -11,7 +11,8 @@ import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
|
||||
import { IIdentifierObfuscatingReplacer } from '../../interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IIdentifierObfuscatingReplacer';
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
||||
import { IScopeAnalyzer } from '../../interfaces/analyzers/scope-analyzer/IScopeAnalyzer';
|
||||
import { IScopeIdentifiersTraverser } from '../../interfaces/node/IScopeIdentifiersTraverser';
|
||||
import { IScopeIdentifiersTraverserCallbackData } from '../../interfaces/node/IScopeIdentifiersTraverserCallbackData';
|
||||
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
||||
|
||||
import { IdentifierObfuscatingReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer';
|
||||
@@ -26,19 +27,6 @@ import { NodeMetadata } from '../../node/NodeMetadata';
|
||||
*/
|
||||
@injectable()
|
||||
export class ScopeIdentifiersTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
private static readonly argumentsVariableName: string = 'arguments';
|
||||
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
private static readonly globalScopeNames: string[] = [
|
||||
'global',
|
||||
'module'
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {IIdentifierObfuscatingReplacer}
|
||||
*/
|
||||
@@ -50,29 +38,29 @@ export class ScopeIdentifiersTransformer extends AbstractNodeTransformer {
|
||||
private readonly lexicalScopesWithObjectPatternWithoutDeclarationMap: Map<TNodeWithLexicalScope, boolean> = new Map();
|
||||
|
||||
/**
|
||||
* @type {IScopeAnalyzer}
|
||||
* @type {IScopeIdentifiersTraverser}
|
||||
*/
|
||||
private readonly scopeAnalyzer: IScopeAnalyzer;
|
||||
private readonly scopeIdentifiersTraverser: IScopeIdentifiersTraverser;
|
||||
|
||||
/**
|
||||
* @param {TIdentifierObfuscatingReplacerFactory} identifierObfuscatingReplacerFactory
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
* @param {IScopeAnalyzer} scopeAnalyzer
|
||||
* @param {IScopeIdentifiersTraverser} scopeIdentifiersTraverser
|
||||
*/
|
||||
public constructor (
|
||||
@inject(ServiceIdentifiers.Factory__IIdentifierObfuscatingReplacer)
|
||||
identifierObfuscatingReplacerFactory: TIdentifierObfuscatingReplacerFactory,
|
||||
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions,
|
||||
@inject(ServiceIdentifiers.IScopeAnalyzer) scopeAnalyzer: IScopeAnalyzer
|
||||
@inject(ServiceIdentifiers.IScopeIdentifiersTraverser) scopeIdentifiersTraverser: IScopeIdentifiersTraverser
|
||||
) {
|
||||
super(randomGenerator, options);
|
||||
|
||||
this.identifierObfuscatingReplacer = identifierObfuscatingReplacerFactory(
|
||||
IdentifierObfuscatingReplacer.BaseIdentifierObfuscatingReplacer
|
||||
);
|
||||
this.scopeAnalyzer = scopeAnalyzer;
|
||||
this.scopeIdentifiersTraverser = scopeIdentifiersTraverser;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,8 +73,6 @@ export class ScopeIdentifiersTransformer extends AbstractNodeTransformer {
|
||||
return {
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
|
||||
if (parentNode && NodeGuards.isProgramNode(node)) {
|
||||
this.analyzeNode(node, parentNode);
|
||||
|
||||
return this.transformNode(node, parentNode);
|
||||
}
|
||||
}
|
||||
@@ -97,68 +83,46 @@ export class ScopeIdentifiersTransformer extends AbstractNodeTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Program} programNode
|
||||
* @param {Node | null} parentNode
|
||||
* @returns {Node}
|
||||
*/
|
||||
public analyzeNode (programNode: ESTree.Program, parentNode: ESTree.Node | null): void {
|
||||
this.scopeAnalyzer.analyze(programNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VariableDeclaration} programNode
|
||||
* @param {NodeGuards} parentNode
|
||||
* @returns {NodeGuards}
|
||||
*/
|
||||
public transformNode (programNode: ESTree.Program, parentNode: ESTree.Node): ESTree.Node {
|
||||
const globalScope: eslintScope.Scope = this.scopeAnalyzer.acquireScope(programNode);
|
||||
this.scopeIdentifiersTraverser.traverse(
|
||||
programNode,
|
||||
parentNode,
|
||||
(data: IScopeIdentifiersTraverserCallbackData) => {
|
||||
const {
|
||||
isGlobalDeclaration,
|
||||
variable,
|
||||
variableLexicalScopeNode
|
||||
} = data;
|
||||
|
||||
this.traverseScopeVariables(globalScope);
|
||||
if (!this.options.renameGlobals && isGlobalDeclaration) {
|
||||
const isImportBindingOrCatchClauseIdentifier: boolean = variable.defs
|
||||
.every((definition: eslintScope.Definition) =>
|
||||
definition.type === 'ImportBinding'
|
||||
|| definition.type === 'CatchClause'
|
||||
);
|
||||
|
||||
// skip all global identifiers except import statement and catch clause parameter identifiers
|
||||
if (!isImportBindingOrCatchClauseIdentifier) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.transformScopeVariableIdentifiers(
|
||||
variable,
|
||||
variableLexicalScopeNode,
|
||||
isGlobalDeclaration
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return programNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Scope} scope
|
||||
*/
|
||||
private traverseScopeVariables (scope: eslintScope.Scope): void {
|
||||
const lexicalScope: eslintScope.Scope = scope.variableScope;
|
||||
const nodeWithLexicalScope: TNodeWithLexicalScope | null = NodeGuards.isNodeWithBlockLexicalScope(lexicalScope.block)
|
||||
? lexicalScope.block
|
||||
: null;
|
||||
const isGlobalDeclaration: boolean = ScopeIdentifiersTransformer.globalScopeNames.includes(lexicalScope.type);
|
||||
|
||||
if (!nodeWithLexicalScope) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const variable of scope.variables) {
|
||||
if (variable.name === ScopeIdentifiersTransformer.argumentsVariableName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.options.renameGlobals && isGlobalDeclaration) {
|
||||
const isImportBindingOrCatchClauseIdentifier: boolean = variable.defs
|
||||
.every((definition: eslintScope.Definition) =>
|
||||
definition.type === 'ImportBinding'
|
||||
|| definition.type === 'CatchClause'
|
||||
);
|
||||
|
||||
// skip all global identifiers except import statement and catch clause parameter identifiers
|
||||
if (!isImportBindingOrCatchClauseIdentifier) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this.transformScopeVariableIdentifiers(variable, nodeWithLexicalScope, isGlobalDeclaration);
|
||||
}
|
||||
|
||||
for (const childScope of scope.childScopes) {
|
||||
this.traverseScopeVariables(childScope);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Variable} variable
|
||||
* @param {TNodeWithLexicalScope} lexicalScopeNode
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { inject, injectable, } from 'inversify';
|
||||
import * as ESTree from 'estree';
|
||||
import * as eslintScope from 'eslint-scope';
|
||||
|
||||
import { TIdentifierObfuscatingReplacerFactory } from '../../types/container/node-transformers/TIdentifierObfuscatingReplacerFactory';
|
||||
import { TNodeWithLexicalScope } from '../../types/node/TNodeWithLexicalScope';
|
||||
@@ -8,12 +9,14 @@ import { IIdentifierObfuscatingReplacer } from '../../interfaces/node-transforme
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
||||
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
||||
import { IdentifierObfuscatingReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer';
|
||||
import { IScopeIdentifiersTraverser } from '../../interfaces/node/IScopeIdentifiersTraverser';
|
||||
import { IScopeIdentifiersTraverserCallbackData } from '../../interfaces/node/IScopeIdentifiersTraverserCallbackData';
|
||||
|
||||
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
|
||||
import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
|
||||
|
||||
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
||||
import { IdentifierObfuscatingReplacer } from '../../enums/node-transformers/obfuscating-transformers/obfuscating-replacers/IdentifierObfuscatingReplacer';
|
||||
import { NodeGuards } from '../../node/NodeGuards';
|
||||
|
||||
/**
|
||||
@@ -21,32 +24,35 @@ import { NodeGuards } from '../../node/NodeGuards';
|
||||
*/
|
||||
@injectable()
|
||||
export class VariablePreserveTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {TNodeWithLexicalScope[]}
|
||||
*/
|
||||
private readonly enteredLexicalScopesStack: TNodeWithLexicalScope[] = [];
|
||||
|
||||
/**
|
||||
* @type {IIdentifierObfuscatingReplacer}
|
||||
*/
|
||||
private readonly identifierObfuscatingReplacer: IIdentifierObfuscatingReplacer;
|
||||
|
||||
/**
|
||||
* @type {IScopeIdentifiersTraverser}
|
||||
*/
|
||||
private readonly scopeIdentifiersTraverser: IScopeIdentifiersTraverser;
|
||||
|
||||
/**
|
||||
* @param {TIdentifierObfuscatingReplacerFactory} identifierObfuscatingReplacerFactory
|
||||
* @param {IRandomGenerator} randomGenerator
|
||||
* @param {IOptions} options
|
||||
* @param {IScopeIdentifiersTraverser} scopeIdentifiersTraverser
|
||||
*/
|
||||
public constructor (
|
||||
@inject(ServiceIdentifiers.Factory__IIdentifierObfuscatingReplacer)
|
||||
identifierObfuscatingReplacerFactory: TIdentifierObfuscatingReplacerFactory,
|
||||
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions,
|
||||
@inject(ServiceIdentifiers.IScopeIdentifiersTraverser) scopeIdentifiersTraverser: IScopeIdentifiersTraverser
|
||||
) {
|
||||
super(randomGenerator, options);
|
||||
|
||||
this.identifierObfuscatingReplacer = identifierObfuscatingReplacerFactory(
|
||||
IdentifierObfuscatingReplacer.BaseIdentifierObfuscatingReplacer
|
||||
);
|
||||
this.scopeIdentifiersTraverser = scopeIdentifiersTraverser;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,29 +64,9 @@ export class VariablePreserveTransformer extends AbstractNodeTransformer {
|
||||
case TransformationStage.Preparing:
|
||||
return {
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
|
||||
if (NodeGuards.isNodeWithLexicalScope(node)) {
|
||||
this.addLexicalScopeToEnteredLexicalScopesStack(node);
|
||||
}
|
||||
|
||||
if (
|
||||
NodeGuards.isIdentifierNode(node)
|
||||
&& parentNode
|
||||
) {
|
||||
const isOnTheRootLexicalScope: boolean = this.enteredLexicalScopesStack.length === 1;
|
||||
|
||||
if (isOnTheRootLexicalScope) {
|
||||
this.preserveIdentifierNameForRootLexicalScope(node, parentNode);
|
||||
} else {
|
||||
this.preserveIdentifierNameForLexicalScope(node, parentNode);
|
||||
}
|
||||
|
||||
if (parentNode && NodeGuards.isProgramNode(node)) {
|
||||
return this.transformNode(node, parentNode);
|
||||
}
|
||||
},
|
||||
leave: (node: ESTree.Node): void => {
|
||||
if (NodeGuards.isNodeWithLexicalScope(node)) {
|
||||
this.removeLexicalScopeFromEnteredLexicalScopesStack(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,49 +76,74 @@ export class VariablePreserveTransformer extends AbstractNodeTransformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Identifier} identifierNode
|
||||
* @param {Node} parentNode
|
||||
* @returns {Node}
|
||||
* @param {VariableDeclaration} programNode
|
||||
* @param {NodeGuards} parentNode
|
||||
* @returns {NodeGuards}
|
||||
*/
|
||||
public transformNode (identifierNode: ESTree.Identifier, parentNode: ESTree.Node): ESTree.Node {
|
||||
return identifierNode;
|
||||
public transformNode (programNode: ESTree.Program, parentNode: ESTree.Node): ESTree.Node {
|
||||
this.scopeIdentifiersTraverser.traverse(
|
||||
programNode,
|
||||
parentNode,
|
||||
(data: IScopeIdentifiersTraverserCallbackData) => {
|
||||
const {
|
||||
isGlobalDeclaration,
|
||||
variable,
|
||||
variableScope
|
||||
} = data;
|
||||
|
||||
this.preserveScopeVariableIdentifiers(
|
||||
variable,
|
||||
variableScope,
|
||||
isGlobalDeclaration
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return programNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Variable} variable
|
||||
* @param {Scope} variableScope
|
||||
* @param {boolean} isGlobalDeclaration
|
||||
*/
|
||||
private preserveScopeVariableIdentifiers (
|
||||
variable: eslintScope.Variable,
|
||||
variableScope: eslintScope.Scope,
|
||||
isGlobalDeclaration: boolean
|
||||
): void {
|
||||
for (const identifier of variable.identifiers) {
|
||||
if (isGlobalDeclaration) {
|
||||
this.preserveIdentifierNameForRootLexicalScope(identifier);
|
||||
} else {
|
||||
this.preserveIdentifierNameForLexicalScope(identifier, variableScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Identifier} identifierNode
|
||||
* @param {Node} parentNode
|
||||
*/
|
||||
private preserveIdentifierNameForRootLexicalScope (
|
||||
identifierNode: ESTree.Identifier,
|
||||
parentNode: ESTree.Node
|
||||
): void {
|
||||
private preserveIdentifierNameForRootLexicalScope (identifierNode: ESTree.Identifier): void {
|
||||
this.identifierObfuscatingReplacer.preserveName(identifierNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Identifier} identifierNode
|
||||
* @param {Node} parentNode
|
||||
* @param {Scope} variableScope
|
||||
*/
|
||||
private preserveIdentifierNameForLexicalScope (
|
||||
identifierNode: ESTree.Identifier,
|
||||
parentNode: ESTree.Node
|
||||
variableScope: eslintScope.Scope
|
||||
): void {
|
||||
for (const lexicalScope of this.enteredLexicalScopesStack) {
|
||||
this.identifierObfuscatingReplacer.preserveNameForLexicalScope(identifierNode, lexicalScope);
|
||||
const lexicalScopeNode: TNodeWithLexicalScope | null = NodeGuards.isNodeWithLexicalScope(variableScope.block)
|
||||
? variableScope.block
|
||||
: null;
|
||||
|
||||
if (!lexicalScopeNode) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TNodeWithLexicalScope} lexicalScopeNode
|
||||
*/
|
||||
private addLexicalScopeToEnteredLexicalScopesStack (lexicalScopeNode: TNodeWithLexicalScope): void {
|
||||
this.enteredLexicalScopesStack.push(lexicalScopeNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TNodeWithLexicalScope} lexicalScopeNode
|
||||
*/
|
||||
private removeLexicalScopeFromEnteredLexicalScopesStack (lexicalScopeNode: TNodeWithLexicalScope): void {
|
||||
this.enteredLexicalScopesStack.pop();
|
||||
this.identifierObfuscatingReplacer.preserveNameForLexicalScope(identifierNode, lexicalScopeNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { inject, injectable, } from 'inversify';
|
||||
import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
|
||||
|
||||
import * as eslintScope from 'eslint-scope';
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { TNodeWithLexicalScope } from '../types/node/TNodeWithLexicalScope';
|
||||
import { TScopeIdentifiersTraverserCallback } from '../types/node/TScopeIdentifiersTraverserCallback';
|
||||
|
||||
import { IScopeAnalyzer } from '../interfaces/analyzers/scope-analyzer/IScopeAnalyzer';
|
||||
import { IScopeIdentifiersTraverser } from '../interfaces/node/IScopeIdentifiersTraverser';
|
||||
|
||||
import { NodeGuards } from './NodeGuards';
|
||||
|
||||
/**
|
||||
* Scope traverser
|
||||
*/
|
||||
@injectable()
|
||||
export class ScopeIdentifiersTraverser implements IScopeIdentifiersTraverser {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
private static readonly argumentsVariableName: string = 'arguments';
|
||||
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
private static readonly globalScopeNames: string[] = [
|
||||
'global',
|
||||
'module'
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {IScopeAnalyzer}
|
||||
*/
|
||||
private readonly scopeAnalyzer: IScopeAnalyzer;
|
||||
|
||||
/**
|
||||
* @param {IScopeAnalyzer} scopeAnalyzer
|
||||
*/
|
||||
public constructor (
|
||||
@inject(ServiceIdentifiers.IScopeAnalyzer) scopeAnalyzer: IScopeAnalyzer
|
||||
) {
|
||||
this.scopeAnalyzer = scopeAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Program} programNode
|
||||
* @param {Node | null} parentNode
|
||||
* @param {TScopeIdentifiersTraverserCallback} callback
|
||||
*/
|
||||
public traverse (
|
||||
programNode: ESTree.Program,
|
||||
parentNode: ESTree.Node | null,
|
||||
callback: TScopeIdentifiersTraverserCallback
|
||||
): void {
|
||||
this.scopeAnalyzer.analyze(programNode);
|
||||
|
||||
const globalScope: eslintScope.Scope = this.scopeAnalyzer.acquireScope(programNode);
|
||||
|
||||
this.traverseScopeVariables(globalScope, globalScope, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Scope} rootScope
|
||||
* @param {Scope} currentScope
|
||||
* @param {TScopeIdentifiersTraverserCallback} callback
|
||||
*/
|
||||
private traverseScopeVariables (
|
||||
rootScope: eslintScope.Scope,
|
||||
currentScope: eslintScope.Scope,
|
||||
callback: TScopeIdentifiersTraverserCallback
|
||||
): void {
|
||||
const variableScope: eslintScope.Scope = currentScope.variableScope;
|
||||
const variableLexicalScopeNode: TNodeWithLexicalScope | null = NodeGuards.isNodeWithBlockLexicalScope(variableScope.block)
|
||||
? variableScope.block
|
||||
: null;
|
||||
const isGlobalDeclaration: boolean = ScopeIdentifiersTraverser.globalScopeNames.includes(variableScope.type);
|
||||
|
||||
if (!variableLexicalScopeNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const variable of currentScope.variables) {
|
||||
if (variable.name === ScopeIdentifiersTraverser.argumentsVariableName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
callback({
|
||||
isGlobalDeclaration,
|
||||
rootScope,
|
||||
variable,
|
||||
variableScope,
|
||||
variableLexicalScopeNode
|
||||
});
|
||||
}
|
||||
|
||||
for (const childScope of currentScope.childScopes) {
|
||||
this.traverseScopeVariables(rootScope, childScope, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { IScopeIdentifiersTraverserCallbackData } from '../../interfaces/node/IScopeIdentifiersTraverserCallbackData';
|
||||
|
||||
export type TScopeIdentifiersTraverserCallback = (data: IScopeIdentifiersTraverserCallbackData) => void;
|
||||
+2356
File diff suppressed because it is too large
Load Diff
+5
-11
@@ -7,22 +7,16 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
|
||||
|
||||
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
|
||||
`
|
||||
function a (a, b) {
|
||||
|
||||
}
|
||||
|
||||
function b (b, c) {
|
||||
|
||||
}
|
||||
const b = function () {}
|
||||
|
||||
// javascript-obfuscator:disable
|
||||
function a () {}
|
||||
// javascript-obfuscator:enable
|
||||
`,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
compact: false,
|
||||
identifierNamesGenerator: 'mangled',
|
||||
identifiersPrefix: 'a',
|
||||
transformObjectKeys: true,
|
||||
stringArray: true,
|
||||
stringArrayThreshold: 1,
|
||||
log: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
|
||||
+3
-3
@@ -483,9 +483,9 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
|
||||
});
|
||||
|
||||
describe('Variant #3: preserved identifier names shouldn\'t be used as identifier names', () => {
|
||||
const classDeclarationRegExp: RegExp = /class *f *{/;
|
||||
const variableDeclarationsRegExp: RegExp = /let g, *h, *i, *j;/;
|
||||
const classReferenceRegExp: RegExp = /new f\(\);/;
|
||||
const classDeclarationRegExp: RegExp = /class *e *{/;
|
||||
const variableDeclarationsRegExp: RegExp = /let f, *g, *h, *i;/;
|
||||
const classReferenceRegExp: RegExp = /new e\(\);/;
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ describe('ScopeIdentifiersTransformer FunctionDeclaration identifiers', () => {
|
||||
describe('Variant #5: preserved identifier names shouldn\'t be used as identifier names', () => {
|
||||
describe('Variant #1', () => {
|
||||
const functionDeclarationRegExp: RegExp = /function *e\(\) *{/;
|
||||
const variableDeclarationsRegExp: RegExp = /let g, *h, *i, *j;/;
|
||||
const variableDeclarationsRegExp: RegExp = /let f, *g, *h, *i;/;
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
|
||||
+5
-5
@@ -456,11 +456,11 @@ describe('ScopeIdentifiersTransformer VariableDeclaration identifiers', () => {
|
||||
|
||||
describe('Variant #13: preserved identifier names shouldn\'t be used as identifier names', () => {
|
||||
describe('Variant #1', () => {
|
||||
const variableDeclarationRegExp: RegExp = /var f *= *0x1;/;
|
||||
const functionDeclarationRegExp1: RegExp = /function *g *\(\) *{}/;
|
||||
const functionDeclarationRegExp2: RegExp = /function *h *\(\) *{}/;
|
||||
const functionDeclarationRegExp3: RegExp = /function *i *\(\) *{}/;
|
||||
const functionDeclarationRegExp4: RegExp = /function *j *\(\) *{}/;
|
||||
const variableDeclarationRegExp: RegExp = /var e *= *0x1;/;
|
||||
const functionDeclarationRegExp1: RegExp = /function *f *\(\) *{}/;
|
||||
const functionDeclarationRegExp2: RegExp = /function *g *\(\) *{}/;
|
||||
const functionDeclarationRegExp3: RegExp = /function *h *\(\) *{}/;
|
||||
const functionDeclarationRegExp4: RegExp = /function *i *\(\) *{}/;
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
|
||||
+30
@@ -126,4 +126,34 @@ describe('VariablePreserveTransformer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #3: ignored node identifier name conflict with identifier name', () => {
|
||||
describe('Variant #1: global scope', () => {
|
||||
const functionExpressionIdentifierName: RegExp = /const c *= *function *\(\) *{};/;
|
||||
const functionDeclarationIdentifierName: RegExp = /function a *\(\) *{}/;
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/ignored-node-identifier-name-1.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
identifierNamesGenerator: 'mangled',
|
||||
renameGlobals: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('should generate non-preserved name for global identifier', () => {
|
||||
assert.match(obfuscatedCode, functionExpressionIdentifierName);
|
||||
});
|
||||
|
||||
it('should keep the original name for ignored identifier', () => {
|
||||
assert.match(obfuscatedCode, functionDeclarationIdentifierName);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
const b = function () {};
|
||||
|
||||
// javascript-obfuscator:disable
|
||||
function a () {}
|
||||
// javascript-obfuscator:enable
|
||||
Reference in New Issue
Block a user