Skip to content

Latest commit

 

History

History
1477 lines (1108 loc) · 43.7 KB

File metadata and controls

1477 lines (1108 loc) · 43.7 KB

JavaScript Obfuscator - Project Documentation

Project Overview

JavaScript Obfuscator is a powerful, enterprise-grade code obfuscation tool for JavaScript and Node.js applications. It transforms readable JavaScript code into a protected, difficult-to-understand format while maintaining full functionality. The project is widely used for protecting intellectual property and preventing reverse engineering.

Key Features

Core Obfuscation Techniques

  1. Variable & Function Renaming: Replaces identifiable names with cryptic hexadecimal or mangled identifiers
  2. String Extraction & Encryption: Moves string literals to an encoded array with base64/rc4 encryption
  3. Dead Code Injection: Inserts non-functional code blocks to confuse static analysis
  4. Control Flow Flattening: Restructures code flow using switch statements to obscure logic
  5. Code Transformations: Multiple AST-level transformations including:
    • Boolean literal obfuscation
    • Number to expression conversion
    • Object key transformation
    • Template literal transformation
    • Property renaming (safe/unsafe modes)

Advanced Protection Features

  • Self-Defending Code: Code that breaks when beautified or modified
  • Debug Protection: Anti-debugging mechanisms to prevent DevTools usage
  • Domain Lock: Restricts code execution to specific domains/subdomains
  • Console Output Disabling: Removes console.* functionality
  • Unicode Escape Sequences: Additional string obfuscation layer

Architecture Overview

Technology Stack

  • Language: TypeScript 4.9.5
  • 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
  • Testing: Mocha 10.4.0 + Chai 4.3.7
  • Build System: Webpack 5.75.0

Project Structure

javascript-obfuscator/
├── src/                              # Source code
│   ├── JavaScriptObfuscator.ts      # Main obfuscator class
│   ├── JavaScriptObfuscatorFacade.ts # Public API facade
│   ├── JavaScriptObfuscatorCLIFacade.ts # CLI interface
│   ├── ASTParserFacade.ts           # AST parsing wrapper
│   │
│   ├── analyzers/                    # Code analysis components
│   │   ├── calls-graph-analyzer/    # Function call graph analysis
│   │   ├── scope-analyzer/          # Variable scope analysis
│   │   ├── string-array-storage-analyzer/ # String array optimization
│   │   ├── number-numerical-expression-analyzer/
│   │   └── prevailing-kind-of-variables-analyzer/
│   │
│   ├── node-transformers/            # AST transformation pipeline
│   │   ├── AbstractNodeTransformer.ts
│   │   ├── NodeTransformersRunner.ts
│   │   ├── converting-transformers/ # Node type conversions
│   │   ├── control-flow-transformers/ # Control flow flattening
│   │   ├── dead-code-injection-transformers/ # Dead code generation
│   │   ├── finalizing-transformers/ # Post-processing transforms
│   │   ├── initializing-transformers/ # Pre-processing transforms
│   │   ├── preparing-transformers/  # Preparation phase
│   │   ├── rename-identifiers-transformers/ # Variable renaming
│   │   ├── rename-properties-transformers/ # Property renaming
│   │   ├── simplifying-transformers/ # Code simplification
│   │   └── string-array-transformers/ # String array handling
│   │
│   ├── code-transformers/            # Code-level (not AST) transformers
│   │   ├── AbstractCodeTransformer.ts
│   │   ├── CodeTransformersRunner.ts
│   │   └── CodeTransformerNamesGroupsBuilder.ts
│   │
│   ├── custom-code-helpers/          # Injectable code helpers
│   │   ├── common/                   # Global variable templates
│   │   ├── console-output/          # Console disabling templates
│   │   ├── debug-protection/        # Anti-debugging templates
│   │   ├── domain-lock/             # Domain restriction templates
│   │   ├── self-defending/          # Self-defense templates
│   │   └── string-array/            # String array wrapper templates
│   │
│   ├── custom-nodes/                 # Custom AST node generators
│   │   ├── control-flow-flattening-nodes/
│   │   ├── dead-code-injection-nodes/
│   │   ├── object-expression-keys-transformer-nodes/
│   │   └── string-array-nodes/
│   │
│   ├── container/                    # Dependency injection
│   │   ├── InversifyContainerFacade.ts
│   │   ├── ServiceIdentifiers.ts
│   │   └── modules/                 # DI module definitions
│   │
│   ├── options/                      # Configuration system
│   │   ├── Options.ts
│   │   ├── OptionsNormalizer.ts
│   │   ├── validators/              # Option validation
│   │   ├── normalizer-rules/        # Option normalization
│   │   └── presets/                 # Obfuscation presets
│   │
│   ├── storages/                     # Data storage components
│   │   ├── string-array-transformers/
│   │   ├── control-flow-transformers/
│   │   ├── custom-code-helpers/
│   │   └── identifier-names-cache/
│   │
│   ├── node/                         # AST node utilities
│   │   ├── NodeGuards.ts            # Type guards
│   │   ├── NodeFactory.ts           # Node creation
│   │   ├── NodeAppender.ts          # Node insertion
│   │   ├── NodeStatementUtils.ts
│   │   └── NodeUtils.ts
│   │
│   ├── generators/                   # Name/value generators
│   │   ├── identifier-names-generators/
│   │   └── string-array-index-nodes-generators/
│   │
│   ├── utils/                        # Utility functions
│   │   ├── RandomGenerator.ts
│   │   ├── ArrayUtils.ts
│   │   ├── CryptUtils.ts
│   │   ├── LevelledTopologicalSorter.ts
│   │   └── Utils.ts
│   │
│   ├── cli/                          # CLI utilities
│   │   ├── sanitizers/              # Input sanitizers
│   │   └── utils/                   # File handling
│   │
│   ├── enums/                        # Enumerations
│   ├── interfaces/                   # TypeScript interfaces
│   ├── types/                        # Type definitions
│   ├── constants/                    # Constants
│   ├── decorators/                   # Decorators
│   └── logger/                       # Logging system
│
├── test/                             # Test suite
│   ├── functional-tests/            # Feature tests
│   ├── unit-tests/                  # Unit tests
│   ├── performance-tests/           # Performance benchmarks
│   └── index.spec.ts
│
├── webpack/                          # Build configurations
│   ├── webpack.node.config.js
│   └── webpack.browser.config.js
│
├── dist/                             # Compiled output
│   ├── index.js                     # Node.js bundle
│   └── index.browser.js             # Browser bundle
│
├── bin/                              # CLI executable
│   └── javascript-obfuscator
│
└── typings/                          # TypeScript declarations

