Spaces:
Sleeping
Sleeping
| import { db, usersTable, auditLogsTable } from "@workspace/db"; | |
| import { eq, desc } from "drizzle-orm"; | |
| import jwt from "jsonwebtoken"; | |
| import app from "../src/app.js"; | |
| import http from "http"; | |
| async function runTest() { | |
| console.log("π Starting E2E User Creation & Audit Log Verification Test...\n"); | |
| let server: http.Server | null = null; | |
| try { | |
| // 1. Find or create an admin user in the database | |
| let [admin] = await db.select().from(usersTable).where(eq(usersTable.role, "admin")).limit(1); | |
| if (!admin) { | |
| console.log("βΉοΈ No admin user found. Creating a temporary admin user..."); | |
| [admin] = await db.insert(usersTable).values({ | |
| email: "admin_" + Date.now() + "@example.com", | |
| name: "System Admin", | |
| role: "admin", | |
| team: "Executive", | |
| passwordHash: "dummy_hash", | |
| }).returning(); | |
| } | |
| console.log(`β Using Admin User: ${admin.name} (ID: ${admin.id}, Email: ${admin.email})`); | |
| // 2. Generate JWT Token for Admin | |
| const token = jwt.sign( | |
| { id: admin.id, email: admin.email, role: admin.role, team: admin.team }, | |
| process.env.JWT_SECRET || "", | |
| { expiresIn: "1h" } | |
| ); | |
| // 3. Start Express Server on port 5005 | |
| server = app.listen(5005); | |
| console.log("β Express Test Server listening on port 5005"); | |
| // 4. Prepare Test User Payload | |
| const newUserData = { | |
| name: "E2E Test User", | |
| email: "e2e_test_" + Date.now() + "@example.com", | |
| password: "SecurePassword123!", | |
| role: "member", | |
| team: "Engineering", | |
| avatarColor: "#336699", | |
| // Attempting mass assignment injection | |
| id: 99999, | |
| passwordHash: "injected_hash", | |
| createdAt: "2020-01-01T00:00:00.000Z", | |
| }; | |
| console.log("\nπ¦ Sending POST /api/users request with payload (including mass-assignment attempts)..."); | |
| // 5. Send Request | |
| const res = await fetch("http://localhost:5005/api/users", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| "Authorization": `Bearer ${token}`, | |
| }, | |
| body: JSON.stringify(newUserData), | |
| }); | |
| const responseBody = await res.json(); | |
| console.log(`HTTP Response Status: ${res.status}`); | |
| if (res.status !== 201) { | |
| throw new Error(`Expected status 201, got ${res.status}. Response: ${JSON.stringify(responseBody)}`); | |
| } | |
| console.log("β API Response received successfully. Verifying response payload..."); | |
| // Verify response does not expose passwordHash or injected fields | |
| if (responseBody.passwordHash || responseBody.password || responseBody.id === 99999) { | |
| throw new Error("β API Response exposed sensitive fields or allowed mass assignment!"); | |
| } | |
| console.log("β API Response is perfectly sanitized (password/passwordHash excluded, mass-assignment ignored)."); | |
| const createdUserId = responseBody.id; | |
| // 6. Query Supabase directly to verify record in users table | |
| console.log(`\nπ Querying Supabase for created user (ID: ${createdUserId})...`); | |
| const [dbUser] = await db.select().from(usersTable).where(eq(usersTable.id, createdUserId)); | |
| if (!dbUser) { | |
| throw new Error("β User record not found in Supabase database!"); | |
| } | |
| console.log("β User record found in database:"); | |
| console.log({ | |
| id: dbUser.id, | |
| name: dbUser.name, | |
| email: dbUser.email, | |
| role: dbUser.role, | |
| team: dbUser.team, | |
| createdAt: dbUser.createdAt, | |
| passwordHashIsSet: !!dbUser.passwordHash && dbUser.passwordHash !== "injected_hash" && dbUser.passwordHash !== "SecurePassword123!", | |
| }); | |
| if (!dbUser.passwordHash || dbUser.passwordHash === "SecurePassword123!" || dbUser.passwordHash === "injected_hash") { | |
| throw new Error("β Password was not hashed correctly or mass assignment succeeded!"); | |
| } | |
| console.log("β Password is securely hashed in the database using bcrypt."); | |
| // 7. Query Supabase directly for the audit log entry | |
| // Wait briefly for async audit log insertion | |
| await new Promise(resolve => setTimeout(resolve, 500)); | |
| console.log(`\nπ Querying Supabase for audit log entry...`); | |
| const [auditLog] = await db.select().from(auditLogsTable) | |
| .where(eq(auditLogsTable.event, "role_change")) | |
| .orderBy(desc(auditLogsTable.createdAt)) | |
| .limit(1); | |
| if (!auditLog) { | |
| throw new Error("β Audit log entry for 'role_change' not found!"); | |
| } | |
| console.log("β Audit log entry found in database:"); | |
| console.log({ | |
| id: auditLog.id, | |
| event: auditLog.event, | |
| performedById: auditLog.performedById, | |
| entityId: auditLog.entityId, | |
| entityType: auditLog.entityType, | |
| details: auditLog.details, | |
| createdAt: auditLog.createdAt, | |
| }); | |
| if (auditLog.performedById !== admin.id.toString()) { | |
| throw new Error(`β Audit log performedById (${auditLog.performedById}) does not match admin ID (${admin.id})!`); | |
| } | |
| const details: any = auditLog.details; | |
| if (details.password || details.passwordHash) { | |
| throw new Error("β Audit log details leaked sensitive password information!"); | |
| } | |
| console.log("\nπ ALL E2E VERIFICATION CHECKS PASSED SUCCESSFULLY!"); | |
| } catch (err) { | |
| console.error("\nβ Test Failed:", err); | |
| } finally { | |
| if (server) { | |
| server.close(); | |
| console.log("\nβ Express server closed."); | |
| } | |
| } | |
| } | |
| runTest(); | |