import { isAbsolute, relative, resolve } from "node:path";
import { realpathSync } from "node:fs";

function isWithin(parentPath: string, candidatePath: string): boolean {
  const rel = relative(parentPath, candidatePath);
  return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
}

/**
 * Checks if a path is allowed under any of the allowedRoots.
 * - Normalizes and resolves symlinks.
 * - Prevents traversal outside allowedRoots.
 */
export function isPathAllowed(path: string, allowedRoots: string[]): boolean {
  if (!Array.isArray(allowedRoots) || allowedRoots.length === 0) return false;
  let realTarget: string;
  try {
    realTarget = realpathSync.native(resolve(path));
  } catch {
    return false;
  }

  for (const root of allowedRoots) {
    let realRoot: string;
    try {
      realRoot = realpathSync.native(resolve(root));
    } catch {
      continue;
    }
    if (isWithin(realRoot, realTarget)) {
      return true;
    }
  }

  return false;
}
