js-deobfuscator 2.0.0

Universal JavaScript deobfuscator built on OXC
Documentation
//! JS-Deobfuscator CLI.

use std::io::Read;

use clap::Parser;

use js_deobfuscator::engine::api::JSDeobfuscator;

#[derive(Parser)]
#[command(name = "deob", about = "Universal JavaScript deobfuscator")]
struct Cli {
    /// Input file (reads from stdin if omitted)
    input: Option<String>,

    /// Output file (writes to stdout if omitted)
    #[arg(short, long)]
    output: Option<String>,

    /// Maximum convergence iterations
    #[arg(short, long, default_value = "50")]
    max_iterations: usize,

    /// Disable static expression folding
    #[arg(long)]
    no_fold: bool,

    /// Disable semantic transforms
    #[arg(long)]
    no_transforms: bool,

    /// Global values as JSON (e.g., '{"window":{"key":"value"}}')
    #[arg(short, long)]
    globals: Option<String>,

    /// Print statistics after deobfuscation
    #[arg(short, long)]
    stats: bool,
}

fn main() {
    let cli = Cli::parse();

    // Read input
    let source = match &cli.input {
        Some(path) => std::fs::read_to_string(path).unwrap_or_else(|e| {
            eprintln!("error reading {path}: {e}");
            std::process::exit(1);
        }),
        None => {
            let mut buf = String::new();
            std::io::stdin().read_to_string(&mut buf).unwrap_or_else(|e| {
                eprintln!("error reading stdin: {e}");
                std::process::exit(1);
            });
            buf
        }
    };

    // Build deobfuscator
    let mut deob = JSDeobfuscator::new()
        .max_iterations(cli.max_iterations)
        .static_eval(!cli.no_fold)
        .transforms(!cli.no_transforms);

    if let Some(globals_json) = &cli.globals {
        match serde_json::from_str::<serde_json::Value>(globals_json) {
            Ok(val) => {
                if let Some(obj) = val.as_object() {
                    for (k, v) in obj {
                        deob = deob.global(k, v.clone());
                    }
                }
            }
            Err(e) => {
                eprintln!("error parsing --globals JSON: {e}");
                std::process::exit(1);
            }
        }
    }

    // Run
    let result = match deob.deobfuscate(&source) {
        Ok(r) => r,
        Err(e) => {
            eprintln!("error: {e}");
            std::process::exit(1);
        }
    };

    // Output
    match &cli.output {
        Some(path) => std::fs::write(path, &result.code).unwrap_or_else(|e| {
            eprintln!("error writing {path}: {e}");
            std::process::exit(1);
        }),
        None => print!("{}", result.code),
    }

    // Stats
    if cli.stats {
        eprintln!(
            "iterations: {}, modifications: {}, converged: {}",
            result.iterations, result.modifications, result.converged
        );
    }
}