import { describe, it, expect, jest, beforeEach } from "@jest/globals";
import request from "supertest";
import express from "express";
import jwt from "jsonwebtoken";
import { validateFilePath, sanitizeInput } from "../../utils/security.js";
// Mock environment variables
process.env.JWT_SECRET = "test-secret-key-for-testing-only-minimum-32-chars";
process.env.NODE_ENV = "test";
// Mock fs module
jest.mock("fs", ()=>({
        existsSync: jest.fn((testPath)=>{
            // Mock existence of valid paths
            const validRoots = [
                "/music",
                "/media"
            ];
            const path = String(testPath);
            // Check if path is a valid root
            if (validRoots.includes(path)) return true;
            // Check if path starts with a valid root and doesn't contain traversal
            for (const root of validRoots){
                if (path === root) return true;
                if (path.startsWith(root + "/")) {
                    const subPath = path.substring(root.length);
                    // Allow any subdirectory structure as long as it doesn't contain traversal
                    if (!subPath.includes("../") && !subPath.includes("..\\") && !subPath.includes("/../")) {
                        return true;
                    }
                }
            }
            // Special case for /media/music
            if (path === "/media/music" || path.startsWith("/media/music/")) {
                const subPath = path.substring("/media/music".length);
                return !subPath.includes("../") && !subPath.includes("..\\");
            }
            return false;
        }),
        realpathSync: jest.fn((testPath)=>{
            // Mock realpath - just return the normalized path for valid paths
            const path = String(testPath);
            if (path.includes("..")) throw new Error("Invalid path");
            return path;
        }),
        mkdirSync: jest.fn(),
        rmSync: jest.fn()
    }));
