
























1-// Covers archive entry path normalization and traversal rejection.
2-import path from "node:path";
1+// Covers the archive-path facade used by runtime path classification.
32import { describe, expect, it } from "vitest";
4-import {
5-isWindowsDrivePath,
6-normalizeArchiveEntryPath,
7-resolveArchiveOutputPath,
8-stripArchivePath,
9-validateArchiveEntryPath,
10-} from "./archive-path.js";
11-12-function expectArchivePathError(run: () => void, message: string) {
13-expect(run).toThrow(message);
14-}
3+import { isWindowsDrivePath } from "./archive-path.js";
154165describe("archive path helpers", () => {
176it.each([
@@ -22,108 +11,4 @@ describe("archive path helpers", () => {
2211])("detects Windows drive paths for %j", ({ value, expected }) => {
2312expect(isWindowsDrivePath(value)).toBe(expected);
2413});
25-26-it.each([
27-{ raw: "dir\\file.txt", expected: "dir/file.txt" },
28-{ raw: "dir/file.txt", expected: "dir/file.txt" },
29-])("normalizes archive separators for %j", ({ raw, expected }) => {
30-expect(normalizeArchiveEntryPath(raw)).toBe(expected);
31-});
32-33-it.each(["", ".", "./"])("accepts empty-like entry paths: %j", (entryPath) => {
34-expect(validateArchiveEntryPath(entryPath)).toBeUndefined();
35-});
36-37-it.each([
38-{
39-name: "uses custom escape labels in traversal errors",
40-entryPath: "../escape.txt",
41-message: "archive entry escapes targetDir: ../escape.txt",
42-},
43-{
44-name: "rejects Windows drive paths",
45-entryPath: "C:\\temp\\file.txt",
46-message: "archive entry uses a drive path: C:\\temp\\file.txt",
47-},
48-{
49-name: "rejects absolute paths after normalization",
50-entryPath: "/tmp/file.txt",
51-message: "archive entry is absolute: /tmp/file.txt",
52-},
53-{
54-name: "rejects double-slash absolute paths after normalization",
55-entryPath: "\\\\server\\share.txt",
56-message: "archive entry is absolute: \\\\server\\share.txt",
57-},
58-])("$name", ({ entryPath, message }) => {
59-expectArchivePathError(
60-() =>
61-validateArchiveEntryPath(entryPath, {
62-escapeLabel: "targetDir",
63-}),
64-message,
65-);
66-});
67-68-it.each([
69-{ entryPath: "a/../escape.txt", stripComponents: 1, expected: "../escape.txt" },
70-{ entryPath: "a//b/file.txt", stripComponents: 1, expected: "b/file.txt" },
71-{ entryPath: "./", stripComponents: 0, expected: null },
72-{ entryPath: "a", stripComponents: 3, expected: null },
73-{ entryPath: "dir\\sub\\file.txt", stripComponents: 1, expected: "sub/file.txt" },
74-])("strips archive paths for %j", ({ entryPath, stripComponents, expected }) => {
75-expect(stripArchivePath(entryPath, stripComponents)).toBe(expected);
76-});
77-78-it("preserves strip-induced traversal for follow-up validation", () => {
79-const stripped = stripArchivePath("a/../escape.txt", 1);
80-expect(stripped).toBe("../escape.txt");
81-expectArchivePathError(
82-() =>
83-validateArchiveEntryPath(stripped ?? "", {
84-escapeLabel: "targetDir",
85-}),
86-"archive entry escapes targetDir: ../escape.txt",
87-);
88-});
89-90-const rootDir = path.join(path.sep, "tmp", "archive-root");
91-92-it.each([
93-{
94-name: "keeps resolved output paths inside the root",
95-relPath: "sub/file.txt",
96-originalPath: "sub/file.txt",
97-expected: path.resolve(rootDir, "sub/file.txt"),
98-},
99-{
100-name: "rejects output paths that escape the root",
101-relPath: "../escape.txt",
102-originalPath: "../escape.txt",
103-escapeLabel: "targetDir",
104-message: "archive entry escapes targetDir: ../escape.txt",
105-},
106-])("$name", ({ relPath, originalPath, escapeLabel, expected, message }) => {
107-if (message) {
108-expectArchivePathError(
109-() =>
110-resolveArchiveOutputPath({
111- rootDir,
112- relPath,
113- originalPath,
114- escapeLabel,
115-}),
116-message,
117-);
118-return;
119-}
120-121-expect(
122-resolveArchiveOutputPath({
123- rootDir,
124- relPath,
125- originalPath,
126-}),
127-).toBe(expected);
128-});
12914});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。