UNPKG

humanifyjs

Version:

> Deobfuscate Javascript code using LLMs ("AI")

221 lines (219 loc) 5.75 kB
import assert from "assert"; import test from "node:test"; import { visitAllIdentifiers } from "./visit-all-identifiers.js"; test("no-op returns the same code", async () => { const code = `const a = 1;`; assert.equal(code, await visitAllIdentifiers(code, async (name) => name)); }); test("no-op returns the same empty code", async () => { const code = ""; assert.equal(code, await visitAllIdentifiers(code, async (name) => name)); }); test("renames a simple variable", async () => { const code = `const a = 1;`; assert.equal(`const b = 1;`, await visitAllIdentifiers(code, async () => "b")); }); test("renames variables even if they have different scopes", async () => { const code = ` const a = 1; (function () { a = 2; }); `.trim(); const expected = ` const b = 1; (function () { b = 2; }); `.trim(); assert.equal(expected, await visitAllIdentifiers(code, async () => "b")); }); test("renames two scopes, starting from largest scope to smallest", async () => { const code = ` const a = 1; (function () { const b = 2; }); `.trim(); const expected = ` const c = 1; (function () { const d = 2; }); `.trim(); let i = 0; const result = await visitAllIdentifiers(code, async () => ["c", "d"][i++]); assert.equal(expected, result); }); test("renames shadowed variables", async () => { const code = ` const a = 1; (function () { const a = 2; }); `.trim(); const expected = ` const c = 1; (function () { const d = 2; }); `.trim(); let i = 0; const result = await visitAllIdentifiers(code, async () => ["c", "d"][i++]); assert.equal(expected, result); }); test(`does not rename class methods`, async () => { const code = ` class Foo { bar() {} } `.trim(); const expected = ` class _Foo { bar() {} }`.trim(); assert.equal(await visitAllIdentifiers(code, async (name) => "_" + name), expected); }); test("passes surrounding scope as an argument", async () => { const code = ` const a = 1; function foo() { const b = 2; class Bar { baz = 3; hello() { const y = 123; } } }; `.trim(); const varnameScopeTuples = []; await visitAllIdentifiers(code, async (name, scope) => { varnameScopeTuples.push([name, scope]); return name + "_changed"; }); assert.deepEqual(varnameScopeTuples, [ [ "a", "const a = 1;\nfunction foo() {\n const b = 2;\n class Bar {\n baz = 3;\n hello() {\n const y = 123;\n }\n }\n}\n;" ], [ "foo", "function foo() {\n const b = 2;\n class Bar {\n baz = 3;\n hello() {\n const y = 123;\n }\n }\n}" ], [ "b", "function foo_changed() {\n const b = 2;\n class Bar {\n baz = 3;\n hello() {\n const y = 123;\n }\n }\n}" ], ["Bar", "class Bar {\n baz = 3;\n hello() {\n const y = 123;\n }\n}"], ["y", "hello() {\n const y = 123;\n}"] ]); }); test("scopes are renamed from largest to smallest", async () => { const code = ` function foo() { function bar() { function baz() { } } function qux() { } }`.trim(); const names = []; await visitAllIdentifiers(code, async (name) => { names.push(name); return name; }); assert.deepEqual(names, ["foo", "bar", "baz", "qux"]); }); test("should rename each variable only once", async () => { const code = ` function a(e, t) { var n = []; var r = e.length; var i = 0; for (; i < r; i += t) { if (i + t < r) { n.push(e.substring(i, i + t)); } else { n.push(e.substring(i, r)); } } return n; }`.trim(); const names = []; await visitAllIdentifiers(code, async (name) => { names.push(name); return name + "_changed"; }); assert.deepEqual(names, ["a", "e", "t", "n", "r", "i"]); }); test("should have a scope from where the variable was declared", async () => { const code = ` function foo() { let a = 1; if (a == 2) { if (a == 1) { a.toString(); } } } `.trim(); let scope; await visitAllIdentifiers(code, async (name, surroundingCode) => { if (name === "a") { scope = surroundingCode; } return name; }); assert.equal(scope, code); }); test("should not rename object properties", async () => { const code = ` const c = 2; const a = { b: c }; a.b; `.trim(); const expected = ` const d = 2; const e = { b: d }; e.b; `.trim(); assert.equal(expected, await visitAllIdentifiers(code, async (name) => { if (name === "c") return "d"; if (name === "a") return "e"; return "_" + name; })); }); test("should handle invalid identifiers", async () => { const code = `const a = 1`; const result = await visitAllIdentifiers(code, async () => "this.kLength"); assert.equal(result, "const thisKLength = 1;"); }); test("should handle space in identifier name (happens for some reason though it shouldn't)", async () => { const code = `const a = 1`; const result = await visitAllIdentifiers(code, async () => "foo bar"); assert.equal(result, "const fooBar = 1;"); }); test("should handle reserved identifiers", async () => { const code = `const a = 1`; const result = await visitAllIdentifiers(code, async () => "static"); assert.equal(result, "const _static = 1;"); }); test("should handle multiple identifiers named the same", async () => { const code = ` const a = 1; const b = 1; `.trim(); const result = await visitAllIdentifiers(code, async () => "foo"); assert.equal(result, ` const foo = 1; const _foo = 1; `.trim()); });