/**
 * Path sandboxing utilities for preventing directory traversal attacks.
 * All filesystem operations must validate paths through these functions.
 */
import { realpath } from "node:fs/promises";
import { resolve } from "node:path";
import { ErrorCode } from "@pocketcoder/shared/constants";
import { AppError } from "../errors/index.js";
/**
 * Validates that a resolved path stays within the project root.
 * Resolves symlinks to prevent symlink-based escapes.
 * Throws AppError with OUTSIDE_PROJECT_ROOT if path escapes.
 *
 * @param projectRoot - Absolute path to the project root directory
 * @param relativePath - Relative path within the project
 * @returns The resolved absolute path
 */
export async function validateProjectPath(projectRoot, relativePath) {
    const resolvedRoot = await realpath(projectRoot);
    // Reject absolute paths that don't start with the project root
    if (relativePath.startsWith("/") || relativePath.startsWith("\\")) {
        throw new AppError(ErrorCode.OUTSIDE_PROJECT_ROOT, "Absolute paths are not allowed");
    }
    const targetPath = resolve(resolvedRoot, relativePath);
    let resolvedTarget;
    try {
        resolvedTarget = await realpath(targetPath);
    }
    catch (err) {
        // If file doesn't exist, we can still check the normalized path
        if (err &&
            typeof err === "object" &&
            "code" in err &&
            err.code === "ENOENT") {
            // Normalize to remove .. segments and check
            const normalized = resolve(targetPath);
            if (!normalized.startsWith(resolvedRoot + "/") && normalized !== resolvedRoot) {
                throw new AppError(ErrorCode.OUTSIDE_PROJECT_ROOT, "Path escapes project directory");
            }
            return normalized;
        }
        throw err;
    }
    if (!resolvedTarget.startsWith(resolvedRoot + "/") && resolvedTarget !== resolvedRoot) {
        throw new AppError(ErrorCode.OUTSIDE_PROJECT_ROOT, "Path escapes project directory");
    }
    return resolvedTarget;
}
/**
 * Validates a path for writing (target may not exist yet).
 * Checks the normalized path stays within the project root.
 *
 * @param projectRoot - Absolute path to the project root directory
 * @param relativePath - Relative path within the project
 * @returns The normalized absolute path
 */
export async function validateParentPath(projectRoot, relativePath) {
    const resolvedRoot = await realpath(projectRoot);
    if (relativePath.startsWith("/") || relativePath.startsWith("\\")) {
        throw new AppError(ErrorCode.OUTSIDE_PROJECT_ROOT, "Absolute paths are not allowed");
    }
    const targetPath = resolve(resolvedRoot, relativePath);
    const normalized = resolve(targetPath);
    if (!normalized.startsWith(resolvedRoot + "/") && normalized !== resolvedRoot) {
        throw new AppError(ErrorCode.OUTSIDE_PROJECT_ROOT, "Path escapes project directory");
    }
    return normalized;
}
//# sourceMappingURL=path-sandbox.js.map