describe("Security Features", ()=>{
    describe("Path Traversal Protection", ()=>{
        const allowedPaths = [
            "/music",
            "/media/music"
        ];
        it("should block path traversal attempts", ()=>{
            expect(validateFilePath("/music/../etc/passwd", allowedPaths)).toBe(false);
            expect(validateFilePath("/music/../../etc/shadow", allowedPaths)).toBe(false);
            expect(validateFilePath("/music/%2e%2e%2f%2e%2e%2fetc/passwd", allowedPaths)).toBe(false);
            expect(validateFilePath("/music/./../../etc/passwd", allowedPaths)).toBe(false);
        });
        it("should block absolute paths outside allowed directories", ()=>{
            expect(validateFilePath("/etc/passwd", allowedPaths)).toBe(false);
            expect(validateFilePath("/home/user/.ssh/id_rsa", allowedPaths)).toBe(false);
            expect(validateFilePath("C:\\Windows\\System32\\config\\SAM", allowedPaths)).toBe(false);
        });
        it("should reject paths with parent directory references", ()=>{
            // Path traversal attempts should fail
            expect(validateFilePath("/music/../etc/passwd", allowedPaths)).toBe(false);
            expect(validateFilePath("/music/artist/../../etc/passwd", allowedPaths)).toBe(false);
        });
        it("should handle URL encoded traversal attempts", ()=>{
            // Encoded traversal should be blocked
            expect(validateFilePath("/music/%2e%2e/secret", allowedPaths)).toBe(false);
            expect(validateFilePath("/music/%2e%2e%2fsecret", allowedPaths)).toBe(false);
        });
        it("should handle null bytes", ()=>{
            expect(validateFilePath("/music/file.mp3\x00.txt", allowedPaths)).toBe(false);
            expect(validateFilePath("/music/file.mp3%00.txt", allowedPaths)).toBe(false);
        });
    });
    describe("SQL Injection Protection", ()=>{
        it("should validate orderBy parameters", ()=>{
            const allowedOrderBy = [
                "title",
                "artist",
                "album",
                "year"
            ];
            // Valid values
            expect(allowedOrderBy.includes("title")).toBe(true);
            expect(allowedOrderBy.includes("artist")).toBe(true);
            // Invalid values that could be SQL injection
            expect(allowedOrderBy.includes("title; DROP TABLE tracks;")).toBe(false);
            expect(allowedOrderBy.includes("artist UNION SELECT * FROM users")).toBe(false);
            expect(allowedOrderBy.includes("1' OR '1'='1")).toBe(false);
        });
    });
    describe("Input Sanitization", ()=>{
        it("should sanitize HTML and script tags", ()=>{
            expect(sanitizeInput('<script>alert("xss")</script>')).toBe("");
            expect(sanitizeInput('<img src=x onerror=alert("xss")>')).toBe("");
            expect(sanitizeInput("Hello <b>World</b>")).toBe("Hello World");
        });
        it("should handle special characters", ()=>{
            expect(sanitizeInput("Test & Co.")).toBe("Test &amp; Co.");
            expect(sanitizeInput("Price < 100")).toBe("Price &lt; 100");
            expect(sanitizeInput('Quote: "Hello"')).toBe("Quote: &quot;Hello&quot;");
        });
        it("should trim whitespace", ()=>{
            expect(sanitizeInput("  test  ")).toBe("test");
            expect(sanitizeInput("\n\ttest\n\t")).toBe("test");
        });
    });
});
describe("JWT Authentication", ()=>{
    let app;
    const validToken = jwt.sign({
        userId: "test-user"
    }, process.env.JWT_SECRET);
    const invalidToken = jwt.sign({
        userId: "test-user"
    }, "wrong-secret");
    const expiredToken = jwt.sign({
        userId: "test-user"
    }, process.env.JWT_SECRET, {
        expiresIn: "-1h"
    });
    beforeEach(()=>{
        app = express();
        app.use(express.json());
        // Mock auth middleware
        const checkAuth = (req, res, next)=>{
            const token = req.headers.authorization?.replace("Bearer ", "");
            if (!token) {
                return res.status(401).json({
                    error: "No token provided"
                });
            }
            try {
                const decoded = jwt.verify(token, process.env.JWT_SECRET);
                req.user = decoded;
                next();
            } catch (error) {
                return res.status(401).json({
                    error: "Invalid token"
                });
            }
        };
        // Protected endpoint
        app.get("/api/protected", checkAuth, (req, res)=>{
            res.json({
                message: "Success",
                user: req.user
            });
        });
        // Public endpoint
        app.get("/api/public", (req, res)=>{
            res.json({
                message: "Public access"
            });
        });
    });
    it("should block access without token", async ()=>{
        const response = await request(app).get("/api/protected");
        expect(response.status).toBe(401);
        expect(response.body.error).toBe("No token provided");
    });
    it("should block access with invalid token", async ()=>{
        const response = await request(app).get("/api/protected").set("Authorization", `Bearer ${invalidToken}`);
        expect(response.status).toBe(401);
        expect(response.body.error).toBe("Invalid token");
    });
    it("should block access with expired token", async ()=>{
        const response = await request(app).get("/api/protected").set("Authorization", `Bearer ${expiredToken}`);
        expect(response.status).toBe(401);
        expect(response.body.error).toBe("Invalid token");
    });
    it("should allow access with valid token", async ()=>{
        const response = await request(app).get("/api/protected").set("Authorization", `Bearer ${validToken}`);
        expect(response.status).toBe(200);
        expect(response.body.user.userId).toBe("test-user");
    });
    it("should allow public endpoints without token", async ()=>{
        const response = await request(app).get("/api/public");
        expect(response.status).toBe(200);
        expect(response.body.message).toBe("Public access");
    });
    it("should handle malformed tokens", async ()=>{
        const response = await request(app).get("/api/protected").set("Authorization", "Bearer malformed.token.here");
        expect(response.status).toBe(401);
    });
});
describe("Rate Limiting", ()=>{
    let app;
    let rateLimitedRequests = 0;
    beforeEach(()=>{
        rateLimitedRequests = 0;
        app = express();
        // Mock rate limiter
        const rateLimit = (limit)=>(req, res, next)=>{
                rateLimitedRequests++;
                if (rateLimitedRequests > limit) {
                    return res.status(429).json({
                        error: "Too many requests"
                    });
                }
                next();
            };
        app.get("/api/limited", rateLimit(5), (req, res)=>{
            res.json({
                message: "Success"
            });
        });
    });
    it("should allow requests under the limit", async ()=>{
        for(let i = 0; i < 5; i++){
            const response = await request(app).get("/api/limited");
            expect(response.status).toBe(200);
        }
    });
    it("should block requests over the limit", async ()=>{
        // Make 5 successful requests
        for(let i = 0; i < 5; i++){
            await request(app).get("/api/limited");
        }
        // 6th request should be rate limited
        const response = await request(app).get("/api/limited");
        expect(response.status).toBe(429);
        expect(response.body.error).toBe("Too many requests");
    });
});
describe("CORS Configuration", ()=>{
    let app;
    beforeEach(()=>{
        app = express();
        // Mock CORS middleware
        const corsMiddleware = (req, res, next)=>{
            const origin = req.headers.origin;
            if (process.env.NODE_ENV === "development") {
                res.setHeader("Access-Control-Allow-Origin", origin || "*");
            } else {
                const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",") || [];
                if (allowedOrigins.includes(origin)) {
                    res.setHeader("Access-Control-Allow-Origin", origin);
                }
            }
            res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
            if (req.method === "OPTIONS") {
                return res.sendStatus(204);
            }
            next();
        };
        app.use(corsMiddleware);
        app.get("/api/test", (req, res)=>res.json({
                message: "Success"
            }));
    });
    it("should handle preflight requests", async ()=>{
        const response = await request(app).options("/api/test").set("Origin", "http://localhost:3000");
        expect(response.status).toBe(204);
        expect(response.headers["access-control-allow-methods"]).toBe("GET, POST, PUT, DELETE, OPTIONS");
    });
    it("should allow any origin in development", async ()=>{
        process.env.NODE_ENV = "development";
        const response = await request(app).get("/api/test").set("Origin", "http://evil.com");
        expect(response.headers["access-control-allow-origin"]).toBe("http://evil.com");
    });
    it("should restrict origins in production", async ()=>{
        process.env.NODE_ENV = "production";
        process.env.ALLOWED_ORIGINS = "http://localhost:3000,https://app.example.com";
        const response = await request(app).get("/api/test").set("Origin", "http://evil.com");
        expect(response.headers["access-control-allow-origin"]).toBeUndefined();
    });
});
describe("Error Handling", ()=>{
    let app;
    beforeEach(()=>{
        app = express();
        app.use(express.json());
        app.get("/api/error", (req, res)=>{
            throw new Error("Database connection failed at 192.168.1.100:5432");
        });
        app.post("/api/validate", (req, res)=>{
            if (!req.body.name) {
                return res.status(400).json({
                    error: "Name is required"
                });
            }
            res.json({
                success: true
            });
        });
        // Error handler
        app.use((err, req, res, _next)=>{
            const isDev = process.env.NODE_ENV === "development";
            res.status(500).json({
                error: isDev ? err.message : "Internal server error",
                ...isDev && {
                    stack: err.stack
                }
            });
        });
    });
    it("should hide sensitive error details in production", async ()=>{
        process.env.NODE_ENV = "production";
        const response = await request(app).get("/api/error");
        expect(response.status).toBe(500);
        expect(response.body.error).toBe("Internal server error");
        expect(response.body.stack).toBeUndefined();
        expect(response.body.error).not.toContain("192.168.1.100");
    });
    it("should show error details in development", async ()=>{
        process.env.NODE_ENV = "development";
        const response = await request(app).get("/api/error");
        expect(response.status).toBe(500);
        expect(response.body.error).toContain("Database connection failed");
        expect(response.body.stack).toBeDefined();
    });
    it("should validate required fields", async ()=>{
        const response = await request(app).post("/api/validate").send({});
        expect(response.status).toBe(400);
        expect(response.body.error).toBe("Name is required");
    });
});
describe("Security Headers", ()=>{
    let app;
    beforeEach(()=>{
        app = express();
        // Security headers middleware
        app.use((req, res, next)=>{
            res.setHeader("X-Content-Type-Options", "nosniff");
            res.setHeader("X-Frame-Options", "DENY");
            res.setHeader("X-XSS-Protection", "1; mode=block");
            res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
            res.setHeader("Content-Security-Policy", "default-src 'self'");
            res.removeHeader("X-Powered-By");
            next();
        });
        app.get("/api/test", (req, res)=>res.json({
                message: "Success"
            }));
    });
    it("should set security headers", async ()=>{
        const response = await request(app).get("/api/test");
        expect(response.headers["x-content-type-options"]).toBe("nosniff");
        expect(response.headers["x-frame-options"]).toBe("DENY");
        expect(response.headers["x-xss-protection"]).toBe("1; mode=block");
        expect(response.headers["strict-transport-security"]).toBe("max-age=31536000; includeSubDomains");
        expect(response.headers["content-security-policy"]).toBe("default-src 'self'");
        expect(response.headers["x-powered-by"]).toBeUndefined();
    });
});

//# sourceMappingURL=security.test.js.map