Core Workflow

Obfuscation Pipeline

The obfuscation process follows a multi-stage pipeline defined in JavaScriptObfuscator.ts:

1. Code Transformation Stage: PreparingTransformers
   └─> Raw code preprocessing (e.g., hashbang handling)

2. AST Parsing
   └─> Parse source code into ESTree-compliant AST using Acorn

3. Node Transformation Stages (sequential):
   ├─> Initializing
   │   └─> Initial AST setup, parentification, metadata
   ├─> Preparing
   │   └─> Scope analysis, obfuscating guards, identifier collection
   ├─> DeadCodeInjection (optional)
   │   └─> Insert dead code blocks
   ├─> ControlFlowFlattening (optional)
   │   └─> Flatten control flow with switch statements
   ├─> RenameProperties (optional)
   │   └─> Rename object properties
   ├─> Converting
   │   └─> Transform nodes (literals, expressions, etc.)
   ├─> RenameIdentifiers
   │   └─> Rename variables and functions
   ├─> StringArray
   │   └─> Extract strings to array, add wrappers
   ├─> Simplifying (optional)
   │   └─> Simplify and merge statements
   └─> Finalizing
       └─> Final cleanup, directive placement

4. Code Generation
   └─> Generate obfuscated code using escodegen

5. Code Transformation Stage: FinalizingTransformers
   └─> Post-processing on generated code

6. Source Map Generation (optional)
   └─> Create source maps for debugging

Dependency Injection Architecture

The project uses InversifyJS for dependency injection, providing:

  • Modularity: Clean separation of concerns
  • Testability: Easy mocking and testing
  • Flexibility: Runtime configuration of transformers
  • Scalability: Easy addition of new transformers

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

Key Components Deep Dive

1. JavaScriptObfuscator (Main Engine)

Location: src/JavaScriptObfuscator.ts

The core orchestrator that:

  • Manages the complete obfuscation pipeline
  • Coordinates code and node transformers
  • Handles AST parsing and code generation
  • Integrates with logger and random generator

Key Methods:

  • obfuscate(sourceCode: string): IObfuscationResult - Main entry point
  • parseCode() - AST parsing with Acorn
  • transformAstTree() - Applies transformation stages
  • generateCode() - Code generation with escodegen

2. Node Transformers

Location: src/node-transformers/

Each transformer implements INodeTransformer interface with:

  • getVisitor(stage): IVisitor | null - Returns visitor for specific stage
  • transformNode(node, parent): Node - Transforms individual AST node

Key Transformers:

  • StringArrayTransformer: Extracts string literals to centralized array
  • BooleanLiteralTransformer: Converts true/false to !![] and ![]
  • NumberToNumericalExpressionTransformer: Converts numbers to expressions
  • BlockStatementControlFlowTransformer: Implements control flow flattening
  • DeadCodeInjectionTransformer: Injects dead code blocks
  • RenamePropertiesTransformer: Renames object properties
  • ScopeIdentifiersTransformer: Renames variables based on scope

3. Analyzers

Location: src/analyzers/

  • CallsGraphAnalyzer: Builds function call dependency graph
  • ScopeAnalyzer: Analyzes variable scopes using eslint-scope
  • StringArrayStorageAnalyzer: Optimizes string array storage
  • PrevailingKindOfVariablesAnalyzer: Determines var/let/const usage
  • NumberNumericalExpressionAnalyzer: Analyzes numeric expressions

