forked from Ayushh-Sharmaa/NexaSphere
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfix2.js
More file actions
95 lines (80 loc) · 3.69 KB
/
Copy pathfix2.js
File metadata and controls
95 lines (80 loc) · 3.69 KB
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
const fs = require('fs');
let js = fs.readFileSync('server/index.js', 'utf8');
const regex = /<<<<<<< HEAD\r?\n(?:.|\r|\n)*?>>>>>>> 5406478[^\n]*\r?\n/g;
const replacement = `// Hard cap on tracked entries. When the limit is reached, the
// oldest inserted entry is evicted before adding a new one, preventing the
// Map from growing without bound when an attacker rotates through many
// distinct usernames from the same or different IP addresses.
const MAX_PASSKEY_TRACKED_KEYS = 10_000;
const failedPasskeyAttemptsByIp = new Map();
const failedPasskeyAttemptsByUsername = new Map();
// Periodic sweep every 30 minutes: remove entries whose lockout period has
// expired and whose attempt count has already been reset to 0, so they do
// not accumulate for keys that are never visited again.
setInterval(() => {
const now = Date.now();
for (const [key, entry] of failedPasskeyAttemptsByIp) {
if (entry.count === 0 && now > entry.lockoutUntil) {
failedPasskeyAttemptsByIp.delete(key);
}
}
for (const [key, entry] of failedPasskeyAttemptsByUsername) {
if (now > entry.lockoutUntil) {
failedPasskeyAttemptsByUsername.delete(key);
}
}
}, 30 * 60 * 1000).unref();
function checkPasskeyLockout(username, ip) {
const ipKey = String(ip || 'unknown');
const userKey = String(username || '').toLowerCase();
const ipEntry = failedPasskeyAttemptsByIp.get(ipKey);
const userEntry = failedPasskeyAttemptsByUsername.get(userKey);
const now = Date.now();
if (ipEntry && ipEntry.lockoutUntil !== 0 && now <= ipEntry.lockoutUntil) {
return true;
}
if (userEntry && userEntry.lockoutUntil !== 0 && now <= userEntry.lockoutUntil) {
return true;
}
// Cleanup expired entries proactively
if (ipEntry && ipEntry.lockoutUntil !== 0 && now > ipEntry.lockoutUntil) {
failedPasskeyAttemptsByIp.delete(ipKey);
}
if (userEntry && userEntry.lockoutUntil !== 0 && now > userEntry.lockoutUntil) {
failedPasskeyAttemptsByUsername.delete(userKey);
}
return false;
}
function recordFailedPasskeyAttempt(username, ip) {
const ipKey = String(ip || 'unknown');
const userKey = String(username || '').toLowerCase();
// IP tracking
if (!failedPasskeyAttemptsByIp.has(ipKey) && failedPasskeyAttemptsByIp.size >= MAX_PASSKEY_TRACKED_KEYS) {
failedPasskeyAttemptsByIp.delete(failedPasskeyAttemptsByIp.keys().next().value);
}
const ipEntry = failedPasskeyAttemptsByIp.get(ipKey) || { count: 0, lockoutUntil: 0 };
ipEntry.count += 1;
if (ipEntry.count >= 5) {
ipEntry.lockoutUntil = Date.now() + 15 * 60 * 1000; // 15 mins
ipEntry.count = 0; // Reset count so they need 5 more AFTER lockout to be locked again
}
failedPasskeyAttemptsByIp.set(ipKey, ipEntry);
// Username tracking (Exponential backoff)
if (!failedPasskeyAttemptsByUsername.has(userKey) && failedPasskeyAttemptsByUsername.size >= MAX_PASSKEY_TRACKED_KEYS) {
failedPasskeyAttemptsByUsername.delete(failedPasskeyAttemptsByUsername.keys().next().value);
}
const userEntry = failedPasskeyAttemptsByUsername.get(userKey) || { count: 0, lockoutUntil: 0 };
userEntry.count += 1;
if (userEntry.count >= 5) {
// 5 attempts = 1 min, 6 = 2 mins, 7 = 4 mins, 8 = 8 mins, 9+ = 15 mins
const factor = Math.pow(2, Math.max(0, userEntry.count - 5));
const delayMinutes = Math.min(15, factor);
userEntry.lockoutUntil = Date.now() + delayMinutes * 60 * 1000;
}
failedPasskeyAttemptsByUsername.set(userKey, userEntry);
return { ipEntry, userEntry };
}
`;
// It seems the conflict markers are split! Let's just do a string replace
let targetJs = js.replace(/<<<<<<< HEAD[\s\S]*?function clearPasskeyAttempts/m, replacement + '\nfunction clearPasskeyAttempts');
fs.writeFileSync('server/index.js', targetJs);