mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 19:54:46 +00:00
test: tighten archive security assertions
This commit is contained in:
@@ -55,8 +55,23 @@ async function createDirectorySymlink(targetDir: string, linkPath: string) {
|
||||
await fs.symlink(targetDir, linkPath, directorySymlinkType);
|
||||
}
|
||||
|
||||
async function expectRejectedCode(promise: Promise<unknown>, expected: string | RegExp) {
|
||||
try {
|
||||
await promise;
|
||||
} catch (error) {
|
||||
const code = (error as Partial<ArchiveSecurityError>).code;
|
||||
if (typeof expected === "string") {
|
||||
expect(code).toBe(expected);
|
||||
return;
|
||||
}
|
||||
expect(String(code)).toMatch(expected);
|
||||
return;
|
||||
}
|
||||
throw new Error("expected promise to reject");
|
||||
}
|
||||
|
||||
async function expectPathMissing(filePath: string) {
|
||||
await expect(fs.stat(filePath)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expectRejectedCode(fs.stat(filePath), "ENOENT");
|
||||
}
|
||||
|
||||
async function expectExtractedSizeBudgetExceeded(params: {
|
||||
@@ -148,15 +163,14 @@ describe("archive utils", () => {
|
||||
await fs.rm(extractDir, { recursive: true, force: true });
|
||||
await createDirectorySymlink(realExtractDir, extractDir);
|
||||
|
||||
await expect(
|
||||
await expectRejectedCode(
|
||||
extractArchive({
|
||||
archivePath,
|
||||
destDir: extractDir,
|
||||
timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS,
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "destination-symlink",
|
||||
} satisfies Partial<ArchiveSecurityError>);
|
||||
"destination-symlink",
|
||||
);
|
||||
|
||||
await expectPathMissing(path.join(realExtractDir, "package", "hello.txt"));
|
||||
});
|
||||
@@ -189,15 +203,14 @@ describe("archive utils", () => {
|
||||
zip.file("escape/pwn.txt", "owned");
|
||||
await fs.writeFile(archivePath, await zip.generateAsync({ type: "nodebuffer" }));
|
||||
|
||||
await expect(
|
||||
await expectRejectedCode(
|
||||
extractArchive({
|
||||
archivePath,
|
||||
destDir: extractDir,
|
||||
timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS,
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "destination-symlink-traversal",
|
||||
} satisfies Partial<ArchiveSecurityError>);
|
||||
"destination-symlink-traversal",
|
||||
);
|
||||
|
||||
const outsideFile = path.join(outsideDir, "pwn.txt");
|
||||
const outsideExists = await fs
|
||||
@@ -239,9 +252,8 @@ describe("archive utils", () => {
|
||||
});
|
||||
} catch (error) {
|
||||
rejected = true;
|
||||
expect(error).toMatchObject({
|
||||
code: expect.stringMatching(/destination-symlink-traversal|not-file/),
|
||||
} satisfies Partial<ArchiveSecurityError>);
|
||||
const code = (error as Partial<ArchiveSecurityError>).code;
|
||||
expect(String(code)).toMatch(/destination-symlink-traversal|not-file/);
|
||||
}
|
||||
|
||||
await expect(fs.readFile(outsideTarget, "utf8")).resolves.toBe("SAFE");
|
||||
@@ -280,21 +292,20 @@ describe("archive utils", () => {
|
||||
});
|
||||
|
||||
try {
|
||||
await expect(
|
||||
await expectRejectedCode(
|
||||
extractArchive({
|
||||
archivePath,
|
||||
destDir: extractDir,
|
||||
timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS,
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: expect.stringMatching(/^(?:destination-symlink-traversal|hardlink)$/u),
|
||||
});
|
||||
/^(?:destination-symlink-traversal|hardlink)$/u,
|
||||
);
|
||||
} finally {
|
||||
lstatSpy.mockRestore();
|
||||
}
|
||||
|
||||
await expect(fs.readFile(outsideAlias, "utf8")).resolves.toBe("");
|
||||
await expect(fs.stat(extractedPath)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expectPathMissing(extractedPath);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -327,15 +338,14 @@ describe("archive utils", () => {
|
||||
await createDirectorySymlink(outsideDir, path.join(extractDir, "escape"));
|
||||
await tar.c({ cwd: archiveRoot, file: archivePath }, ["escape"]);
|
||||
|
||||
await expect(
|
||||
await expectRejectedCode(
|
||||
extractArchive({
|
||||
archivePath,
|
||||
destDir: extractDir,
|
||||
timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS,
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "destination-symlink-traversal",
|
||||
} satisfies Partial<ArchiveSecurityError>);
|
||||
"destination-symlink-traversal",
|
||||
);
|
||||
|
||||
await expectPathMissing(path.join(outsideDir, "pwn.txt"));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user