Skip to main content
Glama
http.test.ts9.57 kB
import { describe, test, expect } from "bun:test"; import { parseBasicAuth } from "../../../src/auth/http.js"; import { createHash } from "crypto"; describe("HTTP Authentication", () => { describe("parseBasicAuth", () => { test("should parse valid Basic Auth header", () => { const email = "test@example.com"; const password = "mypassword123"; const authHeader = `Basic ${Buffer.from(`${email}:${password}`).toString("base64")}`; const result = parseBasicAuth(authHeader); expect(result.email).toBe(email); expect(result.password).toBe(password); }); test("should handle special characters in password", () => { const email = "user@domain.com"; const password = "p@ssw0rd!#$%^&*()"; const authHeader = `Basic ${Buffer.from(`${email}:${password}`).toString("base64")}`; const result = parseBasicAuth(authHeader); expect(result.email).toBe(email); expect(result.password).toBe(password); }); test("should handle colons in password", () => { const email = "admin@test.com"; const password = "pass:with:colons"; const authHeader = `Basic ${Buffer.from(`${email}:${password}`).toString("base64")}`; const result = parseBasicAuth(authHeader); expect(result.email).toBe(email); expect(result.password).toBe(password); }); test("should handle empty password", () => { const email = "user@example.com"; const password = ""; const authHeader = `Basic ${Buffer.from(`${email}:${password}`).toString("base64")}`; const result = parseBasicAuth(authHeader); expect(result.email).toBe(email); expect(result.password).toBe(password); }); test("should reject malformed Base64", () => { const authHeader = "Basic not-valid-base64!!!"; // Will throw or return unexpected results expect(() => parseBasicAuth(authHeader)).toThrow(); }); test("should reject auth without email", () => { const authHeader = `Basic ${Buffer.from(":password").toString("base64")}`; expect(() => parseBasicAuth(authHeader)).toThrow("Invalid Basic Auth format"); }); test("should reject auth without colon separator", () => { const authHeader = `Basic ${Buffer.from("emailwithoutcolon").toString("base64")}`; expect(() => parseBasicAuth(authHeader)).toThrow("Invalid Basic Auth format"); }); }); describe("Cache Key Generation (Security)", () => { // Note: We're testing the concept, not the actual implementation // since getCacheKey is not exported function getCacheKey(email: string, password: string): string { return createHash("sha256") .update(`${email}:${password}`) .digest("hex"); } test("should generate different keys for different passwords", () => { const email = "test@example.com"; const password1 = "password1"; const password2 = "password2"; const key1 = getCacheKey(email, password1); const key2 = getCacheKey(email, password2); expect(key1).not.toBe(key2); }); test("should generate different keys for different emails", () => { const email1 = "user1@example.com"; const email2 = "user2@example.com"; const password = "samepassword"; const key1 = getCacheKey(email1, password); const key2 = getCacheKey(email2, password); expect(key1).not.toBe(key2); }); test("should generate same key for identical credentials", () => { const email = "test@example.com"; const password = "mypassword"; const key1 = getCacheKey(email, password); const key2 = getCacheKey(email, password); expect(key1).toBe(key2); }); test("should generate deterministic hashes", () => { const email = "consistent@example.com"; const password = "consistent-password"; const key1 = getCacheKey(email, password); // Wait a bit const key2 = getCacheKey(email, password); expect(key1).toBe(key2); }); test("should produce hex string of correct length", () => { const key = getCacheKey("user@test.com", "password123"); // SHA-256 produces 64 character hex string expect(key.length).toBe(64); expect(/^[0-9a-f]+$/.test(key)).toBe(true); }); test("should handle special characters in credentials", () => { const email = "user+tag@example.com"; const password = "p@ss!w0rd#$%"; const key = getCacheKey(email, password); expect(key.length).toBe(64); expect(typeof key).toBe("string"); }); test("should prevent authentication bypass attack", () => { // Attack scenario: Try to use correct email but wrong password const correctEmail = "legitimate@example.com"; const correctPassword = "correct-password"; const wrongPassword = "wrong-password"; const legitKey = getCacheKey(correctEmail, correctPassword); const attackKey = getCacheKey(correctEmail, wrongPassword); // These should be completely different expect(legitKey).not.toBe(attackKey); // And have no predictable relationship expect(legitKey.substring(0, 10)).not.toBe(attackKey.substring(0, 10)); }); }); describe("Session Data TTL Validation", () => { test("should validate idle timeout correctly", () => { const CLIENT_IDLE_TIMEOUT = 10 * 60 * 1000; // 10 minutes const now = Date.now(); // Valid: accessed 5 minutes ago const validSession = { lastAccessedAt: now - 5 * 60 * 1000, createdAt: now - 30 * 60 * 1000, }; const idleTime = now - validSession.lastAccessedAt; expect(idleTime).toBeLessThan(CLIENT_IDLE_TIMEOUT); // Invalid: accessed 15 minutes ago const expiredSession = { lastAccessedAt: now - 15 * 60 * 1000, createdAt: now - 30 * 60 * 1000, }; const expiredIdleTime = now - expiredSession.lastAccessedAt; expect(expiredIdleTime).toBeGreaterThan(CLIENT_IDLE_TIMEOUT); }); test("should validate max lifetime correctly", () => { const CLIENT_MAX_LIFETIME = 60 * 60 * 1000; // 1 hour const now = Date.now(); // Valid: created 30 minutes ago const validSession = { lastAccessedAt: now, createdAt: now - 30 * 60 * 1000, }; const lifetime = now - validSession.createdAt; expect(lifetime).toBeLessThan(CLIENT_MAX_LIFETIME); // Invalid: created 90 minutes ago (even if recently accessed) const expiredSession = { lastAccessedAt: now, // Just accessed! createdAt: now - 90 * 60 * 1000, }; const expiredLifetime = now - expiredSession.createdAt; expect(expiredLifetime).toBeGreaterThan(CLIENT_MAX_LIFETIME); }); test("should require both idle and lifetime checks to pass", () => { const CLIENT_IDLE_TIMEOUT = 10 * 60 * 1000; const CLIENT_MAX_LIFETIME = 60 * 60 * 1000; const now = Date.now(); // Case 1: Valid idle but expired lifetime const case1 = { lastAccessedAt: now - 5 * 60 * 1000, // 5 min ago - VALID createdAt: now - 90 * 60 * 1000, // 90 min ago - EXPIRED }; const case1Valid = now - case1.lastAccessedAt < CLIENT_IDLE_TIMEOUT && now - case1.createdAt < CLIENT_MAX_LIFETIME; expect(case1Valid).toBe(false); // Should be invalid // Case 2: Expired idle but valid lifetime const case2 = { lastAccessedAt: now - 20 * 60 * 1000, // 20 min ago - EXPIRED createdAt: now - 30 * 60 * 1000, // 30 min ago - VALID }; const case2Valid = now - case2.lastAccessedAt < CLIENT_IDLE_TIMEOUT && now - case2.createdAt < CLIENT_MAX_LIFETIME; expect(case2Valid).toBe(false); // Should be invalid // Case 3: Both valid const case3 = { lastAccessedAt: now - 5 * 60 * 1000, // 5 min ago - VALID createdAt: now - 30 * 60 * 1000, // 30 min ago - VALID }; const case3Valid = now - case3.lastAccessedAt < CLIENT_IDLE_TIMEOUT && now - case3.createdAt < CLIENT_MAX_LIFETIME; expect(case3Valid).toBe(true); // Should be valid }); }); describe("Sliding Window Behavior", () => { test("should extend session lifetime with access", () => { const CLIENT_IDLE_TIMEOUT = 10 * 60 * 1000; const now = Date.now(); // Session accessed at T=0 let lastAccessed = now; // Access at T+5min (within timeout) lastAccessed = now + 5 * 60 * 1000; expect(lastAccessed - now).toBeLessThan(CLIENT_IDLE_TIMEOUT); // Access again at T+12min (would have expired without previous access) const newNow = now + 12 * 60 * 1000; expect(newNow - lastAccessed).toBeLessThan(CLIENT_IDLE_TIMEOUT); // Valid due to sliding window }); test("should not extend beyond max lifetime", () => { const CLIENT_IDLE_TIMEOUT = 10 * 60 * 1000; const CLIENT_MAX_LIFETIME = 60 * 60 * 1000; const createdAt = Date.now(); // Even with continuous access every 5 minutes... const lastAccessedAt = createdAt + 90 * 60 * 1000; // 90 minutes later const now = createdAt + 90 * 60 * 1000; const idleValid = now - lastAccessedAt < CLIENT_IDLE_TIMEOUT; // true const lifetimeValid = now - createdAt < CLIENT_MAX_LIFETIME; // false expect(idleValid).toBe(true); expect(lifetimeValid).toBe(false); expect(idleValid && lifetimeValid).toBe(false); // Session invalid despite recent access }); }); });

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/robertn702/mcp-sunsama'

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