forked from EduCraft/curriculum-project-hub
fix: allow arbitrary workspace file delivery
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { extname, isAbsolute, join } from "node:path";
|
||||
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
import {
|
||||
WorkspaceFileBoundaryError,
|
||||
readWorkspaceFileNoFollow,
|
||||
@@ -10,39 +10,21 @@ export interface DeliverableFile {
|
||||
readonly data: Buffer;
|
||||
}
|
||||
|
||||
const DELIVERABLE_EXTENSIONS = new Set([
|
||||
".csv",
|
||||
".doc",
|
||||
".docx",
|
||||
".gif",
|
||||
".jpeg",
|
||||
".jpg",
|
||||
".md",
|
||||
".pdf",
|
||||
".png",
|
||||
".ppt",
|
||||
".pptx",
|
||||
".svg",
|
||||
".txt",
|
||||
".typ",
|
||||
".xls",
|
||||
".xlsx",
|
||||
".zip",
|
||||
]);
|
||||
const DELIVERABLE_CPH_DIRECTORY = "inbox";
|
||||
|
||||
export async function resolveDeliverableFile(
|
||||
requestedPath: string,
|
||||
workspaceRoot: string,
|
||||
workspaceDir: string,
|
||||
maxBytes?: number,
|
||||
): Promise<DeliverableFile | null> {
|
||||
const token = requestedPath.trim();
|
||||
if (token === "" || !DELIVERABLE_EXTENSIONS.has(extname(token).toLowerCase())) {
|
||||
return null;
|
||||
}
|
||||
if (token === "") return null;
|
||||
|
||||
for (const candidate of candidatePaths(token)) {
|
||||
assertNotPlatformRuntimePath(candidate, workspaceDir);
|
||||
try {
|
||||
return await readWorkspaceFileNoFollow(workspaceRoot, workspaceDir, candidate);
|
||||
return await readWorkspaceFileNoFollow(workspaceRoot, workspaceDir, candidate, maxBytes);
|
||||
} catch (error) {
|
||||
if (!(error instanceof WorkspaceFileBoundaryError) || error.reason !== "not_found") {
|
||||
throw error;
|
||||
@@ -54,6 +36,20 @@ export async function resolveDeliverableFile(
|
||||
return null;
|
||||
}
|
||||
|
||||
function assertNotPlatformRuntimePath(candidate: string, workspaceDir: string): void {
|
||||
const workspace = resolve(workspaceDir);
|
||||
const target = isAbsolute(candidate) ? resolve(candidate) : resolve(workspace, candidate);
|
||||
const rel = relative(workspace, target);
|
||||
const components = rel.split(sep);
|
||||
if (components[0] === ".cph" && components[1] !== DELIVERABLE_CPH_DIRECTORY) {
|
||||
throw new WorkspaceFileBoundaryError(
|
||||
`platform runtime files cannot be delivered: ${candidate}`,
|
||||
candidate,
|
||||
"boundary",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function candidatePaths(token: string): string[] {
|
||||
if (isAbsolute(token)) return [token];
|
||||
const candidates = [token];
|
||||
|
||||
Reference in New Issue
Block a user