4. Custom Code Helpers

Location: src/custom-code-helpers/

Injectable runtime helpers that provide:

  • String Array Decoders: Base64/RC4 decoding functions
  • Debug Protection: Anti-debugging wrapper code
  • Domain Lock: Domain validation code
  • Self-Defending: Code integrity checks
  • Console Output Disable: Console method replacements

5. Options System

Location: src/options/

Sophisticated configuration system with:

  • Validation: Using class-validator decorators
  • Normalization: Automatic option interdependency handling
  • Presets: Default, low, medium, and high obfuscation presets
  • Type Safety: Full TypeScript support

Key Option Categories:

  • Code output (compact, target)
  • String transformations (stringArray*, splitStrings)
  • Control flow (controlFlowFlattening, deadCodeInjection)
  • Naming (identifierNamesGenerator, renameGlobals, renameProperties)
  • Protection (selfDefending, debugProtection, domainLock)
  • Advanced (numbersToExpressions, simplify, transformObjectKeys)

Important Patterns and Conventions

1. Visitor Pattern

Transformers use the visitor pattern for AST traversal:

interface IVisitor {
    enter?: (node: Node, parent: Node) => Node | VisitorOption;
    leave?: (node: Node, parent: Node) => Node | VisitorOption;
}

2. Initializable Pattern

Many components implement IInitializable for lazy initialization:

interface IInitializable {
    initialize(...args: any[]): void;
}

Managed via @Initializable() decorator.

3. Stage-Based Processing

Both code and node transformers operate in stages:

Code Transformation Stages:

  • PreparingTransformers
  • FinalizingTransformers

Node Transformation Stages:

  • Initializing
  • Preparing
  • DeadCodeInjection
  • ControlFlowFlattening
  • RenameProperties
  • Converting
  • RenameIdentifiers
  • StringArray
  • Simplifying
  • Finalizing

4. Factory Pattern

Extensive use of factories for object creation:

  • TObfuscationResultFactory
  • Custom node factories
  • Identifier name generators

5. Storage Pattern

Centralized storages for shared data:

  • String array storage
  • Custom code helpers storage
  • Identifier names cache storage
  • Control flow transformers storage

CLI Usage

Location: bin/javascript-obfuscator, src/JavaScriptObfuscatorCLIFacade.ts

Basic Commands

# Obfuscate single file
javascript-obfuscator input.js --output output.js

# Obfuscate directory
javascript-obfuscator ./src --output ./dist

# Use configuration file
javascript-obfuscator input.js --config config.json

# High obfuscation preset
javascript-obfuscator input.js --options-preset high-obfuscation

CLI Features

  • Automatic identifier prefix for multiple files
  • Glob pattern exclusions
  • Source map support
  • Identifier names cache (cross-file consistency)
  • Progress logging

API Usage

Basic Obfuscation

const JavaScriptObfuscator = require('javascript-obfuscator');

const obfuscationResult = JavaScriptObfuscator.obfuscate(
    `
    var foo = 'Hello World';
    console.log(foo);
    `,
    {
        compact: true,
        controlFlowFlattening: true
    }
);

console.log(obfuscationResult.getObfuscatedCode());
console.log(obfuscationResult.getSourceMap());
console.log(obfuscationResult.getIdentifierNamesCache());

Multiple Files

const sourceCodesObject = {
    'file1.js': 'var foo = 1;',
    'file2.js': 'var bar = 2;'
};

const obfuscationResults = JavaScriptObfuscator.obfuscateMultiple(
    sourceCodesObject,
    options
);

Identifier Names Cache (Cross-File Consistency)

// First file
const result1 = JavaScriptObfuscator.obfuscate(code1, {
    identifierNamesCache: {},
    renameGlobals: true
});
const cache = result1.getIdentifierNamesCache();

// Second file using same cache
const result2 = JavaScriptObfuscator.obfuscate(code2, {
    identifierNamesCache: cache,
    renameGlobals: true
});

Browser Support

The project includes a browser build at dist/index.browser.js that can be used in web environments:

<script src="https://cdn.jsdelivr.net/npm/javascript-obfuscator/dist/index.browser.js"></script>
<script>
    const obfuscationResult = JavaScriptObfuscator.obfuscate(code, options);
</script>

Note: No eval() in browser-no-eval target.

Build System

Webpack Configuration

  • Node.js build: webpack/webpack.node.config.js

    • Target: CommonJS module
    • External dependencies: node_modules
    • Output: dist/index.js
  • Browser build: webpack/webpack.browser.config.js

    • Target: UMD module
    • Bundled dependencies
    • Output: dist/index.browser.js

Build Scripts

# Production build
npm run build

# Development watch mode
npm run watch

# Build TypeScript typings
npm run build:typings

# Linting
npm run eslint

Testing

Test Structure

