diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a64df512..930f6ca0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: '**/node_modules' key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} diff --git a/.gitignore b/.gitignore index b79ba16a..cfe4fd23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.claude .DS_Store .idea .nyc_output diff --git a/.npmignore b/.npmignore index 217c0685..d202d73b 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ .awcache +.claude .github .idea .nyc_output diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..ae03f9f1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,1478 @@ +# 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. + +- **Version**: 4.1.1 +- **Author**: Timofey Kachalov (@sanex3339) +- **License**: BSD-2-Clause +- **Repository**: https://github.com/javascript-obfuscator/javascript-obfuscator +- **Homepage**: https://obfuscator.io/ +- **Node Requirement**: >=12.22.0 + +## 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: + +```typescript +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: + +```typescript +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 + +```bash +# 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 + +```javascript +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 + +```javascript +const sourceCodesObject = { + 'file1.js': 'var foo = 1;', + 'file2.js': 'var bar = 2;' +}; + +const obfuscationResults = JavaScriptObfuscator.obfuscateMultiple( + sourceCodesObject, + options +); +``` + +### Identifier Names Cache (Cross-File Consistency) + +```javascript +// 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: + +```html + + +``` + +**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 + +```bash +# 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 + +```bash +# 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 + +```bash +# 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 + +```bash +npx mocha --require ts-node/register --require source-map-support/register +``` + +#### Common Examples + +```bash +# 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: + +```bash +# 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/`): +```bash +# 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/`): +```bash +# 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/`): +```bash +# 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 + +```bash +# 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: + +```json +{ + "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: +```bash +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: + ```bash + # 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: + ```bash + 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: + ```bash + npx mocha --require ts-node/register "test/**/*.spec.ts" --bail + ``` + +4. **Check the exit code** to verify test success in scripts: + ```bash + npx mocha --require ts-node/register test/functional-tests/options/Options.spec.ts && echo "Tests passed!" + ``` + +5. **Combine with watch mode** for TDD workflow: + ```bash + npx mocha --require ts-node/register test/functional-tests/options/Options.spec.ts --watch --reporter min + ``` + +## Linting + +### Running ESLint + +#### Quick Start + +```bash +# 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:** +```bash +npx eslint +``` + +**Common Examples:** + +```bash +# 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 + +```bash +# 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 + +```bash +# 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: + +```bash +# 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 + +```bash +# 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 + +```bash +# 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:** +```bash +npx eslint src/JavaScriptObfuscator.ts +npx eslint src/JavaScriptObfuscatorFacade.ts +npx eslint src/ASTParserFacade.ts +``` + +**Node Transformers:** +```bash +# 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:** +```bash +# 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:** +```bash +# 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:** +```bash +# 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:** +```bash +# 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 + +```bash +# 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: + +```json +{ + "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: +```bash +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 + +```bash +# 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 + +```bash +# 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 + ```bash + npx eslint src/ && git commit -m "Your message" + ``` + +2. **Use --fix cautiously**: Review changes before committing auto-fixes + ```bash + npx eslint src/MyFile.ts --fix + git diff # Review changes + ``` + +3. **Focus on errors first**: Use `--quiet` to see only errors + ```bash + npx eslint src/ --quiet + ``` + +4. **Lint specific files during development**: Don't lint everything when working on one file + ```bash + npx eslint src/node-transformers/MyNewTransformer.ts + ``` + +5. **Check exit code**: Useful in scripts and CI/CD + ```bash + npx eslint src/ || echo "Linting failed!" + ``` + +## Development Workflow + +### Setting Up Development Environment + +```bash +# 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 + +```bash +# 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**: + ```bash + npm run watch + ``` + This rebuilds automatically when you save files. + +2. **Run specific tests during development**: + ```bash + npm run test:dev + ``` + Faster than full test suite. + +3. **Check linting before committing**: + ```bash + npm run eslint + ``` + Fix issues before the pre-commit hook runs. + +4. **Test memory usage**: + ```bash + npm run test:mocha-memory-performance + ``` + Ensure your changes don't cause memory issues. + +5. **Generate coverage reports**: + ```bash + 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: + +```javascript +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](https://github.com/javascript-obfuscator/webpack-obfuscator) plugin + +### Gulp + +Use [gulp-javascript-obfuscator](https://github.com/javascript-obfuscator/gulp-javascript-obfuscator) + +### Rollup + +Use [rollup-plugin-javascript-obfuscator](https://github.com/javascript-obfuscator/rollup-plugin-javascript-obfuscator) + +### Grunt + +Use [grunt-contrib-obfuscator](https://github.com/javascript-obfuscator/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 +- **OpenCollective**: Financial support and sponsorship +- **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 + +- **Main Repository**: https://github.com/javascript-obfuscator/javascript-obfuscator +- **Online Tool**: https://obfuscator.io +- **NPM Package**: https://www.npmjs.com/package/javascript-obfuscator +- **Documentation**: In README.md and inline code comments + +--- + +## 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