Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
- **Parser**: Acorn 8.8.2 (ES3-ES2020 support)
- **Code Generator**: @javascript-obfuscator/escodegen 2.3.0
- **AST Traversal**: @javascript-obfuscator/estraverse 5.4.0
- **DI Framework**: InversifyJS 6.0.1
- **DI Framework**: InversifyJS 7.10.8
- **Testing**: Mocha 10.4.0 + Chai 4.3.7
- **Build System**: Webpack 5.75.0

Expand Down Expand Up @@ -210,7 +210,7 @@ The obfuscation process follows a multi-stage pipeline defined in `JavaScriptObf

### Dependency Injection Architecture

The project uses **InversifyJS** for dependency injection, providing:
The project uses **InversifyJS v7** for dependency injection, providing:

- **Modularity**: Clean separation of concerns
- **Testability**: Easy mocking and testing
Expand All @@ -219,6 +219,13 @@ The project uses **InversifyJS** for dependency injection, providing:

All components are registered in container modules located in `src/container/modules/`.

**Key Changes in InversifyJS v7:**
- Container modules now use `ContainerModuleLoadOptions` instead of separate `bind`, `unbind`, etc. parameters
- `getNamed`, `getTagged`, etc. are replaced by `get(serviceId, { name: ... })` or `get(serviceId, { tag: ... })`
- `load()` and `unload()` are now async, with `loadSync()` and `unloadSync()` alternatives for synchronous operations
- Types like `Context`, `Newable`, `Factory` are now directly exported instead of through `interfaces` namespace
- Custom metadata and middleware features have been removed

## Key Components Deep Dive

### 1. JavaScriptObfuscator (Main Engine)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"eslint-scope": "8.4.0",
"eslint-visitor-keys": "4.2.1",
"fast-deep-equal": "3.1.3",
"inversify": "6.1.4",
"inversify": "7.11.0",
"js-string-escape": "1.0.1",
"md5": "2.3.0",
"mkdirp": "3.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { injectable } from 'inversify';
import { injectable, injectFromBase } from 'inversify';

import * as estraverse from '@javascript-obfuscator/estraverse';
import * as ESTree from 'estree';
Expand All @@ -9,6 +9,7 @@ import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
import { NodeGuards } from '../../../node/NodeGuards';
import { NodeStatementUtils } from '../../../node/NodeStatementUtils';

@injectFromBase()
@injectable()
export class FunctionDeclarationCalleeDataExtractor extends AbstractCalleeDataExtractor {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { injectable } from 'inversify';
import { injectable, injectFromBase } from 'inversify';

import * as estraverse from '@javascript-obfuscator/estraverse';
import * as ESTree from 'estree';
Expand All @@ -9,6 +9,7 @@ import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
import { NodeGuards } from '../../../node/NodeGuards';
import { NodeStatementUtils } from '../../../node/NodeStatementUtils';

@injectFromBase()
@injectable()
export class FunctionExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { injectable } from 'inversify';
import { injectable, injectFromBase } from 'inversify';

import * as estraverse from '@javascript-obfuscator/estraverse';
import * as ESTree from 'estree';
Expand All @@ -11,6 +11,7 @@ import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
import { NodeGuards } from '../../../node/NodeGuards';
import { NodeStatementUtils } from '../../../node/NodeStatementUtils';

@injectFromBase()
@injectable()
export class ObjectExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
/**
Expand Down
3 changes: 2 additions & 1 deletion src/code-transformers/CodeTransformerNamesGroupsBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { injectable } from 'inversify';
import { injectable, injectFromBase } from 'inversify';

import { ICodeTransformer } from '../interfaces/code-transformers/ICodeTransformer';

import { CodeTransformer } from '../enums/code-transformers/CodeTransformer';

import { AbstractTransformerNamesGroupsBuilder } from '../utils/AbstractTransformerNamesGroupsBuilder';

@injectFromBase()
@injectable()
export class CodeTransformerNamesGroupsBuilder extends AbstractTransformerNamesGroupsBuilder<
CodeTransformer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { inject, injectable } from 'inversify';
import { inject, injectable, injectFromBase } from 'inversify';
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';

import { IOptions } from '../../interfaces/options/IOptions';
Expand All @@ -8,6 +8,7 @@ import { CodeTransformationStage } from '../../enums/code-transformers/CodeTrans

import { AbstractCodeTransformer } from '../AbstractCodeTransformer';

@injectFromBase()
@injectable()
export class HashbangOperatorTransformer extends AbstractCodeTransformer {
/**
Expand Down
104 changes: 50 additions & 54 deletions src/container/InversifyContainerFacade.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Container, interfaces } from 'inversify';
import { Container, ResolutionContext, ServiceIdentifier, Factory } from 'inversify';
import { ServiceIdentifiers } from './ServiceIdentifiers';

import { analyzersModule } from './modules/analyzers/AnalyzersModule';
Expand Down Expand Up @@ -42,9 +42,9 @@ import { SourceCode } from '../source-code/SourceCode';

export class InversifyContainerFacade implements IInversifyContainerFacade {
/**
* @type {interfaces.Container}
* @type {Container}
*/
private readonly container: interfaces.Container;
private readonly container: Container;

public constructor() {
this.container = new Container();
Expand All @@ -55,11 +55,11 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
* @returns {U}
*/
public static getFactory<T extends string, U>(
serviceIdentifier: interfaces.ServiceIdentifier<U>
): (context: interfaces.Context) => (bindingName: T) => U {
return (context: interfaces.Context): ((bindingName: T) => U) => {
serviceIdentifier: ServiceIdentifier<U>
): (context: ResolutionContext) => (bindingName: T) => U {
return (context: ResolutionContext): ((bindingName: T) => U) => {
return (bindingName: T): U => {
return context.container.getNamed<U>(serviceIdentifier, bindingName);
return context.get<U>(serviceIdentifier, { name: bindingName });
};
};
}
Expand All @@ -69,17 +69,17 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
* @returns {U}
*/
public static getCacheFactory<T extends string, U>(
serviceIdentifier: interfaces.ServiceIdentifier<U>
): (context: interfaces.Context) => (bindingName: T) => U {
return (context: interfaces.Context): ((bindingName: T) => U) => {
serviceIdentifier: ServiceIdentifier<U>
): (context: ResolutionContext) => (bindingName: T) => U {
return (context: ResolutionContext): ((bindingName: T) => U) => {
const cache: Map<T, U> = new Map();

return (bindingName: T): U => {
if (cache.has(bindingName)) {
return <U>cache.get(bindingName);
}

const object: U = context.container.getNamed<U>(serviceIdentifier, bindingName);
const object: U = context.get<U>(serviceIdentifier, { name: bindingName });

cache.set(bindingName, object);

Expand All @@ -91,24 +91,21 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
/**
* @param {interfaces.ServiceIdentifier<TConstructor<Record<string, any>[], U>>} serviceIdentifier
* @param {interfaces.ServiceIdentifier<TConstructor<Record<string, any>[], U>>} dependencies
* @returns {(context: interfaces.Context) => (bindingName: T) => U}
* @returns {(context: ResolutionContext) => (bindingName: T) => U}
*/
public static getConstructorFactory<T extends string, U>(
serviceIdentifier: interfaces.ServiceIdentifier<TConstructor<Record<string, any>[], U>>,
...dependencies: interfaces.ServiceIdentifier<TConstructor<Record<string, any>[], U>>[]
): (context: interfaces.Context) => (bindingName: T) => U {
return (context: interfaces.Context): ((bindingName: T) => U) => {
serviceIdentifier: ServiceIdentifier<TConstructor<Record<string, any>[], U>>,
...dependencies: ServiceIdentifier<TConstructor<Record<string, any>[], U>>[]
): (context: ResolutionContext) => (bindingName: T) => U {
return (context: ResolutionContext): ((bindingName: T) => U) => {
const cache: Map<T, TConstructor<Record<string, any>[], U>> = new Map();
const cachedDependencies: Record<string, any>[] = [];

return (bindingName: T): U => {
dependencies.forEach(
(
dependency: interfaces.ServiceIdentifier<TConstructor<Record<string, any>[], U>>,
index: number
) => {
(dependency: ServiceIdentifier<TConstructor<Record<string, any>[], U>>, index: number) => {
if (!cachedDependencies[index]) {
cachedDependencies[index] = context.container.get(dependency);
cachedDependencies[index] = context.get(dependency);
}
}
);
Expand All @@ -117,10 +114,9 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
return new (<TConstructor<Record<string, any>[], U>>cache.get(bindingName))(...cachedDependencies);
}

const constructor = context.container.getNamed<TConstructor<Record<string, any>[], U>>(
serviceIdentifier,
bindingName
);
const constructor = context.get<TConstructor<Record<string, any>[], U>>(serviceIdentifier, {
name: bindingName
});

cache.set(bindingName, constructor);

Expand All @@ -130,20 +126,20 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
}

/**
* @param {interfaces.ServiceIdentifier<T>} serviceIdentifier
* @param {ServiceIdentifier<T>} serviceIdentifier
* @returns {T}
*/
public get<T>(serviceIdentifier: interfaces.ServiceIdentifier<T>): T {
public get<T>(serviceIdentifier: ServiceIdentifier<T>): T {
return this.container.get<T>(serviceIdentifier);
}

/**
* @param {interfaces.ServiceIdentifier<T>} serviceIdentifier
* @param {ServiceIdentifier<T>} serviceIdentifier
* @param {string | number | symbol} named
* @returns {T}
*/
public getNamed<T>(serviceIdentifier: interfaces.ServiceIdentifier<T>, named: string | number | symbol): T {
return this.container.getNamed<T>(serviceIdentifier, named);
public getNamed<T>(serviceIdentifier: ServiceIdentifier<T>, named: string | number | symbol): T {
return this.container.get<T>(serviceIdentifier, { name: named });
}

/**
Expand Down Expand Up @@ -182,10 +178,10 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
this.container.bind<IObfuscationResult>(ServiceIdentifiers.IObfuscationResult).to(ObfuscationResult);

this.container
.bind<IObfuscationResult>(ServiceIdentifiers.Factory__IObfuscationResult)
.toFactory<IObfuscationResult, [string, string]>((context: interfaces.Context) => {
.bind<Factory<IObfuscationResult, [string, string]>>(ServiceIdentifiers.Factory__IObfuscationResult)
.toFactory((context: ResolutionContext) => {
return (obfuscatedCodeAsString: string, sourceMapAsString: string): IObfuscationResult => {
const obfuscationResult: IObfuscationResult = context.container.get<IObfuscationResult>(
const obfuscationResult: IObfuscationResult = context.get<IObfuscationResult>(
ServiceIdentifiers.IObfuscationResult
);

Expand All @@ -196,29 +192,29 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
});

// modules
this.container.load(analyzersModule);
this.container.load(codeTransformersModule);
this.container.load(controlFlowTransformersModule);
this.container.load(convertingTransformersModule);
this.container.load(customCodeHelpersModule);
this.container.load(customNodesModule);
this.container.load(deadCodeInjectionTransformersModule);
this.container.load(finalizingTransformersModule);
this.container.load(generatorsModule);
this.container.load(initializingTransformersModule);
this.container.load(nodeModule);
this.container.load(nodeTransformersModule);
this.container.load(optionsModule);
this.container.load(preparingTransformersModule);
this.container.load(renameIdentifiersTransformersModule);
this.container.load(renamePropertiesTransformersModule);
this.container.load(simplifyingTransformersModule);
this.container.load(storagesModule);
this.container.load(stringArrayTransformersModule);
this.container.load(utilsModule);
this.container.loadSync(analyzersModule);
this.container.loadSync(codeTransformersModule);
this.container.loadSync(controlFlowTransformersModule);
this.container.loadSync(convertingTransformersModule);
this.container.loadSync(customCodeHelpersModule);
this.container.loadSync(customNodesModule);
this.container.loadSync(deadCodeInjectionTransformersModule);
this.container.loadSync(finalizingTransformersModule);
this.container.loadSync(generatorsModule);
this.container.loadSync(initializingTransformersModule);
this.container.loadSync(nodeModule);
this.container.loadSync(nodeTransformersModule);
this.container.loadSync(optionsModule);
this.container.loadSync(preparingTransformersModule);
this.container.loadSync(renameIdentifiersTransformersModule);
this.container.loadSync(renamePropertiesTransformersModule);
this.container.loadSync(simplifyingTransformersModule);
this.container.loadSync(storagesModule);
this.container.loadSync(stringArrayTransformersModule);
this.container.loadSync(utilsModule);
}

public unload(): void {
this.container.unbindAll();
this.container.unbindAllSync();
}
}
47 changes: 26 additions & 21 deletions src/container/modules/analyzers/AnalyzersModule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { InversifyContainerFacade } from '../../InversifyContainerFacade';
import { ContainerModule, interfaces } from 'inversify';
import { ContainerModule, ContainerModuleLoadOptions, Factory } from 'inversify';
import { ServiceIdentifiers } from '../../ServiceIdentifiers';

import { ICalleeDataExtractor } from '../../../interfaces/analyzers/calls-graph-analyzer/ICalleeDataExtractor';
Expand All @@ -19,48 +19,53 @@ import { PrevailingKindOfVariablesAnalyzer } from '../../../analyzers/prevailing
import { ScopeAnalyzer } from '../../../analyzers/scope-analyzer/ScopeAnalyzer';
import { StringArrayStorageAnalyzer } from '../../../analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer';

export const analyzersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
export const analyzersModule: ContainerModule = new ContainerModule((options: ContainerModuleLoadOptions) => {
// calls graph analyzer
bind<ICallsGraphAnalyzer>(ServiceIdentifiers.ICallsGraphAnalyzer).to(CallsGraphAnalyzer).inSingletonScope();
options.bind<ICallsGraphAnalyzer>(ServiceIdentifiers.ICallsGraphAnalyzer).to(CallsGraphAnalyzer).inSingletonScope();

// number numerical expression analyzer
bind<INumberNumericalExpressionAnalyzer>(ServiceIdentifiers.INumberNumericalExpressionAnalyzer)
options
.bind<INumberNumericalExpressionAnalyzer>(ServiceIdentifiers.INumberNumericalExpressionAnalyzer)
.to(NumberNumericalExpressionAnalyzer)
.inSingletonScope();

// prevailing kind of variables analyzer
bind<IPrevailingKindOfVariablesAnalyzer>(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
options
.bind<IPrevailingKindOfVariablesAnalyzer>(ServiceIdentifiers.IPrevailingKindOfVariablesAnalyzer)
.to(PrevailingKindOfVariablesAnalyzer)
.inSingletonScope();

// scope analyzer
bind<IScopeAnalyzer>(ServiceIdentifiers.IScopeAnalyzer).to(ScopeAnalyzer).inSingletonScope();
options.bind<IScopeAnalyzer>(ServiceIdentifiers.IScopeAnalyzer).to(ScopeAnalyzer).inSingletonScope();

// string array storage analyzer
bind<IStringArrayStorageAnalyzer>(ServiceIdentifiers.IStringArrayStorageAnalyzer)
options
.bind<IStringArrayStorageAnalyzer>(ServiceIdentifiers.IStringArrayStorageAnalyzer)
.to(StringArrayStorageAnalyzer)
.inSingletonScope();

// callee data extractors
bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
options
.bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
.to(FunctionDeclarationCalleeDataExtractor)
.whenTargetNamed(CalleeDataExtractor.FunctionDeclarationCalleeDataExtractor);
.whenNamed(CalleeDataExtractor.FunctionDeclarationCalleeDataExtractor);

bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
options
.bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
.to(FunctionExpressionCalleeDataExtractor)
.whenTargetNamed(CalleeDataExtractor.FunctionExpressionCalleeDataExtractor);
.whenNamed(CalleeDataExtractor.FunctionExpressionCalleeDataExtractor);

bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
options
.bind<ICalleeDataExtractor>(ServiceIdentifiers.ICalleeDataExtractor)
.to(ObjectExpressionCalleeDataExtractor)
.whenTargetNamed(CalleeDataExtractor.ObjectExpressionCalleeDataExtractor);
.whenNamed(CalleeDataExtractor.ObjectExpressionCalleeDataExtractor);

// callee data extractor factory
bind<ICalleeDataExtractor>(ServiceIdentifiers.Factory__ICalleeDataExtractor).toFactory<
ICalleeDataExtractor,
[CalleeDataExtractor]
>(
InversifyContainerFacade.getCacheFactory<CalleeDataExtractor, ICalleeDataExtractor>(
ServiceIdentifiers.ICalleeDataExtractor
)
);
options
.bind<Factory<ICalleeDataExtractor, [CalleeDataExtractor]>>(ServiceIdentifiers.Factory__ICalleeDataExtractor)
.toFactory(
InversifyContainerFacade.getCacheFactory<CalleeDataExtractor, ICalleeDataExtractor>(
ServiceIdentifiers.ICalleeDataExtractor
)
);
});
Loading