Skip to main content
Glama
sanitize.test.ts6.54 kB
/** * Unit tests for input sanitization */ import { describe, it, expect, vi, afterEach } from "vitest"; import { sanitizeText, sanitizeProblemDescription, sanitizeQuery, validateModelCode, validateTransformationKey, rateLimiter, } from "../utils/sanitize.js"; describe("Input Sanitization", () => { describe("sanitizeText", () => { it("should remove script tags", () => { const input = "Hello <script>alert('xss')</script> world"; const result = sanitizeText(input); expect(result).not.toContain("<script>"); expect(result).toContain("Hello"); expect(result).toContain("world"); }); it("should remove javascript protocol", () => { const input = "Click javascript:alert('xss') here"; const result = sanitizeText(input); expect(result).not.toContain("javascript:"); }); it("should remove event handlers", () => { const input = "<div onclick=\"alert('xss')\">Test</div>"; const result = sanitizeText(input); expect(result).not.toContain("onclick="); }); it("should enforce length limits", () => { const input = "a".repeat(10000); const result = sanitizeText(input, 100); expect(result.length).toBe(100); }); it("should preserve legitimate text", () => { const input = "This is a normal problem description with numbers 123"; const result = sanitizeText(input); expect(result).toBe(input); }); it("should trim whitespace", () => { const input = " spaces around "; const result = sanitizeText(input); expect(result).toBe("spaces around"); }); it("should handle empty input", () => { expect(sanitizeText("")).toBe(""); expect(sanitizeText(null as any)).toBe(""); expect(sanitizeText(undefined as any)).toBe(""); }); }); describe("sanitizeProblemDescription", () => { it("should accept valid problem descriptions", () => { const input = "Our startup is growing rapidly but systems are breaking"; const result = sanitizeProblemDescription(input); expect(result).toBe(input); }); it("should reject too short descriptions", () => { expect(() => sanitizeProblemDescription("short")).toThrow(); }); it("should sanitize and validate", () => { const input = "Long problem <script>alert('xss')</script> description here"; const result = sanitizeProblemDescription(input); expect(result).not.toContain("<script>"); expect(result.length).toBeGreaterThanOrEqual(10); }); }); describe("sanitizeQuery", () => { it("should accept valid queries", () => { const input = "decision making"; const result = sanitizeQuery(input); expect(result).toBe(input); }); it("should reject too short queries", () => { expect(() => sanitizeQuery("a")).toThrow(); }); it("should enforce query length limits", () => { const input = "a".repeat(1000); const result = sanitizeQuery(input); expect(result.length).toBeLessThanOrEqual(500); }); }); describe("validateModelCode", () => { it("should accept valid codes", () => { const validCodes = ["P1", "IN5", "CO10", "DE15", "RE20", "SY3"]; validCodes.forEach((code) => { expect(validateModelCode(code)).toBe(code); }); }); it("should normalize to uppercase", () => { expect(validateModelCode("p1")).toBe("P1"); expect(validateModelCode("in5")).toBe("IN5"); }); it("should reject invalid formats", () => { expect(() => validateModelCode("INVALID")).toThrow(); expect(() => validateModelCode("P0")).toThrow(); expect(() => validateModelCode("P21")).toThrow(); expect(() => validateModelCode("XX1")).toThrow(); }); it("should reject empty or null", () => { expect(() => validateModelCode("")).toThrow(); expect(() => validateModelCode(null as any)).toThrow(); }); }); describe("validateTransformationKey", () => { it("should accept valid keys", () => { const validKeys = ["P", "IN", "CO", "DE", "RE", "SY"]; validKeys.forEach((key) => { expect(validateTransformationKey(key)).toBe(key); }); }); it("should normalize to uppercase", () => { expect(validateTransformationKey("p")).toBe("P"); expect(validateTransformationKey("in")).toBe("IN"); }); it("should reject invalid keys", () => { expect(() => validateTransformationKey("INVALID")).toThrow(); expect(() => validateTransformationKey("XX")).toThrow(); }); it("should reject empty or null", () => { expect(() => validateTransformationKey("")).toThrow(); expect(() => validateTransformationKey(null as any)).toThrow(); }); }); describe("RateLimiter", () => { afterEach(() => { rateLimiter.clear("limit-test-user"); rateLimiter.clear("cleanup-test-user"); vi.useRealTimers(); }); it("should allow requests within limit", () => { rateLimiter.clear("test-user-1"); expect(rateLimiter.allow("test-user-1")).toBe(true); expect(rateLimiter.allow("test-user-1")).toBe(true); }); it("should track remaining requests", () => { rateLimiter.clear("test-user-2"); const initial = rateLimiter.remaining("test-user-2"); rateLimiter.allow("test-user-2"); const after = rateLimiter.remaining("test-user-2"); expect(after).toBe(initial - 1); }); it("should clear rate limits", () => { rateLimiter.clear("test-user-3"); rateLimiter.allow("test-user-3"); expect(rateLimiter.remaining("test-user-3")).toBeLessThan(100); rateLimiter.clear("test-user-3"); expect(rateLimiter.remaining("test-user-3")).toBe(100); }); it("should reject when exceeding max requests", () => { rateLimiter.clear("limit-test-user"); for (let i = 0; i < 100; i += 1) { expect(rateLimiter.allow("limit-test-user")).toBe(true); } expect(rateLimiter.allow("limit-test-user")).toBe(false); }); it("should clean up expired timestamps", () => { vi.useFakeTimers(); const now = Date.now(); rateLimiter.clear("cleanup-test-user"); vi.setSystemTime(now); for (let i = 0; i < 5; i += 1) { expect(rateLimiter.allow("cleanup-test-user")).toBe(true); } vi.advanceTimersByTime(60001); // Advance beyond window rateLimiter.cleanup(); expect(rateLimiter.remaining("cleanup-test-user")).toBe(100); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hummbl-dev/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server