mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Fixed controlFlowFlattening occasionally dropping the ?. short-circuit on foo?.(arg) calls, causing TypeError: <X> is not a function
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
Change Log
|
Change Log
|
||||||
|
|
||||||
|
v5.4.3
|
||||||
|
---
|
||||||
|
* Fixed `controlFlowFlattening` occasionally dropping the `?.` short-circuit on `foo?.(arg)` calls, causing `TypeError: <X> is not a function`. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1408
|
||||||
|
|
||||||
v5.4.2
|
v5.4.2
|
||||||
---
|
---
|
||||||
* Fixed obfuscated code hanging in Bun when `selfDefending` is enabled. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1404
|
* Fixed obfuscated code hanging in Bun when `selfDefending` is enabled. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1404
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "javascript-obfuscator",
|
"name": "javascript-obfuscator",
|
||||||
"version": "5.4.2",
|
"version": "5.4.3",
|
||||||
"description": "JavaScript obfuscator",
|
"description": "JavaScript obfuscator",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"obfuscator",
|
"obfuscator",
|
||||||
|
|||||||
+5
-1
@@ -64,7 +64,11 @@ export class CallExpressionControlFlowReplacer extends AbstractControlFlowReplac
|
|||||||
|
|
||||||
const isChainExpressionParent = NodeGuards.isChainExpressionNode(parentNode);
|
const isChainExpressionParent = NodeGuards.isChainExpressionNode(parentNode);
|
||||||
|
|
||||||
const replacerId: number = callExpressionNode.arguments.length;
|
// Bucket reuse-eligible wrappers by both arg count AND optional-ness, so an
|
||||||
|
// optional `foo?.(arg)` call never reuses the wrapper of a non-optional `bar(arg)`
|
||||||
|
// that happens to share `arguments.length` — which would drop the `?.` short-circuit
|
||||||
|
// and crash on undefined callees (issue #1408).
|
||||||
|
const replacerId: string = `${callExpressionNode.arguments.length}-${isChainExpressionParent ? 'optional' : 'standard'}`;
|
||||||
const callExpressionFunctionCustomNode: ICustomNode<TInitialData<CallExpressionFunctionNode>> =
|
const callExpressionFunctionCustomNode: ICustomNode<TInitialData<CallExpressionFunctionNode>> =
|
||||||
this.controlFlowCustomNodeFactory(ControlFlowCustomNode.CallExpressionFunctionNode);
|
this.controlFlowCustomNodeFactory(ControlFlowCustomNode.CallExpressionFunctionNode);
|
||||||
const expressionArguments: (ESTree.Expression | ESTree.SpreadElement)[] = callExpressionNode.arguments;
|
const expressionArguments: (ESTree.Expression | ESTree.SpreadElement)[] = callExpressionNode.arguments;
|
||||||
|
|||||||
+28
@@ -248,5 +248,33 @@ describe('CallExpressionControlFlowReplacer', function () {
|
|||||||
assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
|
assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Variant #8 - optional `?.()` call must not reuse a non-optional wrapper (issue #1408)', () => {
|
||||||
|
const samplesCount: number = 200;
|
||||||
|
|
||||||
|
it('should preserve the `?.` short-circuit on every obfuscation when an optional call and a non-optional call share the same arity', () => {
|
||||||
|
const code: string = readFileAsString(
|
||||||
|
__dirname + '/fixtures/issue-1408-mixed-optional-and-plain-calls.js'
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < samplesCount; i++) {
|
||||||
|
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(code, {
|
||||||
|
...NO_ADDITIONAL_NODES_PRESET,
|
||||||
|
controlFlowFlattening: true,
|
||||||
|
controlFlowFlatteningThreshold: 1,
|
||||||
|
deadCodeInjection: true,
|
||||||
|
deadCodeInjectionThreshold: 1
|
||||||
|
}).getObfuscatedCode();
|
||||||
|
|
||||||
|
const result: unknown = eval(obfuscatedCode);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
result,
|
||||||
|
'ok',
|
||||||
|
`iteration ${i}: optional ?.() call lost its short-circuit (obfuscated code threw or returned wrong value)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
(function () {
|
||||||
|
function noise (n) {
|
||||||
|
var acc = 0;
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
acc += i;
|
||||||
|
} else {
|
||||||
|
acc ^= i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bump (v) {
|
||||||
|
return v + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function outer () {
|
||||||
|
var captured = undefined;
|
||||||
|
var inner = function (msg) {
|
||||||
|
captured?.(msg);
|
||||||
|
return 'ok';
|
||||||
|
};
|
||||||
|
return inner('payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
noise(50);
|
||||||
|
bump(7);
|
||||||
|
noise(25);
|
||||||
|
bump(13);
|
||||||
|
return outer();
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user