40 lines
1.8 KiB
TypeScript
40 lines
1.8 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { existsSync } from "node:fs";
|
|
import { readdir } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { openSource } from "../src/util/source";
|
|
import { PYTHON, tmp } from "./fixtures";
|
|
import { run } from "../src/util/exec";
|
|
|
|
/** Builds a zip by hand with traversal, absolute and symlink entries. */
|
|
async function hostileZip(path: string) {
|
|
await run([PYTHON, "-I", "-c", `
|
|
import sys, zipfile, stat
|
|
z = zipfile.ZipFile(sys.argv[1], 'w')
|
|
z.writestr('../../evil-up.txt', 'x')
|
|
z.writestr('/tmp/stc-evil-abs.txt', 'x')
|
|
z.writestr('ok/fine.txt', 'fine')
|
|
i = zipfile.ZipInfo('ok/link'); i.create_system = 3; i.external_attr = (stat.S_IFLNK | 0o777) << 16
|
|
z.writestr(i, '/etc')
|
|
i2 = zipfile.ZipInfo('ok/link/pwned.txt'); z.writestr(i2, 'x')
|
|
z.close()`, path]);
|
|
}
|
|
|
|
test("hostile zip entries never write outside the extraction dir", async () => {
|
|
const base = await tmp("stc-zip-");
|
|
await hostileZip(join(base, "h.zip"));
|
|
const src = await openSource(join(base, "h.zip"));
|
|
const dest = join(base, "a", "b", "out");
|
|
await src.extract(src.entries, dest).catch(() => {}); // unzip may warn; what matters is where files land
|
|
expect(existsSync(join(base, "evil-up.txt"))).toBe(false);
|
|
expect(existsSync(join(base, "a", "evil-up.txt"))).toBe(false);
|
|
expect(existsSync(join(base, "..", "evil-up.txt"))).toBe(false);
|
|
expect(existsSync("/tmp/stc-evil-abs.txt")).toBe(false);
|
|
expect(existsSync("/etc/pwned.txt")).toBe(false);
|
|
expect(await Bun.file(join(dest, "ok/fine.txt")).text()).toBe("fine");
|
|
const left: string[] = [];
|
|
const walk = async (d: string) => { for (const e of await readdir(d, { withFileTypes: true })) { if (e.isSymbolicLink()) left.push(e.name); else if (e.isDirectory()) await walk(join(d, e.name)); } };
|
|
await walk(dest);
|
|
expect(left).toEqual([]); // the symlink entry is dropped
|
|
});
|