Location: test/

  • Functional tests: Feature-level tests for transformers and options
  • Unit tests: Component-level tests
  • Performance tests: Memory and speed benchmarks

Running Tests

Quick Start

# Install dependencies first
npm install
# or
yarn install

# Run all tests (includes dev test, coverage, and memory performance)
npm test
# or
yarn test

Individual Test Commands

# Run full test suite (test:dev + test:mocha-coverage + test:mocha-memory-performance). This is slow.
npm run test:full
yarn run test:full

# Run Mocha tests only (no coverage)
npm run test:mocha
yarn run test:mocha

# Run tests with coverage report
npm run test:mocha-coverage
yarn run test:mocha-coverage

# Generate detailed coverage report (after running test:mocha-coverage)
npm run test:mocha-coverage:report
yarn run test:mocha-coverage:report

# Run memory performance tests (tests memory constraints)
npm run test:mocha-memory-performance
yarn run test:mocha-memory-performance

# Run development test (custom dev test file)
npm run test:dev
yarn run test:dev

# Run compile performance test
npm run test:devCompilePerformance
yarn run test:devCompilePerformance

# Run runtime performance test
npm run test:devRuntimePerformance
yarn run test:devRuntimePerformance

Test Details

test:full

  • Runs the complete test suite
  • Includes: development tests, coverage tests, and memory performance tests
  • This is what runs when you execute npm test

test:mocha

  • Runs all Mocha tests from test/index.spec.ts
  • Uses ts-node for TypeScript execution
  • No code coverage reporting

test:mocha-coverage

  • Runs Mocha tests with NYC (Istanbul) code coverage
  • Allocates up to 4GB memory (--max-old-space-size=4096)
  • Generates coverage reports (text-summary by default)
  • Use test:mocha-coverage:report to generate detailed lcov report

test:mocha-memory-performance

  • Tests obfuscator memory usage under constraints
  • Allocates only 280MB memory to test memory efficiency
  • Located at: test/performance-tests/JavaScriptObfuscatorMemory.spec.ts

test:dev

  • Custom development test script
  • Located at: test/dev/dev.ts
  • Useful for quick testing during development

Test Configuration Files

  • .mocharc.json: Mocha test runner configuration
  • .nycrc.json: NYC (Istanbul) coverage tool configuration
  • TypeScript: Uses ts-node for direct TS execution without compilation

Running Specific Test Files

You can run individual test files or groups of tests for faster iteration during development.

Basic Command Format

npx mocha --require ts-node/register --require source-map-support/register <path-to-test-file>

Common Examples

# Run a specific test file by exact path
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/options/Options.spec.ts

# Run CLI tests
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/cli/JavaScriptObfuscatorCLI.spec.ts

# Run a specific analyzer test
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/analyzers/calls-graph-analyzer/CallsGraphAnalyzer.spec.ts

# Run scope analyzer tests
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/analyzers/scope-analyzer/ScopeAnalyzer.spec.ts

# Run string array tests
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/custom-code-helpers/string-array/StringArrayCodeHelper.spec.ts

# Run self-defending code tests
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/custom-code-helpers/self-defending/SelfDefendingCodeHelper.spec.ts

Pattern Matching

Use glob patterns to run multiple related test files:

# Run all options-related tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/options/**/*.spec.ts"

# Run all analyzer tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/analyzers/**/*.spec.ts"

# Run all string array related tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/**/*StringArray*.spec.ts"

# Run all control flow tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/**/*ControlFlow*.spec.ts"

# Run all node transformer tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/node-transformers/**/*.spec.ts"

# Run all unit tests only
npx mocha --require ts-node/register --require source-map-support/register "test/unit-tests/**/*.spec.ts"

# Run all functional tests only
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/**/*.spec.ts"

Running Tests by Category

The test suite is organized into these main categories:

Functional Tests (test/functional-tests/):

# Options tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/options/**/*.spec.ts"

# Analyzers tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/analyzers/**/*.spec.ts"

# Node transformers tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/node-transformers/**/*.spec.ts"

# Code transformers tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/code-transformers/**/*.spec.ts"

# Custom code helpers tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/custom-code-helpers/**/*.spec.ts"

# Storage tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/storages/**/*.spec.ts"

# CLI tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/cli/**/*.spec.ts"

# Generator tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/generators/**/*.spec.ts"

# Main obfuscator tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/javascript-obfuscator/**/*.spec.ts"

# Issue regression tests
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/issues/**/*.spec.ts"

Unit Tests (test/unit-tests/):

# All unit tests
npx mocha --require ts-node/register --require source-map-support/register "test/unit-tests/**/*.spec.ts"

# Options unit tests
npx mocha --require ts-node/register --require source-map-support/register "test/unit-tests/options/**/*.spec.ts"

# Utils unit tests
npx mocha --require ts-node/register --require source-map-support/register "test/unit-tests/utils/**/*.spec.ts"

# Node utilities unit tests
npx mocha --require ts-node/register --require source-map-support/register "test/unit-tests/node/**/*.spec.ts"

