Safe Path Join Blocking Traversal

A path resolver that joins untrusted segments onto a base directory and rejects any attempt to escape it via dot dot segments or absolute paths.

TypescriptSecurity Boundaries

Code

import { isAbsolute, join, normalize, resolve, sep } from "node:path";

export class PathTraversalError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "PathTraversalError";
  }
}

/**
 * Resolve untrusted segments against a base directory, refusing any escape.
 * Containment is verified with an explicit prefix check after normalization. */
export function safeJoin(baseDir: string, ...segments: string[]): string {
  const base = resolve(baseDir);
  for (const segment of segments) {
    if (typeof segment !== "string" || segment.length === 0) {
      throw new PathTraversalError("path segments must be non-empty strings");
    }
    if (isAbsolute(segment)) {
      throw new PathTraversalError("absolute path segments are not allowed: " + segment);
    }
  }
  const target = normalize(join(base, ...segments));
  if (target !== base && !target.startsWith(base + sep)) {
    throw new PathTraversalError("resolved path escapes the base directory: " + target);
  }
  return target;
}