mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 09:17:27 +00:00
test(browser): dedupe path fixture calls and cover root resolvers
This commit is contained in:
@@ -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");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user