refactor(infra): trim dead archive facade exports

This commit is contained in:
Vincent Koc
2026-06-19 10:44:18 +08:00
parent aaceaf8e7c
commit d1ab308f5c
3 changed files with 2 additions and 123 deletions
+2 -117
View File
@@ -1,17 +1,6 @@
// Covers archive entry path normalization and traversal rejection.
import path from "node:path";
// Covers the archive-path facade used by runtime path classification.
import { describe, expect, it } from "vitest";
import {
isWindowsDrivePath,
normalizeArchiveEntryPath,
resolveArchiveOutputPath,
stripArchivePath,
validateArchiveEntryPath,
} from "./archive-path.js";
function expectArchivePathError(run: () => void, message: string) {
expect(run).toThrow(message);
}
import { isWindowsDrivePath } from "./archive-path.js";
describe("archive path helpers", () => {
it.each([
@@ -22,108 +11,4 @@ describe("archive path helpers", () => {
])("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)).toBeUndefined();
});
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 }) => {
expectArchivePathError(
() =>
validateArchiveEntryPath(entryPath, {
escapeLabel: "targetDir",
}),
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", () => {
const stripped = stripArchivePath("a/../escape.txt", 1);
expect(stripped).toBe("../escape.txt");
expectArchivePathError(
() =>
validateArchiveEntryPath(stripped ?? "", {
escapeLabel: "targetDir",
}),
"archive entry escapes targetDir: ../escape.txt",
);
});
const rootDir = path.join(path.sep, "tmp", "archive-root");
it.each([
{
name: "keeps resolved output paths inside the root",
relPath: "sub/file.txt",
originalPath: "sub/file.txt",
expected: 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 }) => {
if (message) {
expectArchivePathError(
() =>
resolveArchiveOutputPath({
rootDir,
relPath,
originalPath,
escapeLabel,
}),
message,
);
return;
}
expect(
resolveArchiveOutputPath({
rootDir,
relPath,
originalPath,
}),
).toBe(expected);
});
});
-4
View File
@@ -4,8 +4,4 @@ import "./fs-safe-defaults.js";
// Archive path facade kept in infra so callers share one traversal policy.
export {
isWindowsDrivePath,
normalizeArchiveEntryPath,
resolveArchiveOutputPath,
stripArchivePath,
validateArchiveEntryPath,
} from "@openclaw/fs-safe/archive";
-2
View File
@@ -10,13 +10,11 @@ export {
DEFAULT_MAX_ENTRIES,
DEFAULT_MAX_EXTRACTED_BYTES,
DEFAULT_MAX_ENTRY_BYTES,
createArchiveSymlinkTraversalError,
createTarEntryPreflightChecker,
extractArchive,
loadZipArchiveWithPreflight,
mergeExtractedTreeIntoDestination,
prepareArchiveDestinationDir,
prepareArchiveOutputPath,
readZipCentralDirectoryEntryCount,
resolveArchiveKind,
resolvePackedRootDir,