Performance Tests (test/performance-tests/):

# Memory performance tests
npx mocha --require ts-node/register --require source-map-support/register test/performance-tests/JavaScriptObfuscatorMemory.spec.ts

Using Mocha Options with Individual Tests

# Run with grep to filter by test description
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/options/Options.spec.ts --grep "compact"

# Run and show slow tests
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/options/Options.spec.ts --reporter spec

# Run with timeout override (default is 10000ms)
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/options/Options.spec.ts --timeout 20000

# Run with bail (stop on first failure)
npx mocha --require ts-node/register --require source-map-support/register "test/functional-tests/**/*.spec.ts" --bail

# Run and watch for changes
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/options/Options.spec.ts --watch

# Run with specific reporter
npx mocha --require ts-node/register --require source-map-support/register test/functional-tests/options/Options.spec.ts --reporter json

Creating Test Aliases (Optional)

For convenience, you can add these aliases to your package.json scripts:

{
  "scripts": {
    "test:options": "mocha --require ts-node/register --require source-map-support/register 'test/functional-tests/options/**/*.spec.ts'",
    "test:analyzers": "mocha --require ts-node/register --require source-map-support/register 'test/functional-tests/analyzers/**/*.spec.ts'",
    "test:transformers": "mocha --require ts-node/register --require source-map-support/register 'test/functional-tests/node-transformers/**/*.spec.ts'",
    "test:unit": "mocha --require ts-node/register --require source-map-support/register 'test/unit-tests/**/*.spec.ts'",
    "test:functional": "mocha --require ts-node/register --require source-map-support/register 'test/functional-tests/**/*.spec.ts'"
  }
}

Then run with:

npm run test:options
npm run test:analyzers
npm run test:transformers

Tips for Running Individual Tests

  1. Use quotes around glob patterns to prevent shell expansion:

    # Good
    npx mocha "test/**/*.spec.ts"
    
    # Bad (shell will expand the pattern)
    npx mocha test/**/*.spec.ts
  2. Use --grep to run specific test cases within a file:

    npx mocha --require ts-node/register test/functional-tests/options/Options.spec.ts --grep "should enable compact"
  3. Use --bail to stop on first failure when debugging:

    npx mocha --require ts-node/register "test/**/*.spec.ts" --bail
  4. Check the exit code to verify test success in scripts:

    npx mocha --require ts-node/register test/functional-tests/options/Options.spec.ts && echo "Tests passed!"
  5. Combine with watch mode for TDD workflow:

    npx mocha --require ts-node/register test/functional-tests/options/Options.spec.ts --watch --reporter min

Linting

Running ESLint

Quick Start

# Lint all TypeScript files in src/
npm run eslint
yarn run eslint

This runs: eslint src/**/*.ts

Linting Individual Files

You can lint specific files or directories for faster feedback during development.

Basic Command Format:

npx eslint <path-to-file-or-directory>

Common Examples:

# Lint a specific file
npx eslint src/JavaScriptObfuscator.ts

# Lint the main facade file
npx eslint src/JavaScriptObfuscatorFacade.ts

# Lint a specific transformer
npx eslint src/node-transformers/converting-transformers/StringArrayTransformer.ts

# Lint a specific analyzer
npx eslint src/analyzers/calls-graph-analyzer/CallsGraphAnalyzer.ts

# Lint options file
npx eslint src/options/Options.ts

# Lint a custom code helper
npx eslint src/custom-code-helpers/string-array/StringArrayCodeHelper.ts

# Lint container files
npx eslint src/container/InversifyContainerFacade.ts

Linting Multiple Files or Directories

# Lint entire src directory
npx eslint src/

# Lint all files in a specific subdirectory
npx eslint src/node-transformers/

# Lint all analyzers
npx eslint src/analyzers/

# Lint all transformers
npx eslint src/node-transformers/**/*.ts

# Lint all options-related files
npx eslint src/options/

# Lint all custom code helpers
npx eslint src/custom-code-helpers/

# Lint all utils
npx eslint src/utils/

# Lint CLI files
npx eslint src/cli/

# Lint container modules
npx eslint src/container/

# Lint storage files
npx eslint src/storages/

Using Glob Patterns

# Lint all TypeScript files in src (same as npm run eslint)
npx eslint "src/**/*.ts"

# Lint all transformer files
npx eslint "src/**/*Transformer.ts"

# Lint all analyzer files
npx eslint "src/**/*Analyzer.ts"

# Lint all storage files
npx eslint "src/**/*Storage.ts"

# Lint all helper files
npx eslint "src/**/*Helper.ts"

# Lint all files containing "String" in the name
npx eslint "src/**/*String*.ts"

# Lint all files in node-transformers subdirectories
npx eslint "src/node-transformers/**/*.ts"

Auto-fixing Issues

ESLint can automatically fix many issues:

# Auto-fix all files in src/
npx eslint src/**/*.ts --fix

# Auto-fix a specific file
npx eslint src/JavaScriptObfuscator.ts --fix

# Auto-fix specific directory
npx eslint src/node-transformers/ --fix

# Auto-fix with glob pattern
npx eslint "src/analyzers/**/*.ts" --fix

# Auto-fix only safe fixes (no potentially breaking changes)
npx eslint src/JavaScriptObfuscator.ts --fix --fix-type suggestion,layout

Checking Specific Rules

# Show only errors (no warnings)
npx eslint src/JavaScriptObfuscator.ts --quiet

# Check specific rule only
npx eslint src/JavaScriptObfuscator.ts --rule 'no-console: error'

# Disable specific rules for a file check
npx eslint src/JavaScriptObfuscator.ts --rule 'no-console: off'

# Output format options
npx eslint src/JavaScriptObfuscator.ts --format stylish  # Default
npx eslint src/JavaScriptObfuscator.ts --format json     # JSON output
npx eslint src/JavaScriptObfuscator.ts --format compact  # Compact output
npx eslint src/JavaScriptObfuscator.ts --format unix     # Unix style

Getting Detailed Information

# Show more details about errors
npx eslint src/JavaScriptObfuscator.ts --format stylish

# List all files that would be linted (dry-run)
npx eslint src/ --debug 2>&1 | grep "Processing"

# Show timing information for rules
npx eslint src/JavaScriptObfuscator.ts --debug

# Get statistics about linting
npx eslint src/ --format json | jq '.[] | {file: .filePath, errors: .errorCount, warnings: .warningCount}'

Linting by Component

Organized by project structure:

Core Files:

npx eslint src/JavaScriptObfuscator.ts
npx eslint src/JavaScriptObfuscatorFacade.ts
npx eslint src/ASTParserFacade.ts

Node Transformers:

# All node transformers
npx eslint src/node-transformers/

# Converting transformers
npx eslint src/node-transformers/converting-transformers/

# Control flow transformers
npx eslint src/node-transformers/control-flow-transformers/

# String array transformers
npx eslint src/node-transformers/string-array-transformers/

# Rename transformers
npx eslint src/node-transformers/rename-identifiers-transformers/
npx eslint src/node-transformers/rename-properties-transformers/

Analyzers:

# All analyzers
npx eslint src/analyzers/

# Specific analyzers
npx eslint src/analyzers/calls-graph-analyzer/
npx eslint src/analyzers/scope-analyzer/
npx eslint src/analyzers/string-array-storage-analyzer/

Options System:

# All options files
npx eslint src/options/

# Core options
npx eslint src/options/Options.ts
npx eslint src/options/OptionsNormalizer.ts

# Validators
npx eslint src/options/validators/

# Presets
npx eslint src/options/presets/

Custom Code Helpers:

# All helpers
npx eslint src/custom-code-helpers/

# String array helpers
npx eslint src/custom-code-helpers/string-array/

# Debug protection helpers
npx eslint src/custom-code-helpers/debug-protection/

# Self-defending helpers
npx eslint src/custom-code-helpers/self-defending/

Utilities:

# All utils
npx eslint src/utils/

# Specific utils
npx eslint src/utils/RandomGenerator.ts
npx eslint src/utils/ArrayUtils.ts
npx eslint src/utils/CryptUtils.ts

Integrating with Git

# Lint only staged files (useful for pre-commit)
git diff --cached --name-only --diff-filter=ACM | grep '\.ts$' | xargs npx eslint

# Lint files changed in current branch
git diff --name-only master | grep '\.ts$' | xargs npx eslint

# Lint files changed in last commit
git diff HEAD~1 --name-only | grep '\.ts$' | xargs npx eslint

Creating Lint Aliases (Optional)

Add these to your package.json scripts for convenience:

{
  "scripts": {
    "lint": "eslint src/**/*.ts",
    "lint:fix": "eslint src/**/*.ts --fix",
    "lint:transformers": "eslint src/node-transformers/**/*.ts",
    "lint:analyzers": "eslint src/analyzers/**/*.ts",
    "lint:options": "eslint src/options/**/*.ts",
    "lint:utils": "eslint src/utils/**/*.ts",
    "lint:quiet": "eslint src/**/*.ts --quiet",
    "lint:staged": "git diff --cached --name-only --diff-filter=ACM | grep '\\.ts$' | xargs eslint"
  }
}

Then run with:

npm run lint:transformers
npm run lint:analyzers
npm run lint:fix

ESLint Configuration

Location: .eslintrc.js

The project uses:

  • @typescript-eslint: TypeScript-specific linting rules
  • eslint-plugin-import: Import/export validation
  • eslint-plugin-jsdoc: JSDoc comment validation
  • eslint-plugin-no-null: Prevents null usage (prefer undefined)
  • eslint-plugin-prefer-arrow: Enforces arrow functions
  • eslint-plugin-unicorn: Additional code quality rules

Ignored files: .eslintignore

Viewing Current ESLint Config

