"use strict";
/**
 * Middleware Auth Detection
 * Detects global authentication middleware (Next.js, Express, etc.)
 * and determines which routes are protected
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectGlobalAuthMiddleware = detectGlobalAuthMiddleware;
exports.isRouteProtectedByMiddleware = isRouteProtectedByMiddleware;
exports.getRoutePathFromFile = getRoutePathFromFile;
exports.detectUserScopingPatterns = detectUserScopingPatterns;
/**
 * Detect global auth middleware from scanned files
 * Looks for Next.js middleware.ts, Express auth patterns, etc.
 */
function detectGlobalAuthMiddleware(files) {
    const result = {
        hasAuthMiddleware: false,
        protectedPaths: [],
        publicPaths: [],
        matchers: [],
        context: {
            usesAuthProtect: false,
            usesGetToken: false,
            usesSession: false,
            hasPublicRoutes: false,
        },
    };
    // Find middleware files
    const middlewareFile = files.find(f => /^(src\/)?middleware\.(ts|js)$/.test(f.path) ||
        f.path.endsWith('/middleware.ts') ||
        f.path.endsWith('/middleware.js'));
    if (!middlewareFile) {
        return result;
    }
    result.middlewareFile = middlewareFile.path;
    const content = middlewareFile.content;
    // Detect auth provider type
    result.authType = detectAuthType(content);
    if (result.authType) {
        result.hasAuthMiddleware = true;
    }
    // Extract protected path matchers
    const { protectedPaths, publicPaths, matchers } = extractPathMatchers(content);
    result.protectedPaths = protectedPaths;
    result.publicPaths = publicPaths;
    result.matchers = matchers;
    // Detect specific auth patterns
    result.context.usesAuthProtect = /auth\.protect\(\)|auth\(\)\.protect\(\)|requireAuth\(\)/i.test(content);
    result.context.usesGetToken = /getToken\(|getAuth\(/i.test(content);
    result.context.usesSession = /getSession\(|getServerSession\(/i.test(content);
    result.context.hasPublicRoutes = /isPublicRoute|publicRoutes|ignoredRoutes|excludedPaths/i.test(content);
    // If we found auth imports but no explicit matchers, assume /api/** is protected
    if (result.hasAuthMiddleware && result.protectedPaths.length === 0) {
        // Check if middleware runs on all routes or has default API protection
        if (!result.context.hasPublicRoutes || /\/api/i.test(content)) {
            result.protectedPaths.push('/api/**');
        }
    }
    return result;
}
/**
 * Detect which auth provider/library is being used
 */
function detectAuthType(content) {
    // Clerk patterns
    if (/clerkMiddleware|@clerk\/nextjs|clerkClient|auth\(\)\.protect/i.test(content)) {
        return 'clerk';
    }
    // NextAuth patterns
    if (/next-auth|NextAuth|getServerSession|withAuth\(/i.test(content)) {
        return 'nextauth';
    }
    // Auth0 patterns
    if (/@auth0\/nextjs|withPageAuthRequired|withApiAuthRequired/i.test(content)) {
        return 'auth0';
    }
    // Generic auth middleware patterns
    if (/authMiddleware|requireAuth|verifyToken|checkAuth|isAuthenticated/i.test(content)) {
        return 'custom';
    }
    // Check for common auth imports/usage that suggest middleware is doing auth
    if (/authorization|bearer|jwt|session\.user|getToken/i.test(content)) {
        return 'unknown';
    }
    return undefined;
}
/**
 * Extract path matchers from middleware config
 */
function extractPathMatchers(content) {
    const protectedPaths = [];
    const publicPaths = [];
    const matchers = [];
    // Next.js config.matcher pattern
    // export const config = { matcher: [...] }
    const matcherArrayMatch = content.match(/config\s*=\s*\{[^}]*matcher\s*:\s*\[([\s\S]*?)\]/m);
    if (matcherArrayMatch) {
        const matcherContent = matcherArrayMatch[1];
        // Extract string patterns from the array
        const patterns = matcherContent.match(/['"`]([^'"`]+)['"`]/g) || [];
        for (const p of patterns) {
            const path = p.replace(/['"`]/g, '');
            matchers.push(path);
            // Classify as protected or public based on common patterns
            if (path.includes('/api') || path.includes('/(protected)') || path.includes('/dashboard')) {
                protectedPaths.push(path);
            }
        }
    }
    // Single matcher pattern
    // export const config = { matcher: '/api/:path*' }
    const singleMatcherMatch = content.match(/config\s*=\s*\{[^}]*matcher\s*:\s*['"`]([^'"`]+)['"`]/m);
    if (singleMatcherMatch) {
        const path = singleMatcherMatch[1];
        matchers.push(path);
        if (path.includes('/api')) {
            protectedPaths.push(path);
        }
    }
    // Clerk-specific: createRouteMatcher for public routes
    // const isPublicRoute = createRouteMatcher(['/sign-in', '/sign-up', ...])
    const publicRouteMatch = content.match(/createRouteMatcher\s*\(\s*\[([\s\S]*?)\]/m);
    if (publicRouteMatch) {
        const routeContent = publicRouteMatch[1];
        const patterns = routeContent.match(/['"`]([^'"`]+)['"`]/g) || [];
        for (const p of patterns) {
            const path = p.replace(/['"`]/g, '');
            publicPaths.push(path);
        }
    }
    // Look for publicRoutes/ignoredRoutes arrays
    const publicRoutesMatch = content.match(/(publicRoutes|ignoredRoutes|excludedPaths)\s*[=:]\s*\[([\s\S]*?)\]/m);
    if (publicRoutesMatch) {
        const routeContent = publicRoutesMatch[2];
        const patterns = routeContent.match(/['"`]([^'"`]+)['"`]/g) || [];
        for (const p of patterns) {
            const path = p.replace(/['"`]/g, '');
            publicPaths.push(path);
        }
    }
    // If we found public paths but no explicit protected paths,
    // assume everything else under /api is protected
    if (publicPaths.length > 0 && protectedPaths.length === 0) {
        protectedPaths.push('/api/**');
    }
    return { protectedPaths, publicPaths, matchers };
}
/**
 * Check if a given route path is protected by the middleware
 */
function isRouteProtectedByMiddleware(routePath, config) {
    if (!config.hasAuthMiddleware) {
        return { isProtected: false, reason: 'No auth middleware detected' };
    }
    // Normalize path
    const normalizedPath = routePath.startsWith('/') ? routePath : `/${routePath}`;
    // Check if explicitly public
    for (const publicPath of config.publicPaths) {
        if (matchPath(normalizedPath, publicPath)) {
            return { isProtected: false, reason: `Matches public route pattern: ${publicPath}` };
        }
    }
    // Check if explicitly protected
    for (const protectedPath of config.protectedPaths) {
        if (matchPath(normalizedPath, protectedPath)) {
            return {
                isProtected: true,
                reason: `Protected by ${config.authType || 'auth'} middleware (matches: ${protectedPath})`,
            };
        }
    }
    // Check matchers
    for (const matcher of config.matchers) {
        if (matchPath(normalizedPath, matcher)) {
            return {
                isProtected: true,
                reason: `Protected by middleware matcher: ${matcher}`,
            };
        }
    }
    // Default: if we have auth middleware and no explicit public match,
    // consider API routes as protected
    if (normalizedPath.startsWith('/api/')) {
        return {
            isProtected: true,
            reason: `API route likely protected by ${config.authType || 'auth'} middleware`,
        };
    }
    return { isProtected: false, reason: 'Route not covered by middleware matchers' };
}
/**
 * Extract route path from a file path (e.g., app/api/users/route.ts -> /api/users)
 */
function getRoutePathFromFile(filePath) {
    // Next.js App Router: app/api/users/route.ts -> /api/users
    const appRouterMatch = filePath.match(/app(\/.*?)\/route\.(ts|js)$/i);
    if (appRouterMatch) {
        return appRouterMatch[1];
    }
    // Next.js Pages Router: pages/api/users.ts -> /api/users
    const pagesRouterMatch = filePath.match(/pages(\/api\/.*?)\.(ts|js)$/i);
    if (pagesRouterMatch) {
        return pagesRouterMatch[1];
    }
    // Express-style routes: routes/users.ts (harder to determine path)
    // Return null for now - would need more context
    return null;
}
/**
 * Simple glob-like path matching
 * Supports: ** (any path), * (single segment), :param (path params)
 */
function matchPath(actualPath, pattern) {
    // Normalize
    const normActual = actualPath.replace(/\/+/g, '/');
    let normPattern = pattern.replace(/\/+/g, '/');
    // Handle Next.js :path* patterns
    normPattern = normPattern
        .replace(/:path\*/g, '**')
        .replace(/:\w+/g, '[^/]+');
    // Convert glob to regex
    const regexPattern = normPattern
        .replace(/\*\*/g, '<<<DOUBLESTAR>>>')
        .replace(/\*/g, '[^/]*')
        .replace(/<<<DOUBLESTAR>>>/g, '.*')
        .replace(/\//g, '\\/');
    const regex = new RegExp(`^${regexPattern}$`);
    return regex.test(normActual);
}
/**
 * Get user-scoping patterns from file content
 * Detects patterns like: user_id, userId, session.user.id
 */
function detectUserScopingPatterns(content) {
    const patterns = [];
    // Common user ID patterns in queries/operations
    const userIdPatterns = [
        /\.eq\s*\(\s*['"`]user_id['"`]/i, // Supabase .eq('user_id', ...)
        /\.filter\s*\(\s*user_id\s*=/i, // Django-style
        /where\s*\(\s*['"`]?user_id['"`]?\s*=/i, // SQL-like
        /userId\s*[=:]/i, // Generic userId
        /user\.id\s*[=:]/i, // user.id
        /session\.user\.id/i, // Next.js session
        /req\.user\.id/i, // Express req.user
        /getCurrentUserId\s*\(/i, // Custom helper
        /getAuthUser\s*\(/i, // Auth helper
        /auth\(\).*\.userId/i, // Clerk auth().userId
    ];
    for (const pattern of userIdPatterns) {
        if (pattern.test(content)) {
            patterns.push(pattern.source);
        }
    }
    return {
        hasUserScoping: patterns.length > 0,
        patterns,
    };
}
//# sourceMappingURL=middleware-detector.js.map