UNPKG

javascript-obfuscator

Version:
1,478 lines (1,108 loc) 44.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. - **Version**: 5.0.0 - **Author**: Timofey Kachalov (@sanex3339) - **License**: BSD-2-Clause - **Repository**: https://github.com/javascript-obfuscator/javascript-obfuscator - **Homepage**: https://obfuscator.io/ - **Node Requirement**: >=18.0.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 <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 ```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 <path-to-test-file> ``` #### 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 <path-to-file-or-directory> ``` **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 - **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