mirror of https://github.com/openclaw/openclaw.git
test: expand archive path helper coverage
This commit is contained in:
parent
bc9a9cf972
commit
f0a266cb86
|
|
@ -1,18 +1,71 @@
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import {
|
import {
|
||||||
|
isWindowsDrivePath,
|
||||||
|
normalizeArchiveEntryPath,
|
||||||
resolveArchiveOutputPath,
|
resolveArchiveOutputPath,
|
||||||
stripArchivePath,
|
stripArchivePath,
|
||||||
validateArchiveEntryPath,
|
validateArchiveEntryPath,
|
||||||
} from "./archive-path.js";
|
} from "./archive-path.js";
|
||||||
|
|
||||||
describe("archive path helpers", () => {
|
describe("archive path helpers", () => {
|
||||||
it("uses custom escape labels in traversal errors", () => {
|
it.each([
|
||||||
|
{ value: "C:\\temp\\file.txt", expected: true },
|
||||||
|
{ value: "D:/temp/file.txt", expected: true },
|
||||||
|
{ value: "tmp/file.txt", expected: false },
|
||||||
|
{ value: "/tmp/file.txt", expected: false },
|
||||||
|
])("detects Windows drive paths for %j", ({ value, expected }) => {
|
||||||
|
expect(isWindowsDrivePath(value)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ raw: "dir\\file.txt", expected: "dir/file.txt" },
|
||||||
|
{ raw: "dir/file.txt", expected: "dir/file.txt" },
|
||||||
|
])("normalizes archive separators for %j", ({ raw, expected }) => {
|
||||||
|
expect(normalizeArchiveEntryPath(raw)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(["", ".", "./"])("accepts empty-like entry paths: %j", (entryPath) => {
|
||||||
|
expect(() => validateArchiveEntryPath(entryPath)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: "uses custom escape labels in traversal errors",
|
||||||
|
entryPath: "../escape.txt",
|
||||||
|
message: "archive entry escapes targetDir: ../escape.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rejects Windows drive paths",
|
||||||
|
entryPath: "C:\\temp\\file.txt",
|
||||||
|
message: "archive entry uses a drive path: C:\\temp\\file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rejects absolute paths after normalization",
|
||||||
|
entryPath: "/tmp/file.txt",
|
||||||
|
message: "archive entry is absolute: /tmp/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rejects double-slash absolute paths after normalization",
|
||||||
|
entryPath: "\\\\server\\share.txt",
|
||||||
|
message: "archive entry is absolute: \\\\server\\share.txt",
|
||||||
|
},
|
||||||
|
])("$name", ({ entryPath, message }) => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
validateArchiveEntryPath("../escape.txt", {
|
validateArchiveEntryPath(entryPath, {
|
||||||
escapeLabel: "targetDir",
|
escapeLabel: "targetDir",
|
||||||
}),
|
}),
|
||||||
).toThrow("archive entry escapes targetDir: ../escape.txt");
|
).toThrow(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ entryPath: "a/../escape.txt", stripComponents: 1, expected: "../escape.txt" },
|
||||||
|
{ entryPath: "a//b/file.txt", stripComponents: 1, expected: "b/file.txt" },
|
||||||
|
{ entryPath: "./", stripComponents: 0, expected: null },
|
||||||
|
{ entryPath: "a", stripComponents: 3, expected: null },
|
||||||
|
{ entryPath: "dir\\sub\\file.txt", stripComponents: 1, expected: "sub/file.txt" },
|
||||||
|
])("strips archive paths for %j", ({ entryPath, stripComponents, expected }) => {
|
||||||
|
expect(stripArchivePath(entryPath, stripComponents)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("preserves strip-induced traversal for follow-up validation", () => {
|
it("preserves strip-induced traversal for follow-up validation", () => {
|
||||||
|
|
@ -25,22 +78,40 @@ describe("archive path helpers", () => {
|
||||||
).toThrow("archive entry escapes targetDir: ../escape.txt");
|
).toThrow("archive entry escapes targetDir: ../escape.txt");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps resolved output paths inside the root", () => {
|
it.each([
|
||||||
const rootDir = path.join(path.sep, "tmp", "archive-root");
|
{
|
||||||
const safe = resolveArchiveOutputPath({
|
name: "keeps resolved output paths inside the root",
|
||||||
rootDir,
|
|
||||||
relPath: "sub/file.txt",
|
relPath: "sub/file.txt",
|
||||||
originalPath: "sub/file.txt",
|
originalPath: "sub/file.txt",
|
||||||
});
|
expected: path.resolve(path.join(path.sep, "tmp", "archive-root"), "sub/file.txt"),
|
||||||
expect(safe).toBe(path.resolve(rootDir, "sub/file.txt"));
|
},
|
||||||
|
{
|
||||||
|
name: "rejects output paths that escape the root",
|
||||||
|
relPath: "../escape.txt",
|
||||||
|
originalPath: "../escape.txt",
|
||||||
|
escapeLabel: "targetDir",
|
||||||
|
message: "archive entry escapes targetDir: ../escape.txt",
|
||||||
|
},
|
||||||
|
])("$name", ({ relPath, originalPath, escapeLabel, expected, message }) => {
|
||||||
|
const rootDir = path.join(path.sep, "tmp", "archive-root");
|
||||||
|
if (message) {
|
||||||
|
expect(() =>
|
||||||
|
resolveArchiveOutputPath({
|
||||||
|
rootDir,
|
||||||
|
relPath,
|
||||||
|
originalPath,
|
||||||
|
escapeLabel,
|
||||||
|
}),
|
||||||
|
).toThrow(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
expect(() =>
|
expect(
|
||||||
resolveArchiveOutputPath({
|
resolveArchiveOutputPath({
|
||||||
rootDir,
|
rootDir,
|
||||||
relPath: "../escape.txt",
|
relPath,
|
||||||
originalPath: "../escape.txt",
|
originalPath,
|
||||||
escapeLabel: "targetDir",
|
|
||||||
}),
|
}),
|
||||||
).toThrow("archive entry escapes targetDir: ../escape.txt");
|
).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue