Spaces:
Sleeping
Sleeping
File size: 5,439 Bytes
e166039 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | 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();
|