ConstasJ/cascade-js

An implementation of CASCADE mentioned in google's ArXiv paper

★ 0Forks 0TypeScriptGitHub ↗Compare

README

cascade-js

A full-featured JavaScript deobfuscator powered by AST transforms and LLM-assisted analysis.

cascade-js reverses JavaScript obfuscation, targeting patterns generated by javascript-obfuscator and similar tools. It combines pure AST-based transformations with optional LLM-assisted analysis for complex prelude detection. The LLM is optional, many passes work entirely via AST analysis without any API calls.

Features

cascade-js includes 19 transformation passes organized into five categories:

String Array

Pass Description
string-array Pure AST detection of string array patterns with fallback to LLM-assisted analysis for complex variants
string-array-sandbox Safe execution of string array rotation functions using QuickJS WASM sandbox
string-array-replace Replaces encoded string accesses with decoded literal values

Protection Removal

Pass Description
self-defending Removes self-defending wrappers that prevent code formatting
debug-protection Strips debugger detection and infinite loops designed to thwart debugging
console-output Removes console output tampering and redirection
domain-lock Eliminates domain and URL-based restrictions

Transform

Pass Description
control-flow-flattening Restores flattened control flow from switch-based and object-based CFF patterns
dead-code-removal Eliminates injected dead code blocks that never execute
object-keys Converts computed object keys back to literal property names
split-strings Concatenates split string literals into single strings
unicode-escape Converts Unicode escape sequences to readable characters
numbers-to-expressions Evaluates numeric expressions back to literal values

Propagation

Pass Description
string-replacement Propagates decoded string literals throughout the code
constant-propagation Evaluates and propagates constant expressions at compile time
function-inlining Inlines simple functions to reduce indirection

Cleanup

Pass Description
boolean-literals Normalizes boolean literal expressions to true/false
cleanup Removes unused variables, dead branches, and empty functions
unminify Applies 16 sub-transforms to restore readable formatting: brace spacing, semicolon insertion, quote normalization, indentation, and more

Installation

npm install cascade-js
pnpm add cascade-js

For global CLI access:

npm install -g cascade-js

Requirements: Node.js >= 18 (ES2022 target)

Quick Start

CLI

# Basic usage
cascade-js input.js output.js

# Read from stdin, write to stdout
cat obfuscated.js | cascade-js - -

# Specify LLM provider
cascade-js input.js output.js --provider openai --model gpt-4o-mini

# JSON output for programmatic use
cascade-js input.js --json

API

import { deobfuscate } from 'cascade-js';

const obfuscated = `
  var _0x1234 = ['Hello', 'World'];
  function _0x5678(n) { return _0x1234[n]; }
  console.log(_0x5678(0) + ' ' + _0x5678(1));
`;

const result = await deobfuscate(obfuscated);
console.log(result.code);
// console.log('Hello World');

CLI Usage

The CLI provides a convenient way to deobfuscate files from the command line. For complete documentation, see docs/cli.md.

cascade-js [input] [output] [options]

Arguments:

  • input - Input file path (use - for stdin)
  • output - Output file path (use - for stdout, omit to print to stdout)

Options:

  • --provider <name> - LLM provider: openai, anthropic, gemini, ollama (default: openai)
  • --model <model> - Model name (provider-specific default)
  • --api-key <key> - API key (falls back to CASCADE_API_KEY env var)
  • --base-url <url> - Custom API base URL for compatible providers
  • --timeout <ms> - Timeout in milliseconds (default: 60000)
  • --verbose - Enable verbose logging
  • --quiet - Suppress all output except result
  • --no-prefilter - Skip obfuscation detection, process all input
  • --json - Output results as JSON

Examples:

# Basic file processing
cascade-js obfuscated.js cleaned.js

# Stdin/stdout piping
curl -s https://example.com/script.js | cascade-js - - > cleaned.js

# Using Anthropic with custom timeout
cascade-js input.js output.js --provider anthropic --timeout 120000

# JSON output for scripting
cascade-js input.js --json | jq '.code'

API Usage

The API provides programmatic access to the deobfuscation engine. For complete documentation, see docs/api.md.

Basic Usage

import { deobfuscate } from 'cascade-js';