# Print effective configuration for a file
npx eslint --print-config src/JavaScriptObfuscator.ts

# List all rules being applied
npx eslint --print-config src/JavaScriptObfuscator.ts | grep rules -A 1000

Code Quality Checks

# Run full build (includes webpack, eslint, and tests)
npm run build
yarn run build

# The build script runs:
# 1. webpack:prod (production build)
# 2. eslint (linting)
# 3. test (full test suite)

Tips for Effective Linting

  1. Lint before committing: Always run linting before creating commits

    npx eslint src/ && git commit -m "Your message"
  2. Use --fix cautiously: Review changes before committing auto-fixes

    npx eslint src/MyFile.ts --fix
    git diff  # Review changes
  3. Focus on errors first: Use --quiet to see only errors

    npx eslint src/ --quiet
  4. Lint specific files during development: Don't lint everything when working on one file

    npx eslint src/node-transformers/MyNewTransformer.ts
  5. Check exit code: Useful in scripts and CI/CD

    npx eslint src/ || echo "Linting failed!"

Development Workflow

Setting Up Development Environment

# 1. Clone the repository
git clone https://github.com/javascript-obfuscator/javascript-obfuscator.git
cd javascript-obfuscator

# 2. Install dependencies
npm install
# or
yarn install

# 3. Install Husky hooks (for pre-commit checks)
npm run prepare
# or
yarn run prepare

Development Commands

# Start development mode with watch (auto-recompile on changes)
npm start
# or
npm run watch
# or
yarn run watch

# Build for production
npm run webpack:prod
yarn run webpack:prod

# Build TypeScript type definitions
npm run build:typings
yarn run build:typings

# Full build (webpack + eslint + tests)
npm run build
yarn run build

Pre-commit Hooks

The project uses Husky for git hooks:

  • pre-commit: Automatically runs npm run build before each commit
    • Ensures code compiles
    • Ensures linting passes
    • Ensures all tests pass

Configuration: .husky/ directory

Development Tips

  1. Use watch mode during development:

    npm run watch

    This rebuilds automatically when you save files.

  2. Run specific tests during development:

    npm run test:dev

    Faster than full test suite.

  3. Check linting before committing:

    npm run eslint

    Fix issues before the pre-commit hook runs.

  4. Test memory usage:

    npm run test:mocha-memory-performance

    Ensure your changes don't cause memory issues.

  5. Generate coverage reports:

    npm run test:mocha-coverage
    npm run test:mocha-coverage:report

    Check test coverage in the generated coverage/ directory.

Performance Considerations

Impact on Code Size

  • Default: ~15-30% increase
  • Dead Code Injection: Up to 200% increase
  • String Array: 20-50% increase
  • Control Flow Flattening: 30-80% increase

Runtime Performance

  • No obfuscation: Baseline
  • Low preset: ~10-20% slower
  • Medium preset: ~30-50% slower
  • High preset: ~50-80% slower

Optimization Tips

  1. Use thresholds to apply transformations selectively:

    • controlFlowFlatteningThreshold
    • deadCodeInjectionThreshold
    • stringArrayThreshold
  2. Avoid obfuscating:

    • Third-party libraries
    • Polyfills
    • Large vendor bundles
  3. Use seed option for reproducible builds

  4. Enable simplify for better performance (enabled by default)

Security Considerations

What It Protects

  • Makes reverse engineering harder
  • Prevents casual code inspection
  • Protects string literals and algorithms
  • Adds anti-debugging measures
  • Can lock code to specific domains

What It Doesn't Protect

  • Determined attackers with time and tools
  • Network traffic and API endpoints
  • Runtime behavior analysis
  • Secrets embedded in code (use environment variables!)

Best Practices

  1. Never obfuscate secrets: Use environment variables or secure vaults
  2. Combine with other protections: Minification, HTTPS, CSP headers
  3. Test thoroughly: Obfuscation can introduce subtle bugs
  4. Monitor performance: High obfuscation impacts runtime speed
  5. Use source maps carefully: Keep them private for debugging

Conditional Comments

Control obfuscation for specific code sections:

var foo = 1;
// javascript-obfuscator:disable
var bar = 2; // This won't be obfuscated
// javascript-obfuscator:enable
var baz = 3;

Integration with Build Tools

Webpack

Use webpack-obfuscator plugin

Gulp

Use gulp-javascript-obfuscator

Rollup

Use rollup-plugin-javascript-obfuscator

Grunt

Use grunt-contrib-obfuscator

Common Issues and Solutions

Issue: Code breaks after obfuscation

Solutions:

  • Add function/variable names to reservedNames
  • Add strings to reservedStrings
  • Use renamePropertiesMode: 'safe' instead of 'unsafe'
  • Disable renameProperties if safe mode doesn't work
  • Check for dynamic property access like obj[dynamicKey]

Issue: Performance is too slow

Solutions:

  • Use lower obfuscation preset
  • Reduce threshold values
  • Disable controlFlowFlattening and deadCodeInjection
  • Use target: 'browser-no-eval' if applicable

