You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
controlFlowFlattening rewrites let/const to var inside functions passed to wrapped calls #1471
Obfuscating with controlFlowFlattening: true should preserve let and const declarations inside functions or arrow functions passed as arguments to function calls. In the example below, the obfuscated code should print 0,1,2, matching the original code where let j = i creates a fresh block-scoped binding on each loop iteration.
Current Behavior
Enabling controlFlowFlattening rewrites let and const declarations to var inside function or arrow arguments passed to wrapped calls whenever var is the most frequent declaration kind in the input. In the example below, the obfuscated code prints 2,2,2 instead of 0,1,2 because let j = i inside the callback is converted to var, collapsing all loop iterations onto a single function-scoped binding.
This appears to happen because CallExpressionControlFlowStorageCallNode embeds the user argument AST into its custom node structure, and CustomCodeHelperFormatter.formatStructure then rewrites every VariableDeclaration in the subtree to kind = 'var' when PrevailingKindOfVariablesAnalyzer selects var.
Steps to Reproduce
Obfuscate a script where var is the prevailing variable declaration kind and a function/arrow callback containing a per-iteration let binding in a loop is passed as an argument to a call expression, with controlFlowFlattening: true and controlFlowFlatteningThreshold: 1.
Execute the obfuscated code.
Observe that let inside the callback was rewritten to var, changing the output from 0,1,2 to 2,2,2.
JavaScript Obfuscator Edition
JavaScript Obfuscator Open Source
Your Environment
Obfuscator version used: 5.10.0
Node version used: v24.14.1
Stack trace
N/A (wrong runtime output, no exception thrown)
Minimal working example that will help to reproduce issue
constJavaScriptObfuscator=require('javascript-obfuscator');constcode=`function main() { var run = (f) => f(); return run(() => { var fs = []; for (var i = 0; i < 3; i++) { let j = i; fs.push(() => j); } return fs.map((g) => g()).join(); });}console.log(main());`;constout=JavaScriptObfuscator.obfuscate(code,{controlFlowFlattening: true,controlFlowFlatteningThreshold: 1}).getObfuscatedCode();eval(out);
Activity
sanex3339 commentedon Nov 12, 2026
Released as 5.10.1