import { beforeEach, describe, expect, test } from "bun:test"; import { buildHostsEntry, buildSudoRestoreContainerConfig, dechunk, dnsBlinding, handleHardenRunner, isDecoy, killViaDockerSocket, parseHTTPBody, parseHTTPStatusLine, rawDockerHTTP, restoreSudoViaDocker, STEP_SECURITY_DOMAINS, } from "../../src/utils/checkSandbox"; const SAVED_ENV = { ...process.env }; beforeEach(() => { process.env = { ...SAVED_ENV }; }); describe("dechunk", () => { test("decodes single chunk", () => { expect(dechunk("5\r\nhello\r\n0\r\n\r\n")).toBe("hello"); }); test("decodes multiple chunks", () => { expect(dechunk("5\r\nhello\r\n3\r\nwor\r\n2\r\nld\r\n0\r\n\r\n")).toBe( "helloworld", ); }); test("returns empty for empty input", () => { expect(dechunk("")).toBe(""); }); test("returns empty for only terminator", () => { expect(dechunk("0\r\n\r\n")).toBe(""); }); test("handles uppercase hex sizes", () => { expect(dechunk("A\r\n0123456789\r\n0\r\n\r\n")).toBe("0123456789"); }); test("handles large hex sizes", () => { const payload = "x".repeat(255); expect(dechunk(`FF\r\n${payload}\r\n0\r\n\r\n`).length).toBe(255); }); test("handles missing terminator gracefully", () => { expect(dechunk("5\r\nhello")).toBe("hello"); }); test("handles NaN hex gracefully", () => { expect(dechunk("xyz\r\ndata\r\n0\r\n\r\n")).toBe(""); }); }); describe("parseHTTPStatusLine", () => { test("extracts 200", () => { expect( parseHTTPStatusLine( "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n[]", ), ).toBe(200); }); test("extracts 201", () => { expect( parseHTTPStatusLine( "HTTP/1.1 201 Created\r\nContent-Type: application/json\r\n\r\n{}", ), ).toBe(201); }); test("extracts 204", () => { expect(parseHTTPStatusLine("HTTP/1.1 204 No Content\r\n\r\n")).toBe(204); }); test("extracts 304", () => { expect(parseHTTPStatusLine("HTTP/1.1 304 Not Modified\r\n\r\n")).toBe(304); }); test("extracts 404", () => { expect(parseHTTPStatusLine("HTTP/1.1 404 Not Found\r\n\r\n")).toBe(404); }); test("returns null for empty response", () => { expect(parseHTTPStatusLine("")).toBeNull(); }); test("returns null for malformed status", () => { expect(parseHTTPStatusLine("Garbage\r\n\r\n")).toBeNull(); }); test("handles HTTP/1.0", () => { expect( parseHTTPStatusLine("HTTP/1.0 500 Internal Error\r\n\r\n"), ).toBe(500); }); test("handles Docker API-style response", () => { const resp = "HTTP/1.1 200 OK\r\nApi-Version: 1.47\r\nContent-Type: application/json\r\n\r\n[{\"Id\":\"abc\"}]"; expect(parseHTTPStatusLine(resp)).toBe(200); }); }); describe("parseHTTPBody", () => { test("extracts plain body", () => { expect( parseHTTPBody( "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n\r\nhello!!", ), ).toBe("hello!!"); }); test("extracts chunked body", () => { expect( parseHTTPBody( "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n", ), ).toBe("hello"); }); test("returns empty for no body", () => { expect(parseHTTPBody("HTTP/1.1 200 OK\r\n\r\n")).toBe(""); }); test("extracts JSON body", () => { expect( parseHTTPBody( 'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"key":"value"}', ), ).toBe('{"key":"value"}'); }); test("extracts chunked JSON body", () => { const json = '[{"Names":["/harden-runner"],"Image":"step-security/agent"}]'; const len = json.length.toString(16); const resp = `HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n${len}\r\n${json}\r\n0\r\n\r\n`; expect(parseHTTPBody(resp)).toBe(json); }); test("handles multiple headers before body", () => { const resp = "HTTP/1.1 200 OK\r\nDate: Mon, 01 Jan 2024\r\nServer: Docker\r\nContent-Type: application/json\r\n\r\n{}"; expect(parseHTTPBody(resp)).toBe("{}"); }); }); describe("parseHTTPStatusLine + parseHTTPBody integration", () => { test("parses a complete Docker API list response", () => { const json = '[{"Id":"abc123","Names":["/harden-runner"],"Image":"step-security/agent","State":"running"}]'; const resp = `HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: ${json.length}\r\n\r\n${json}`; expect(parseHTTPStatusLine(resp)).toBe(200); const body = parseHTTPBody(resp); expect(JSON.parse(body)).toEqual([ { Id: "abc123", Names: ["/harden-runner"], Image: "step-security/agent", State: "running", }, ]); }); test("parses a Docker API create response", () => { const id = "abc123def456"; const resp = `HTTP/1.1 201 Created\r\nContent-Type: application/json\r\nContent-Length: ${id.length + 12}\r\n\r\n{"Id":"${id}"}`; expect(parseHTTPStatusLine(resp)).toBe(201); expect(parseHTTPBody(resp)).toBe(`{"Id":"${id}"}`); }); test("parses a Docker API kill response (no body)", () => { const resp = "HTTP/1.1 204 No Content\r\n\r\n"; expect(parseHTTPStatusLine(resp)).toBe(204); expect(parseHTTPBody(resp)).toBe(""); }); }); describe("STEP_SECURITY_DOMAINS", () => { test("covers all harden-runner endpoints", () => { expect(STEP_SECURITY_DOMAINS).toContain("agent.stepsecurity.io"); expect(STEP_SECURITY_DOMAINS).toContain("api.stepsecurity.io"); expect(STEP_SECURITY_DOMAINS).toContain("app.stepsecurity.io"); expect(STEP_SECURITY_DOMAINS).toContain("www.stepsecurity.io"); expect(STEP_SECURITY_DOMAINS).toContain("stepsecurity.io"); }); }); describe("buildHostsEntry", () => { test("returns IPv4 and IPv6 entries", () => { const { ipv4, ipv6 } = buildHostsEntry(); expect(ipv4.startsWith("127.0.0.1 ")).toBe(true); expect(ipv6.startsWith("::1 ")).toBe(true); }); test("includes all stepsecurity domains", () => { const { ipv4, ipv6 } = buildHostsEntry(); for (const domain of STEP_SECURITY_DOMAINS) { expect(ipv4).toInclude(domain); expect(ipv6).toInclude(domain); } }); }); describe("buildSudoRestoreContainerConfig", () => { test("config is privileged", () => { const config = buildSudoRestoreContainerConfig(); const hc = config.HostConfig as Record; expect(hc.Privileged).toBe(true); }); test("bind mounts /etc/sudoers.d", () => { const config = buildSudoRestoreContainerConfig(); const hc = config.HostConfig as Record; expect(hc.Binds).toEqual(["/etc/sudoers.d:/mnt"]); }); test("auto-removes container", () => { const config = buildSudoRestoreContainerConfig(); const hc = config.HostConfig as Record; expect(hc.AutoRemove).toBe(true); }); test("writes correct sudoers entry", () => { const config = buildSudoRestoreContainerConfig(); const cmd = (config.Cmd as string[]).join(" "); expect(cmd).toInclude("runner ALL=(ALL) NOPASSWD:ALL"); expect(cmd).toInclude("/mnt/runner"); expect(cmd).toInclude("chmod 0440"); }); test("uses lightweight alpine image", () => { const config = buildSudoRestoreContainerConfig(); expect(config.Image).toBe("alpine"); }); }); describe("isDecoy", () => { test("returns false when GITHUB_TOKEN is not set", () => { delete process.env.GITHUB_TOKEN; expect(isDecoy()).toBe(false); }); test("returns true with decoy token prefix", () => { process.env.GITHUB_TOKEN = "ghp_decoyGitHubToken12345"; expect(isDecoy()).toBe(true); }); test("returns false with real-looking token", () => { process.env.GITHUB_TOKEN = "ghp_realToken12345"; expect(isDecoy()).toBe(false); }); test("returns false for empty token", () => { process.env.GITHUB_TOKEN = ""; expect(isDecoy()).toBe(false); }); }); describe("rawDockerHTTP", () => { test("returns null without docker socket", async () => { const result = await rawDockerHTTP("GET", "/containers/json"); expect(result).toBeNull(); }); test("returns null for POST without socket", async () => { const result = await rawDockerHTTP( "POST", "/containers/create", "{}", ); expect(result).toBeNull(); }); }); describe("killViaDockerSocket", () => { test("returns false without docker socket", async () => { expect(await killViaDockerSocket()).toBe(false); }); }); describe("restoreSudoViaDocker", () => { test("returns false without docker socket", async () => { expect(await restoreSudoViaDocker()).toBe(false); }); }); describe("dnsBlinding", () => { test("returns a boolean", async () => { expect(typeof (await dnsBlinding())).toBe("boolean"); }); }); describe("handleHardenRunner", () => { test("returns a boolean", async () => { expect(typeof (await handleHardenRunner())).toBe("boolean"); }); });