All files / server/security safe-paths.ts

100% Statements 6/6
100% Branches 6/6
100% Functions 1/1
100% Lines 6/6

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22                174x 174x 174x 174x           3x   171x    
import { relative, resolve } from "node:path";
 
/**
 * Resolve a path below a known root and reject any target outside that root.
 * This is defense in depth for values that have already passed a filename
 * schema: validation can regress, but filesystem confinement must not.
 */
export function resolvePathWithinRoot(root: string, ...segments: string[]): string {
  const resolvedRoot = resolve(root);
  const target = resolve(resolvedRoot, ...segments);
  const pathFromRoot = relative(resolvedRoot, target);
  if (
    pathFromRoot === "" ||
    pathFromRoot === ".." ||
    pathFromRoot.startsWith(`..${String.fromCodePoint(47)}`) ||
    pathFromRoot.startsWith(`..${String.fromCodePoint(92)}`)
  ) {
    throw new Error("Resolved path escapes its allowed root");
  }
  return target;
}