import { realpath } from "node:fs/promises"; import { relative, resolve, sep } from "node:path"; export function scopesOverlap(left: readonly string[], right: readonly string[]): boolean { return left.some((path) => right.some((other) => { const a = path.replace(/[\\/]+$/, ""); const b = other.replace(/[\\/]+$/, ""); return a === b || a.startsWith(`${b}/`) || b.startsWith(`${a}/`); })); } export function assertDisjointWriterScopes(scopes: readonly (readonly string[])[]): void { for (let index = 0; index < scopes.length; index += 1) for (let other = index + 1; other < scopes.length; other += 1) if (scopesOverlap(scopes[index]!, scopes[other]!)) throw new Error("Parallel writer scopes overlap"); } export function assertOwnedPaths(paths: readonly string[], scope: readonly string[]): void { const owned = scope.map((path) => path.replace(/^\.\//, "").replace(/[\\/]+$/, "") || "."); for (const path of paths) { const candidate = path.replace(/^\.\//, ""); if (candidate.startsWith("/") || candidate.split(/[\\/]/).includes("..") || !owned.some((root) => root === "." || candidate === root || candidate.startsWith(`${root}/`))) throw new Error(`Writer changed a path outside its owned scope: ${path}`); } } export async function assertPathInScope(root: string, candidate: string): Promise { const [resolvedRoot, resolvedCandidate] = await Promise.all([realpath(root), realpath(candidate)]); const diff = relative(resolvedRoot, resolvedCandidate); if (diff === "" || (!diff.startsWith(`..${sep}`) && diff !== ".." && !resolve(resolvedRoot, diff).startsWith(`${resolvedRoot}${sep}`))) return resolvedCandidate; if (diff.startsWith("..") || resolve(resolvedRoot, diff) !== resolvedCandidate) throw new Error("Writer path escapes its owned scope"); return resolvedCandidate; }