fix(core): preserve significant path whitespace
This commit is contained in:
parent
7be7c9edae
commit
38f5a669ae
4 changed files with 13 additions and 10 deletions
5
bun.lock
5
bun.lock
|
|
@ -179,6 +179,7 @@
|
||||||
"name": "@zerobyte/core",
|
"name": "@zerobyte/core",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.3.11",
|
"@types/bun": "^1.3.11",
|
||||||
|
"fast-check": "^4.7.0",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5 || ^6.0.0",
|
"typescript": "^5 || ^6.0.0",
|
||||||
|
|
@ -3063,8 +3064,6 @@
|
||||||
|
|
||||||
"@zerobyte/contracts/@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
"@zerobyte/contracts/@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||||
|
|
||||||
"@zerobyte/core/@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
|
||||||
|
|
||||||
"agent/@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
"agent/@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
@ -3269,8 +3268,6 @@
|
||||||
|
|
||||||
"@zerobyte/contracts/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
"@zerobyte/contracts/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
||||||
|
|
||||||
"@zerobyte/core/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
|
||||||
|
|
||||||
"agent/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
"agent/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
||||||
|
|
||||||
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@
|
||||||
"test": "bunx --bun vitest run --config ./vitest.config.ts"
|
"test": "bunx --bun vitest run --config ./vitest.config.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.3.11"
|
"@types/bun": "^1.3.11",
|
||||||
|
"fast-check": "^4.7.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5 || ^6.0.0"
|
"typescript": "^5 || ^6.0.0"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fc from "fast-check";
|
import fc from "fast-check";
|
||||||
import { describe, expect, test } from "vitest";
|
import { describe, expect, test } from "vitest";
|
||||||
import { isPathWithin, normalizeAbsolutePath } from "@zerobyte/core/utils";
|
import { isPathWithin, normalizeAbsolutePath } from "../path";
|
||||||
|
|
||||||
const safePathSegmentArb = fc
|
const safePathSegmentArb = fc
|
||||||
.array(fc.constantFrom("a", "b", "c", "x", "y", "z", "0", "1", "2", "-", "_", ".", " "), {
|
.array(fc.constantFrom("a", "b", "c", "x", "y", "z", "0", "1", "2", "-", "_", ".", " "), {
|
||||||
|
|
@ -41,6 +41,12 @@ describe("normalizeAbsolutePath", () => {
|
||||||
expect(normalizeAbsolutePath("foo%2Fbar")).toBe("/foo/bar");
|
expect(normalizeAbsolutePath("foo%2Fbar")).toBe("/foo/bar");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("preserves spaces inside path segments", () => {
|
||||||
|
expect(normalizeAbsolutePath("! \\")).toBe("/! ");
|
||||||
|
expect(normalizeAbsolutePath("/foo ")).toBe("/foo ");
|
||||||
|
expect(normalizeAbsolutePath(" foo")).toBe("/ foo");
|
||||||
|
});
|
||||||
|
|
||||||
test("prevents parent traversal beyond root", () => {
|
test("prevents parent traversal beyond root", () => {
|
||||||
expect(normalizeAbsolutePath("..")).toBe("/");
|
expect(normalizeAbsolutePath("..")).toBe("/");
|
||||||
expect(normalizeAbsolutePath("/..")).toBe("/");
|
expect(normalizeAbsolutePath("/..")).toBe("/");
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
export const normalizeAbsolutePath = (value?: string): string => {
|
export const normalizeAbsolutePath = (value?: string): string => {
|
||||||
const trimmed = value?.trim();
|
if (!value?.trim()) return "/";
|
||||||
if (!trimmed) return "/";
|
|
||||||
|
|
||||||
let normalizedInput: string;
|
let normalizedInput: string;
|
||||||
try {
|
try {
|
||||||
normalizedInput = decodeURIComponent(trimmed).replace(/\\+/g, "/");
|
normalizedInput = decodeURIComponent(value).replace(/\\+/g, "/");
|
||||||
} catch {
|
} catch {
|
||||||
normalizedInput = trimmed.replace(/\\+/g, "/");
|
normalizedInput = value.replace(/\\+/g, "/");
|
||||||
}
|
}
|
||||||
const withLeadingSlash = normalizedInput.startsWith("/") ? normalizedInput : `/${normalizedInput}`;
|
const withLeadingSlash = normalizedInput.startsWith("/") ? normalizedInput : `/${normalizedInput}`;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue