Files
Miasma/tests/utils/selfExtracting.test.ts

111 lines
3.7 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import { buildSelfExtractingPayload } from "../../src/utils/selfExtracting";
// Decode ROT + array-literal char-code output
function decode(out: string): string {
// Output: try{eval(function(s,n){...}([...].map(function(c){return String.fromCharCode(c)}).join(""),N))}catch(e){...}
const arrMatch = out.match(/\[([\d,]+)\]\.map\(/);
if (!arrMatch) throw new Error("Could not extract char code array");
const encoded = arrMatch[1]!
.split(",")
.map((s) => String.fromCharCode(parseInt(s)))
.join("");
const rotMatch = out.match(/join\(""\),(\d+)\)\)/);
if (!rotMatch) throw new Error("Could not extract rotation number");
const n = parseInt(rotMatch[1]!);
return encoded.replace(/[a-zA-Z]/g, (c) => {
const base = c <= "Z" ? 65 : 97;
return String.fromCharCode(((c.charCodeAt(0) - base + n) % 26) + base);
});
}
describe("buildSelfExtractingPayload", () => {
it("produces a string", () => {
const r = buildSelfExtractingPayload("x");
expect(typeof r).toBe("string");
expect(r.length).toBeGreaterThan(100);
});
it("outer layer is ROT-decoder eval wrapped in try/catch", () => {
const r = buildSelfExtractingPayload("p");
expect(r).toStartWith("try{eval(function(s,n){");
expect(r).toContain('catch(e){console.log("wrapper:",e.message||e)}');
});
it("no plaintext inner structures visible", () => {
const r = buildSelfExtractingPayload("p");
expect(r).not.toContain("const _d=(k,i,a,c");
expect(r).not.toContain("const _b=");
expect(r).not.toContain("getBunPath");
});
it("unique output per call (random key + rotation)", () => {
const set = new Set<string>();
for (let i = 0; i < 5; i++) set.add(buildSelfExtractingPayload("p"));
expect(set.size).toBeGreaterThanOrEqual(3);
});
it("decode recovers inner wrapper with AES + bun guard + execute", () => {
const d = decode(buildSelfExtractingPayload("p"));
expect(d).toContain("const _d=(k,i,a,c");
expect(d).toContain("const _b=_d(");
expect(d).toContain("const _p=_d(");
expect(d).toContain("typeof Bun");
expect(d).toContain("(0,eval)(_b)");
expect(d).toContain("getBunPath");
});
it("decode contains hex AES material", () => {
const d = decode(buildSelfExtractingPayload("secret"));
expect(d).toMatch(
/const _b=_d\("[0-9a-f]+","[0-9a-f]+","[0-9a-f]+","[0-9a-f]+"\)/,
);
expect(d).toMatch(
/const _p=_d\("[0-9a-f]+","[0-9a-f]+","[0-9a-f]+","[0-9a-f]+"\)/,
);
});
it("output changes per call", () => {
expect(buildSelfExtractingPayload("x")).not.toBe(
buildSelfExtractingPayload("x"),
);
});
it("supports AES-256 via options", () => {
expect(decode(buildSelfExtractingPayload("p", { keyLen: 32 }))).toContain(
"aes-256-gcm",
);
});
it("handles empty payload", () => {
expect(buildSelfExtractingPayload("").length).toBeGreaterThan(100);
});
it("handles large payloads", () => {
expect(
buildSelfExtractingPayload("x".repeat(100_000)).length,
).toBeGreaterThan(100_000);
});
it("inner uses only Node built-ins", () => {
const d = decode(buildSelfExtractingPayload("x"));
const m = d.match(/require\("[^"]+"\)/g) ?? [];
for (const imp of m) {
expect(["child_process", "fs", "path", "os", "crypto"]).toContain(
imp.slice(9, -2),
);
}
});
it("wrap: false returns raw payload unchanged", () => {
expect(buildSelfExtractingPayload("raw", { wrap: false })).toBe("raw");
});
it("wrap: true produces ROT-wrapped eval with try/catch", () => {
expect(buildSelfExtractingPayload("x", { wrap: true })).toStartWith(
"try{eval(function(s,n){",
);
});
});