fix(security): harden browser trace/download temp path handling

This commit is contained in:
Peter Steinberger
2026-02-26 01:01:17 +01:00
parent e56b0cf1a0
commit 496a76c03b
8 changed files with 322 additions and 40 deletions

View File

@@ -1,3 +1,4 @@
import fs from "node:fs/promises";
import type { BrowserFormField } from "../client-actions-core.js";
import type { BrowserRouteContext } from "../server-context.js";
import {
@@ -15,14 +16,17 @@ import {
import {
DEFAULT_DOWNLOAD_DIR,
DEFAULT_UPLOAD_DIR,
resolvePathWithinRoot,
resolveWritablePathWithinRoot,
resolveExistingPathsWithinRoot,
} from "./path-output.js";
import type { BrowserResponse, BrowserRouteRegistrar } from "./types.js";
import { jsonError, toBoolean, toNumber, toStringArray, toStringOrEmpty } from "./utils.js";
function resolveDownloadPathOrRespond(res: BrowserResponse, requestedPath: string): string | null {
const downloadPathResult = resolvePathWithinRoot({
async function resolveDownloadPathOrRespond(
res: BrowserResponse,
requestedPath: string,
): Promise<string | null> {
const downloadPathResult = await resolveWritablePathWithinRoot({
rootDir: DEFAULT_DOWNLOAD_DIR,
requestedPath,
scopeLabel: "downloads directory",
@@ -466,9 +470,10 @@ export function registerBrowserAgentActRoutes(
targetId,
feature: "wait for download",
run: async ({ cdpUrl, tab, pw }) => {
await fs.mkdir(DEFAULT_DOWNLOAD_DIR, { recursive: true });
let downloadPath: string | undefined;
if (out.trim()) {
const resolvedDownloadPath = resolveDownloadPathOrRespond(res, out);
const resolvedDownloadPath = await resolveDownloadPathOrRespond(res, out);
if (!resolvedDownloadPath) {
return;
}
@@ -504,7 +509,8 @@ export function registerBrowserAgentActRoutes(
targetId,
feature: "download",
run: async ({ cdpUrl, tab, pw }) => {
const downloadPath = resolveDownloadPathOrRespond(res, out);
await fs.mkdir(DEFAULT_DOWNLOAD_DIR, { recursive: true });
const downloadPath = await resolveDownloadPathOrRespond(res, out);
if (!downloadPath) {
return;
}

View File

@@ -8,7 +8,7 @@ import {
resolveTargetIdFromQuery,
withPlaywrightRouteContext,
} from "./agent.shared.js";
import { DEFAULT_TRACE_DIR, resolvePathWithinRoot } from "./path-output.js";
import { DEFAULT_TRACE_DIR, resolveWritablePathWithinRoot } from "./path-output.js";
import type { BrowserRouteRegistrar } from "./types.js";
import { toBoolean, toStringOrEmpty } from "./utils.js";
@@ -122,7 +122,7 @@ export function registerBrowserAgentDebugRoutes(
const id = crypto.randomUUID();
const dir = DEFAULT_TRACE_DIR;
await fs.mkdir(dir, { recursive: true });
const tracePathResult = resolvePathWithinRoot({
const tracePathResult = await resolveWritablePathWithinRoot({
rootDir: dir,
requestedPath: out,
scopeLabel: "trace directory",