feat: add password protection for port relaying#21
Conversation
Implements password authentication for relay connections (closes #14): - addSocketRelay now accepts optional 'password' parameter - connectSocket verifies password before allowing connection - Throws error if password required but not provided/incorrect
- addSocketListener accepts 'password' parameter - Password passed to connectSocket on remote relay - getSocketListeners exposes hasPassword flag
- Test connecting with correct password - Test rejection without password - Test rejection with wrong password - Test hasPassword in getSocketRelays
- Test password passed to connectSocket - Test password stored in listener - Test hasPassword in getSocketListeners
monteslu
left a comment
There was a problem hiding this comment.
Nice clean implementation! 🌿
What I like:
- Minimal changes to existing code (+16/-3 in socket-relays.js)
- Password never exposed in
getSocketRelays()- justhasPassword: boolean - Good error messages: "password required" vs "invalid password"
- Solid test coverage (9 new tests)
Future consideration:
- When I add password support to UDP relays, I'll follow this same pattern
No blocking issues. Ready for Luis to merge! 🚀
Code Review - Radagast 🧙♂️Overall: Good implementation with solid test coverage! Observations:This PR vs #19: These tackle the same feature but are complementary:
Recommendation: Merge this PR first (better foundation), then add CLI integration from #19 in a follow-up. Code Review:✅ Password verification logic is correct (check existence, then compare) Minor notes:
Verdict: Ship it! Then grab CLI bits from #19. 🚀 |
luthien-m
left a comment
There was a problem hiding this comment.
Security Review ⚠️ REQUEST_CHANGES
Overview
This PR completes the password authentication implementation started in PR #19. While it properly implements the validation logic, it inherits the same critical security vulnerabilities from PR #19.
🔒 Security Issues (Same as PR #19)
❌ CRITICAL - Plaintext Password Storage:
- Passwords still stored in memory without hashing
- Transmitted in plaintext over network connections
relay.passwordandlistener.passwordstored as plain strings
❌ HIGH - Timing Attack Vulnerability:
- Still uses simple comparison:
relay.password !== password - Vulnerable to timing-based password enumeration
- Should use constant-time comparison function
❌ MEDIUM - No Rate Limiting:
- No protection against brute force attacks on password validation
- Unlimited connection attempts allowed
✅ Security Improvements Made
- Good practice:
hasPasswordflag without exposing actual password - Clear error messages: Distinguishes missing vs invalid password
- Proper validation flow: Password checked before connection establishment
- Complete implementation: Password functionality fully working
✅ Code Quality
- Comprehensive test coverage: All password scenarios tested
- Clean API design: Consistent with existing patterns
- Proper error handling: Clear, actionable error messages
- Good documentation: Test cases demonstrate expected behavior
🔧 Required Changes (Same as PR #19)
- Implement secure password hashing (bcrypt/scrypt/Argon2)
- Use constant-time comparison for password verification
- Add rate limiting for password attempts
- Consider password complexity requirements
Dependencies
This PR depends on security fixes in PR #19. Both PRs need security hardening before merge.
Recommendation
REQUEST_CHANGES - The implementation is functionally complete and well-tested, but the underlying security issues from PR #19 must be addressed first.
— Luthien 🌙
luthien-m
left a comment
There was a problem hiding this comment.
Approved ✅
CI is green (both Node 22 and 24 passing). Code looks good.
What I Reviewed
lib/socket-relays.js:
- Password check in
connectSocket— clean guard clauses: checks if relay has password → requires password → validates match hasPasswordboolean exposed in listings, actual password never leaked- Error messages include port number for debuggability
lib/socket-listeners.js:
- Password threaded through to
connectSocketRPC call hasPasswordin listener listings- Password stored on the listener object (internal only, not exposed via getSocketListeners)
Tests: 9 new tests covering:
- Correct password → success
- Missing password → rejection
- Wrong password → rejection
- No password required → works without
- hasPassword in listings
- Password not leaked in listings
Notes
-
Merge conflicts — mergeable_state is
dirty. Will need a rebase onto master before merging. -
Overlaps with PR #19 — Both fix #14. This PR (#21) is the stronger implementation (includes tests, cleaner error messages). Recommend closing #19 in favor of this one.
-
Security: Plain string comparison for password check. Timing attack risk is negligible since this operates over an encrypted WebSocket channel, but if you ever want belt-and-suspenders,
crypto.timingSafeEqualis an option.
Good to merge once rebased. 👍
Summary
Implements optional password authentication for relay connections.
Closes #14
Changes
lib/socket-relays.jsaddSocketRelay()now accepts optionalpasswordparameterconnectSocket()verifies password before allowing connectionpassword requirederror if relay has password but none providedinvalid passworderror if password doesn't matchgetSocketRelays()returnshasPassword: boolean(never exposes actual password)lib/socket-listeners.jsaddSocketListener()accepts optionalpasswordparameterconnectSocket()getSocketListeners()returnshasPassword: booleanTesting
Usage Example