Skip to content

Commit 53dc05c

Browse files
committed
implemented security practice to hide password hashes from api responses using excludePassword helper function
1 parent 9faf265 commit 53dc05c

4 files changed

Lines changed: 407 additions & 5 deletions

File tree

server/controllers/users.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@ const { PrismaClient } = require("@prisma/client");
22
const prisma = new PrismaClient();
33
const bcrypt = require("bcryptjs");
44

5+
// Helper function to exclude password from user object
6+
function excludePassword(user) {
7+
if (!user) return user;
8+
const { password, ...userWithoutPassword } = user;
9+
return userWithoutPassword;
10+
}
11+
512
async function getAllUsers(request, response) {
613
try {
714
const users = await prisma.user.findMany({});
8-
return response.json(users);
15+
// Exclude password from all users
16+
const usersWithoutPasswords = users.map(user => excludePassword(user));
17+
return response.json(usersWithoutPasswords);
918
} catch (error) {
1019
return response.status(500).json({ error: "Error fetching users" });
1120
}
@@ -23,7 +32,8 @@ async function createUser(request, response) {
2332
role,
2433
},
2534
});
26-
return response.status(201).json(user);
35+
// Exclude password from response
36+
return response.status(201).json(excludePassword(user));
2737
} catch (error) {
2838
console.error("Error creating user:", error);
2939
return response.status(500).json({ error: "Error creating user" });
@@ -56,7 +66,8 @@ async function updateUser(request, response) {
5666
},
5767
});
5868

59-
return response.status(200).json(updatedUser);
69+
// Exclude password from response
70+
return response.status(200).json(excludePassword(updatedUser));
6071
} catch (error) {
6172
return response.status(500).json({ error: "Error updating user" });
6273
}
@@ -87,7 +98,8 @@ async function getUser(request, response) {
8798
if (!user) {
8899
return response.status(404).json({ error: "User not found" });
89100
}
90-
return response.status(200).json(user);
101+
// Exclude password from response
102+
return response.status(200).json(excludePassword(user));
91103
}
92104

93105
async function getUserByEmail(request, response) {
@@ -100,7 +112,8 @@ async function getUserByEmail(request, response) {
100112
if (!user) {
101113
return response.status(404).json({ error: "User not found" });
102114
}
103-
return response.status(200).json(user);
115+
// Exclude password from response
116+
return response.status(200).json(excludePassword(user));
104117
}
105118

106119
module.exports = {
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Quick manual security check for password exposure
2+
const axios = require('axios');
3+
4+
const API_BASE_URL = 'http://localhost:3001';
5+
6+
async function quickSecurityCheck() {
7+
console.log("🔍 Quick Security Check - Testing Password Exposure Prevention\n");
8+
9+
try {
10+
// 1. Create a test user
11+
console.log("1. Creating test user...");
12+
const createResponse = await axios.post(`${API_BASE_URL}/api/users`, {
13+
email: 'security-test@example.com',
14+
password: 'secretpassword123',
15+
role: 'user'
16+
});
17+
18+
console.log(" ✅ User created successfully");
19+
console.log(" �� Response data:", JSON.stringify(createResponse.data, null, 2));
20+
21+
// Check if password is in response
22+
if (createResponse.data.password) {
23+
console.log(" ❌ SECURITY ISSUE: Password found in create response!");
24+
return;
25+
} else {
26+
console.log(" ✅ Password correctly excluded from create response");
27+
}
28+
29+
const userId = createResponse.data.id;
30+
31+
// 2. Get the user by ID
32+
console.log("\n2. Getting user by ID...");
33+
const getResponse = await axios.get(`${API_BASE_URL}/api/users/${userId}`);
34+
35+
console.log(" 📋 Response data:", JSON.stringify(getResponse.data, null, 2));
36+
37+
if (getResponse.data.password) {
38+
console.log(" ❌ SECURITY ISSUE: Password found in get response!");
39+
return;
40+
} else {
41+
console.log(" ✅ Password correctly excluded from get response");
42+
}
43+
44+
// 3. Get user by email
45+
console.log("\n3. Getting user by email...");
46+
const emailResponse = await axios.get(`${API_BASE_URL}/api/users/email/security-test@example.com`);
47+
48+
console.log(" 📋 Response data:", JSON.stringify(emailResponse.data, null, 2));
49+
50+
if (emailResponse.data.password) {
51+
console.log(" ❌ SECURITY ISSUE: Password found in email lookup response!");
52+
return;
53+
} else {
54+
console.log(" ✅ Password correctly excluded from email lookup response");
55+
}
56+
57+
// 4. Get all users
58+
console.log("\n4. Getting all users...");
59+
const allUsersResponse = await axios.get(`${API_BASE_URL}/api/users`);
60+
61+
console.log(` 📋 Retrieved ${allUsersResponse.data.length} users`);
62+
63+
// Check if any user has password field
64+
const hasPasswordInAnyUser = allUsersResponse.data.some(user => user.password);
65+
if (hasPasswordInAnyUser) {
66+
console.log(" ❌ SECURITY ISSUE: Password found in get all users response!");
67+
return;
68+
} else {
69+
console.log(" ✅ Password correctly excluded from all users response");
70+
}
71+
72+
// 5. Update user
73+
console.log("\n5. Updating user...");
74+
const updateResponse = await axios.put(`${API_BASE_URL}/api/users/${userId}`, {
75+
email: 'updated-security-test@example.com',
76+
password: 'newsecretpassword456',
77+
role: 'admin'
78+
});
79+
80+
console.log(" �� Response data:", JSON.stringify(updateResponse.data, null, 2));
81+
82+
if (updateResponse.data.password) {
83+
console.log(" ❌ SECURITY ISSUE: Password found in update response!");
84+
return;
85+
} else {
86+
console.log(" ✅ Password correctly excluded from update response");
87+
}
88+
89+
// Clean up
90+
console.log("\n6. Cleaning up test user...");
91+
await axios.delete(`${API_BASE_URL}/api/users/${userId}`);
92+
console.log(" ✅ Test user deleted");
93+
94+
console.log("\n🎉 ALL SECURITY CHECKS PASSED!");
95+
console.log("🔒 Password fields are correctly excluded from all API responses!");
96+
97+
} catch (error) {
98+
console.error("❌ Test failed:", error.message);
99+
if (error.response) {
100+
console.error(" Response status:", error.response.status);
101+
console.error(" Response data:", error.response.data);
102+
}
103+
}
104+
}
105+
106+
// Run the check
107+
quickSecurityCheck();

server/tests/run-security-tests.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Security Test Runner
2+
const { runUserSecurityTests } = require('./user-security.test');
3+
4+
async function runAllSecurityTests() {
5+
console.log("🚀 Starting Security Test Suite...\n");
6+
7+
try {
8+
await runUserSecurityTests();
9+
console.log("\n🎉 All security tests completed successfully!");
10+
} catch (error) {
11+
console.error("\n❌ Security tests failed:", error.message);
12+
process.exit(1);
13+
}
14+
}
15+
16+
// Run tests if this file is executed directly
17+
if (require.main === module) {
18+
runAllSecurityTests();
19+
}
20+
21+
module.exports = { runAllSecurityTests };

0 commit comments

Comments
 (0)