Issue: Code size is too large

Solutions:

  • Disable deadCodeInjection
  • Reduce stringArrayWrappersCount
  • Use lower stringArrayThreshold
  • Disable unicodeEscapeSequence

Issue: Source maps not working

Solutions:

  • Ensure sourceMap: true in options
  • Set correct sourceMapMode ('inline' or 'separate')
  • Specify inputFileName when using NodeJS API
  • Use sourceMapSourcesMode: 'sources-content' for embedded source

Issue: Domain lock not working

Solutions:

  • Don't use with target: 'node'
  • Test in actual browser environment
  • Check domain format (.example.com for all subdomains)
  • Ensure domainLockRedirectUrl is set

Extension Points

Adding Custom Transformers

  1. Create transformer class extending AbstractNodeTransformer
  2. Implement getVisitor() and transformNode() methods
  3. Register in appropriate module (src/container/modules/node-transformers/)
  4. Add to transformer list in JavaScriptObfuscator.ts
  5. Add to NodeTransformer enum

Adding Custom Options

  1. Add property to IOptions interface
  2. Add validation decorator in Options.ts
  3. Add normalizer rule if needed in options/normalizer-rules/
  4. Add preset values if applicable

Adding Custom Code Helpers

  1. Create helper group extending AbstractCustomCodeHelperGroup
  2. Create template files in custom-code-helpers/[group]/templates/
  3. Register in CustomCodeHelpersModule
  4. Add to CustomCodeHelper enum

TypeScript Configuration

Main Config

Location: tsconfig.json

  • Target: ES2018
  • Module: CommonJS
  • Strict mode: Enabled
  • Decorators: Enabled (experimental)
  • Emit decorator metadata: Enabled

Special Configs

  • tsconfig.browser.json: Browser-specific settings
  • tsconfig.node.json: Node.js-specific settings
  • tsconfig.typings.json: Type declarations generation

Dependencies Overview

Production Dependencies

  • @javascript-obfuscator/escodegen: Modified escodegen for code generation
  • @javascript-obfuscator/estraverse: Modified estraverse for AST traversal
  • acorn: JavaScript parser (ES3-ES2020)
  • inversify: Dependency injection container
  • eslint-scope: Scope analysis (from ESLint)
  • class-validator: Options validation
  • chance: Random data generation
  • commander: CLI argument parsing
  • chalk: Terminal colors
  • md5: Hashing for identifiers

Development Dependencies

  • TypeScript: Type system and compiler
  • Webpack: Module bundler
  • Mocha + Chai: Testing framework
  • NYC: Code coverage
  • ESLint: Code linting
  • Sinon: Test mocking

Contributing

Location: CONTRIBUTING.md, CODE_OF_CONDUCT.md

  1. Fork the repository
  2. Create feature branch
  3. Write tests for new features
  4. Ensure all tests pass
  5. Follow existing code style (ESLint)
  6. Submit pull request

Versioning and Releases

  • Follows semantic versioning (SemVer)
  • Changelog maintained in CHANGELOG.md
  • Precommit hooks run build and tests (Husky)
  • Automated CI/CD via GitHub Actions

Support and Community

  • GitHub Issues: Bug reports and feature requests
  • GitHub Discussions: Questions and general discussion
  • GitHub Sponsors: Direct sponsorship

License

BSD-2-Clause License

Copyright (C) 2016-2024 Timofey Kachalov

See LICENSE.BSD for full license text.

Project Statistics

  • First Release: 2016
  • Language: TypeScript (~90% of codebase)
  • Test Coverage: Extensive functional and unit test suite
  • Supported JavaScript Versions: ES3, ES5, ES2015-ES2019, partial ES2020
  • Downloads: Widely used in production applications
  • Maintenance: Actively maintained

Resources


Quick Reference: File Locations

Component Primary Location
Main Obfuscator src/JavaScriptObfuscator.ts
Public API src/JavaScriptObfuscatorFacade.ts
CLI bin/javascript-obfuscator, src/JavaScriptObfuscatorCLIFacade.ts
Options src/options/Options.ts
Transformers src/node-transformers/
Analyzers src/analyzers/
DI Container src/container/InversifyContainerFacade.ts
Tests test/
Build Config webpack/
Distribution dist/

Quick Reference: Key Enums

  • CodeTransformationStage: PreparingTransformers, FinalizingTransformers
  • NodeTransformationStage: Initializing, Preparing, DeadCodeInjection, ControlFlowFlattening, RenameProperties, Converting, RenameIdentifiers, StringArray, Simplifying, Finalizing
  • OptionsPreset: default, low-obfuscation, medium-obfuscation, high-obfuscation
  • StringArrayEncoding: none, base64, rc4
  • IdentifierNamesGenerator: hexadecimal, mangled, mangled-shuffled, dictionary
  • RenamePropertiesMode: safe, unsafe
  • Target: browser, browser-no-eval, node