/**
 * Path Validation Utilities
 * Provides secure file path validation to prevent path traversal and command injection
 */ import * as path from "path";
import * as fs from "fs";
import { serverLogger } from "./serverLogger.js";
/**
 * Validates that a file path is safe to use and within allowed directories
 * @param filePath - The file path to validate
 * @param allowedBasePaths - Array of allowed base directory paths
 * @returns true if path is valid and safe
 */ export function validateSafeFilePath(filePath, allowedBasePaths) {
    try {
        // 1. Check for null/empty
        if (!filePath || typeof filePath !== "string" || filePath.trim() === "") {
            serverLogger.warn("Empty or invalid file path", {
                filePath
            });
            return false;
        }
        // 2. Check for path traversal patterns (various encodings)
        const pathTraversalPatterns = [
            "../",
            "..\\",
            "%2e%2e%2f",
            "%2e%2e/",
            "..%2f",
            "%2e%2e\\",
            "..%5c",
            "%252e%252e%252f",
            "..%255c"
        ];
        const lowerPath = filePath.toLowerCase();
        for (const pattern of pathTraversalPatterns){
            if (lowerPath.includes(pattern)) {
                serverLogger.warn("Path traversal pattern detected", {
                    filePath,
                    pattern
                });
                return false;
            }
        }
        // 3. Normalize and resolve the path
        const normalizedPath = path.normalize(filePath);
        // For UNC paths, path.resolve may not work as expected, so use normalized path
        const resolvedPath = filePath.startsWith("\\\\") ? normalizedPath : path.resolve(filePath);
        // 4. Reject genuine path-traversal SEGMENTS after normalization — i.e. a
        //    path component that is exactly "..". Do NOT reject paths that merely
        //    CONTAIN ".." inside a filename (e.g. "01..Cream.-.White.Room.flac" or
        //    "track..flac"), which is legal: the previous substring check
        //    (normalizedPath.includes("..")) was a false-positive that blocked
        //    fingerprinting/streaming for such tracks ("Invalid or unsafe file
        //    path"). Defense in depth is preserved: raw "../" / "..\" inputs are
        //    caught in step 2, leading-".." relative traversal yields a ".." segment
        //    caught here, and any absolute traversal that path.normalize() collapses
        //    is caught by the allowedBasePaths boundary check in step 5.
        const hasTraversalSegment = normalizedPath.split(/[/\\]/).some((segment)=>segment === "..");
        if (hasTraversalSegment) {
            serverLogger.warn("Path traversal segment detected after normalization", {
                filePath,
                normalizedPath
            });
            return false;
        }
        // 5. If allowedBasePaths provided, validate against them
        if (allowedBasePaths && allowedBasePaths.length > 0) {
            const isAllowed = allowedBasePaths.some((basePath)=>{
                // Special handling for Windows UNC paths
                if (basePath === "\\\\" && resolvedPath.startsWith("\\\\")) {
                    // Allow any UNC path when "\\\\" is in allowed paths
                    return true;
                }
                // For UNC paths, compare directly without resolve (which may not work correctly)
                if (basePath.startsWith("\\\\") && resolvedPath.startsWith("\\\\")) {
                    const normalizedBase = path.normalize(basePath).toLowerCase();
                    const normalizedResolved = path.normalize(resolvedPath).toLowerCase();
                    return normalizedResolved === normalizedBase || normalizedResolved.startsWith(normalizedBase + "\\");
                }
                const resolvedBase = path.resolve(basePath);
                // Require an exact match or a trailing-separator boundary so a sibling
                // directory that merely shares a name prefix is NOT treated as inside
                // the allowlist: base "/Volumes/Music" must not admit
                // "/Volumes/Music-private/secret.flac".
                return resolvedPath === resolvedBase || resolvedPath.startsWith(resolvedBase + path.sep);
            });
            if (!isAllowed) {
                serverLogger.warn("Path outside allowed directories", {
                    filePath,
                    resolvedPath,
                    allowedBasePaths
                });
                return false;
            }
        }
        return true;
    } catch (error) {
        serverLogger.error("Error validating file path", {
            filePath,
            error: error instanceof Error ? error.message : String(error)
        });
        return false;
    }
}
/**
 * Validates that a file path points to a regular file (not directory, symlink, etc.)
 * and exists on the filesystem
 * @param filePath - The file path to validate
 * @returns true if path is a valid, existing regular file
 */ export function isValidRegularFile(filePath) {
    try {
        if (!fs.existsSync(filePath)) {
            return false;
        }
        const stats = fs.statSync(filePath);
        return stats.isFile() && !stats.isDirectory() && !stats.isSymbolicLink();
    } catch (error) {
        serverLogger.error("Error checking if path is regular file", {
            filePath,
            error: error instanceof Error ? error.message : String(error)
        });
        return false;
    }
}
/**
 * Validates that a file is an audio file based on extension
 * @param filePath - The file path to check
 * @returns true if file has audio extension
 */ export function isAudioFile(filePath) {
    const audioExtensions = [
        ".mp3",
        ".flac",
        ".m4a",
        ".aac",
        ".ogg",
        ".opus",
        ".wav",
        ".wma",
        ".ape",
        ".wv",
        ".tta",
        ".dsf",
        ".dff",
        ".aiff",
        ".aif",
        ".alac"
    ];
    const ext = path.extname(filePath).toLowerCase();
    return audioExtensions.includes(ext);
}
/**
 * DEPRECATED: Use getAllowedMusicPaths() from security.ts instead
 * This function is kept for backward compatibility but should not be used
 *
 * @deprecated Use getAllowedMusicPaths() from utils/security.ts which uses actual library paths
 * @param customPaths - Additional custom paths to allow
 * @returns Array of allowed base paths
 */ export function getAllowedMusicBasePaths(customPaths = []) {
    // SECURITY FIX: Don't use hardcoded paths - this breaks user-defined library paths
    // Import and use the proper function from security.ts
    console.warn("getAllowedMusicBasePaths is deprecated - use getAllowedMusicPaths from security.ts");
    const paths = [
        ...customPaths
    ];
    // Broad platform-specific paths as fallback
    if (process.platform === "darwin") {
        paths.push("/Users", "/Volumes", "/System/Volumes/Data/Users");
    } else if (process.platform === "win32") {
        paths.push("C:\\Users", "C:\\", "D:\\", "E:\\", "F:\\");
    } else {
        paths.push("/home", "/media", "/mnt");
    }
    return paths.filter((p)=>p && p.trim() !== "");
}
/**
 * Comprehensive file path validation for audio processing
 * Validates: safety, existence, type, and allowed locations
 * @param filePath - The file path to validate
 * @param allowedBasePaths - Optional array of allowed base paths
 * @returns true if file is safe, exists, is audio file, and in allowed location
 */ export function validateAudioFilePath(filePath, allowedBasePaths) {
    // 1. Basic safety check
    if (!validateSafeFilePath(filePath, allowedBasePaths)) {
        return false;
    }
    // 2. Check it's a regular file
    if (!isValidRegularFile(filePath)) {
        serverLogger.warn("Path is not a regular file", {
            filePath
        });
        return false;
    }
    // 3. Check it's an audio file
    if (!isAudioFile(filePath)) {
        serverLogger.warn("File is not an audio file", {
            filePath
        });
        return false;
    }
    return true;
}

//# sourceMappingURL=pathValidation.js.map