const result = await deobfuscate(obfuscatedCode);
console.log(result.code);      // Deobfuscated code
console.log(result.warnings);  // Any warnings generated
console.log(result.stats);     // Processing statistics

Custom Pipeline

import { createPipeline, stringArrayPass, controlFlowFlatteningPass, unminifyPass } from 'cascade-js';

const pipeline = createPipeline({
  options: { timeout: 120000 },
  passes: [stringArrayPass, controlFlowFlatteningPass, unminifyPass]
});

const result = await pipeline.run(code);

Custom Passes

import { deobfuscate, definePass } from 'cascade-js';

const myPass = definePass({
  name: 'my-custom-pass',
  dependencies: ['string-replacement'],
  async transform(code, context) {
    // Your transformation logic
    return transformedCode;
  }
});

const result = await deobfuscate(code, {
  customPasses: [myPass]
});

Supported Obfuscation Techniques

cascade-js targets patterns generated by popular obfuscation tools. This table maps javascript-obfuscator options to cascade-js passes:

javascript-obfuscator Option cascade-js Pass(es)
stringArray string-array, string-array-sandbox, string-array-replace
stringArrayRotate string-array-sandbox
stringArrayShuffle string-array-sandbox
controlFlowFlattening control-flow-flattening
deadCodeInjection dead-code-removal
debugProtection debug-protection
selfDefending self-defending
disableConsoleOutput console-output
domainLock domain-lock
splitStrings split-strings
unicodeEscapeSequence unicode-escape
numbersToExpressions numbers-to-expressions
transformObjectKeys object-keys
simplify constant-propagation, function-inlining

Architecture

cascade-js uses a multi-stage pipeline architecture:

Input: Obfuscated JavaScript
         |
         v
  +--------------+
  |  Prefilter   |  --> Quick heuristic detection of obfuscation patterns
  +--------------+
         |
         v
  +--------------+
  |Prelude Detect|  --> LLM-assisted or pure-AST identification of string arrays,
  +--------------+      rotation functions, and fetcher functions
         |
         v
  +--------------+
  |    Sandbox   |  --> Safe execution in QuickJS WASM to extract decoded strings
  +--------------+
         |
         v
  +--------------+
  |Pass Pipeline |  --> Topological sort on pass dependencies, then sequential
  +--------------+      application of AST transformations
         |
         v
Output: Deobfuscated JavaScript

Key Components:

  1. Prefilter - Pattern-based detection using hex identifier counting, string array matching, and confidence scoring

  2. Prelude Detection - Identifies obfuscation prelude structures (string arrays, fetchers, rotators) using either pure AST analysis or LLM-assisted parsing

  3. Sandbox - QuickJS-emscripten isolation for safe execution of untrusted rotation functions without affecting the host environment

  4. Pipeline - Dependency-resolving pass executor that runs transformations in topological order based on declared dependencies

LLM Providers

cascade-js supports multiple LLM providers for complex prelude detection. The LLM is optional and only used when pure AST detection fails to identify obfuscation patterns.

Provider Default Model Environment Variable Config Options
OpenAI gpt-4o-mini OPENAI_API_KEY model, baseURL
Anthropic claude-3-haiku-20240307 ANTHROPIC_API_KEY model, baseURL
Gemini gemini-1.5-flash GOOGLE_API_KEY model, baseURL
Ollama llama3.2 None (local) model, host

Global API Key: You can also set CASCADE_API_KEY as a fallback for any provider.

Example Configuration:

import { deobfuscate, OpenAILLMAdapter } from 'cascade-js';

// Using environment variable
const result = await deobfuscate(code);

// Explicit adapter configuration
const result = await deobfuscate(code, {
  llmAdapter: new OpenAILLMAdapter(process.env.OPENAI_API_KEY, 'gpt-4o')
});

// Using Anthropic
import { AnthropicLLMAdapter } from 'cascade-js';
const result = await deobfuscate(code, {
  llmAdapter: new AnthropicLLMAdapter(process.env.ANTHROPIC_API_KEY)
});

Contributing

Contributions are welcome. See docs/contributing.md for guidelines on setting up the development environment, running tests, and submitting pull requests.

Quick start for contributors:

git clone https://github.com/yourusername/cascade-js.git
cd cascade-js
pnpm install
pnpm build
pnpm test

License

MIT License - see LICENSE for details.

Contributors

ConstasJ

Issues