I have a small Node.js application that tries to deobfuscate a dataset of Javascript source code using synchrony. The synchrony.deobfuscateNode(ast) function returns a promise, thus I implemented a Promise.race in order to have the program returning in case the deobfuscation takes too long:
const { Deobfuscator } = require('./synchrony');
const { timeout } = require('../../../config');
const { parse } = require('../../utils/parser');
async function callDeobfuscator(ast, obfuscator) {
const withTimeout = (promise) => {
let timeoutPid;
const timeoutPromise = new Promise((resolve, reject) =>
timeoutPid = setTimeout(reject, timeout));
return Promise.race([
promise,
timeoutPromise
]).finally(() => {
if (timeoutPid) {
clearTimeout(timeoutPid);
}
});
};
const synchrony = new Deobfuscator();
return await withTimeout(synchrony.deobfuscateNode(ast));
};
const fs = require('fs');
fs.readFile('script.js', 'utf8', async function(err, source){
let ast = parse(source);
ast = await callDeobfuscator(ast);
console.log(ast);
});
However, whenever callDeobfuscator takes longer than the timeout promise, no exception is thrown (timeout promise doesn´t reject) and the program waits for callDeobfuscator to resolve anyway. I tried replacing the callDeobfuscator logic with another timeout promise (testPromise):
async function callDeobfuscator(ast, obfuscator) {
const withTimeout = (promise) => {
let timeoutPid;
const timeoutPromise = new Promise((resolve, reject) =>
timeoutPid = setTimeout(reject, timeout));
return Promise.race([
promise,
timeoutPromise
]).finally(() => {
if (timeoutPid) {
clearTimeout(timeoutPid);
}
});
};
const testPromise = new Promise((resolve, reject) => {
setTimeout(resolve, 1000000000, 'two');
});
return await withTimeout(testPromise);
};
and it now works as intended, so I'm guessing there must be something wrong with the callDeobfuscator promise that I can´t grasp. Any insights?
I quickly went through the source code of synchrony and had a look at the
deobfuscateNodefunction.The function calls
transformin a loop using different code transformers. Although these transformers have an async function signature, I didn't find a single one that actually worked in an async manner (meaning no async IO methods are used and nothing is even awaited in the implementations). This means, even though a promise is returned fromdeobfuscateNode, all code is actually executed synchronously and will keep the JS interpreter busy.This means, once you start running
deobfuscateNode, you will not be able to interrupt it in any way. To be able to do that, the synchrony library would have to be changed in the way it worked.