test(browser): dedupe path fixture calls and cover root resolvers

This commit is contained in:
Peter Steinberger
2026-02-21 19:51:19 +00:00
parent 0f9ea0229a
commit 59189750e4

View File

@@ -2,7 +2,11 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { resolveExistingPathsWithinRoot } from "./paths.js";
import {
resolveExistingPathsWithinRoot,
resolvePathsWithinRoot,
resolvePathWithinRoot,
} from "./paths.js";
async function createFixtureRoot(): Promise<{ baseDir: string; uploadsDir: string }> {
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-browser-paths-"));
@@ -33,6 +37,17 @@ describe("resolveExistingPathsWithinRoot", () => {
}
}
function resolveWithinUploads(params: {
uploadsDir: string;
requestedPaths: string[];
}): Promise<Awaited<ReturnType<typeof resolveExistingPathsWithinRoot>>> {
return resolveExistingPathsWithinRoot({
rootDir: params.uploadsDir,
requestedPaths: params.requestedPaths,
scopeLabel: "uploads directory",
});
}
it("accepts existing files under the upload root", async () => {
await withFixtureRoot(async ({ uploadsDir }) => {
const nestedDir = path.join(uploadsDir, "nested");
@@ -40,10 +55,9 @@ describe("resolveExistingPathsWithinRoot", () => {
const filePath = path.join(nestedDir, "ok.txt");
await fs.writeFile(filePath, "ok", "utf8");
const result = await resolveExistingPathsWithinRoot({
rootDir: uploadsDir,
const result = await resolveWithinUploads({
uploadsDir,
requestedPaths: [filePath],
scopeLabel: "uploads directory",
});
expect(result.ok).toBe(true);
@@ -58,10 +72,9 @@ describe("resolveExistingPathsWithinRoot", () => {
const outsidePath = path.join(baseDir, "outside.txt");
await fs.writeFile(outsidePath, "nope", "utf8");
const result = await resolveExistingPathsWithinRoot({
rootDir: uploadsDir,
const result = await resolveWithinUploads({
uploadsDir,
requestedPaths: ["../outside.txt"],
scopeLabel: "uploads directory",
});
expectInvalidResult(result, "must stay within uploads directory");
@@ -70,10 +83,9 @@ describe("resolveExistingPathsWithinRoot", () => {
it("rejects blank paths", async () => {
await withFixtureRoot(async ({ uploadsDir }) => {
const result = await resolveExistingPathsWithinRoot({
rootDir: uploadsDir,
const result = await resolveWithinUploads({
uploadsDir,
requestedPaths: [" "],
scopeLabel: "uploads directory",
});
expectInvalidResult(result, "path is required");
@@ -82,10 +94,9 @@ describe("resolveExistingPathsWithinRoot", () => {
it("keeps lexical in-root paths when files do not exist yet", async () => {
await withFixtureRoot(async ({ uploadsDir }) => {
const result = await resolveExistingPathsWithinRoot({
rootDir: uploadsDir,
const result = await resolveWithinUploads({
uploadsDir,
requestedPaths: ["missing.txt"],
scopeLabel: "uploads directory",
});
expect(result.ok).toBe(true);
@@ -100,10 +111,9 @@ describe("resolveExistingPathsWithinRoot", () => {
const nestedDir = path.join(uploadsDir, "nested");
await fs.mkdir(nestedDir, { recursive: true });
const result = await resolveExistingPathsWithinRoot({
rootDir: uploadsDir,
const result = await resolveWithinUploads({
uploadsDir,
requestedPaths: ["nested"],
scopeLabel: "uploads directory",
});
expectInvalidResult(result, "regular non-symlink file");
@@ -119,10 +129,9 @@ describe("resolveExistingPathsWithinRoot", () => {
const symlinkPath = path.join(uploadsDir, "leak.txt");
await fs.symlink(outsidePath, symlinkPath);
const result = await resolveExistingPathsWithinRoot({
rootDir: uploadsDir,
const result = await resolveWithinUploads({
uploadsDir,
requestedPaths: ["leak.txt"],
scopeLabel: "uploads directory",
});
expectInvalidResult(result, "regular non-symlink file");
@@ -130,3 +139,56 @@ describe("resolveExistingPathsWithinRoot", () => {
},
);
});
describe("resolvePathWithinRoot", () => {
it("uses default file name when requested path is blank", () => {
const result = resolvePathWithinRoot({
rootDir: "/tmp/uploads",
requestedPath: " ",
scopeLabel: "uploads directory",
defaultFileName: "fallback.txt",
});
expect(result).toEqual({
ok: true,
path: path.resolve("/tmp/uploads", "fallback.txt"),
});
});
it("rejects root-level path aliases that do not point to a file", () => {
const result = resolvePathWithinRoot({
rootDir: "/tmp/uploads",
requestedPath: ".",
scopeLabel: "uploads directory",
});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error).toContain("must stay within uploads directory");
}
});
});
describe("resolvePathsWithinRoot", () => {
it("resolves all valid in-root paths", () => {
const result = resolvePathsWithinRoot({
rootDir: "/tmp/uploads",
requestedPaths: ["a.txt", "nested/b.txt"],
scopeLabel: "uploads directory",
});
expect(result).toEqual({
ok: true,
paths: [path.resolve("/tmp/uploads", "a.txt"), path.resolve("/tmp/uploads", "nested/b.txt")],
});
});
it("returns the first path validation error", () => {
const result = resolvePathsWithinRoot({
rootDir: "/tmp/uploads",
requestedPaths: ["a.txt", "../outside.txt", "b.txt"],
scopeLabel: "uploads directory",
});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error).toContain("must stay within uploads directory");
}
});
});