feat: add external path access allowlist

This commit is contained in:
Federico Jaramillo Martinez
2026-06-23 12:30:30 +02:00
parent 997b821717
commit 9cc20d65fb
35 changed files with 1417 additions and 111 deletions
+6 -5
View File
@@ -1,8 +1,9 @@
import { createReadStream, type ReadStream } from "node:fs";
import { stat } from "node:fs/promises";
import { extname } from "node:path";
import type { PiWebPathAccessConfig } from "../../shared/apiTypes.js";
import { MAX_IMAGE_PREVIEW_BYTES, MAX_IMAGE_PREVIEW_LABEL } from "../../shared/workspaceFiles.js";
import { resolveInsideWorkspace } from "./pathSafety.js";
import { resolveWorkspacePathAccessTarget } from "./pathAccessPolicy.js";
const IMAGE_MIME_TYPES: Record<string, string | undefined> = {
".avif": "image/avif",
@@ -28,16 +29,16 @@ export function imageMimeTypeForPath(path: string): string | undefined {
return IMAGE_MIME_TYPES[extname(path).toLowerCase()];
}
export async function readWorkspaceImagePreview(rootPath: string, path: string | undefined): Promise<WorkspaceImagePreview> {
export async function readWorkspaceImagePreview(rootPath: string, path: string | undefined, pathAccess?: PiWebPathAccessConfig): Promise<WorkspaceImagePreview> {
if (path === undefined || path === "") throw new Error("path query parameter is required");
const { target, relativePath } = await resolveInsideWorkspace(rootPath, path);
const { target, displayPath } = await resolveWorkspacePathAccessTarget(rootPath, path, pathAccess);
const s = await stat(target);
if (!s.isFile()) throw new Error("Path is not a file");
const mimeType = imageMimeTypeForPath(relativePath);
const mimeType = imageMimeTypeForPath(displayPath);
if (mimeType === undefined) throw new Error("Image preview is not supported for this file type");
if (s.size > MAX_IMAGE_PREVIEW_BYTES) throw new Error(`Image is too large to preview (limit ${MAX_IMAGE_PREVIEW_LABEL})`);
return {
path: relativePath,
path: displayPath,
mimeType,
size: s.size,
modifiedAt: s.mtime.toISOString(),