forked from EduCraft/curriculum-project-hub
fix: allow arbitrary workspace file delivery
This commit is contained in:
@@ -37,6 +37,7 @@ export async function readWorkspaceFileNoFollow(
|
||||
workspaceRoot: string,
|
||||
workspaceDir: string,
|
||||
requestedPath: string,
|
||||
maxBytes?: number,
|
||||
): Promise<WorkspaceFileSnapshot> {
|
||||
const workspace = await canonicalWorkspace(workspaceRoot, workspaceDir);
|
||||
const components = fileComponents(workspace, requestedPath);
|
||||
@@ -53,8 +54,15 @@ export async function readWorkspaceFileNoFollow(
|
||||
if (!metadata.isFile()) {
|
||||
throw new WorkspaceFileBoundaryError(`deliverable is not a regular file: ${requestedPath}`, requestedPath);
|
||||
}
|
||||
if (maxBytes !== undefined && metadata.size > maxBytes) {
|
||||
throw new WorkspaceFileBoundaryError(
|
||||
`workspace file exceeds ${maxBytes} bytes: ${requestedPath}`,
|
||||
requestedPath,
|
||||
"limit",
|
||||
);
|
||||
}
|
||||
await assertNameStillReferences(parent, name, metadata.dev, metadata.ino, requestedPath);
|
||||
const data = await file.readFile();
|
||||
const data = await readFileSnapshot(file, maxBytes, requestedPath);
|
||||
result = { path: join(workspace, ...components), name, data };
|
||||
} catch (error) {
|
||||
failure = boundaryError(error, requestedPath, "cannot read workspace file without following symlinks");
|
||||
@@ -67,6 +75,33 @@ export async function readWorkspaceFileNoFollow(
|
||||
return result!;
|
||||
}
|
||||
|
||||
async function readFileSnapshot(
|
||||
file: FileHandle,
|
||||
maxBytes: number | undefined,
|
||||
requestedPath: string,
|
||||
): Promise<Buffer> {
|
||||
if (maxBytes === undefined) return file.readFile();
|
||||
const chunks: Buffer[] = [];
|
||||
let total = 0;
|
||||
let position = 0;
|
||||
while (true) {
|
||||
const remainingWithSentinel = maxBytes - total + 1;
|
||||
const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, remainingWithSentinel));
|
||||
const { bytesRead } = await file.read(chunk, 0, chunk.length, position);
|
||||
if (bytesRead === 0) return Buffer.concat(chunks, total);
|
||||
total += bytesRead;
|
||||
if (total > maxBytes) {
|
||||
throw new WorkspaceFileBoundaryError(
|
||||
`workspace file exceeds ${maxBytes} bytes: ${requestedPath}`,
|
||||
requestedPath,
|
||||
"limit",
|
||||
);
|
||||
}
|
||||
chunks.push(chunk.subarray(0, bytesRead));
|
||||
position += bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create one inbound file exclusively below the workspace and stream into its
|
||||
* already-open descriptor. Linux uses /proc/self/fd-relative traversal so a
|
||||
|
||||
Reference in New Issue
Block a user