fix: security vulnerabilities, export checkExistenceBatch, and handle undefined color/bgTemp#108
fix: security vulnerabilities, export checkExistenceBatch, and handle undefined color/bgTemp#108voioo wants to merge 4 commits intoLifeforge-app:mainfrom
Conversation
…es (#1) * fix(security): resolve critical vulnerabilities and architecture issues - fix(path-traversal): validate file paths in module serving middleware - fix(ssrf): add blocked hosts list to CORS proxy - refactor(2fa): replace global state with per-user state management - refactor(oauth): replace global codeVerifier with per-session state - fix(auth): add proper error logging to silent catch blocks Security improvements: - Path traversal protection with sanitized paths and boundary checks - SSRF protection blocking internal IPs and metadata endpoints - Concurrent request safety for 2FA and OAuth flows - Proper error handling and logging throughout * perf: optimize performance and improve type safety - perf(response): replace sync file operations with async cleanup - perf(validation): add batch existence check to fix N+1 query pattern - refactor(response): improve type safety and error handling - refactor(validation): add proper TypeScript interfaces Performance improvements: - Async file cleanup doesn't block error responses - Batch database queries for record existence checks - Better error handling and logging throughout
- Add missing export for checkExistenceBatch function - Fixes Docker server build failure
|
@voioo is attempting to deploy a commit to the Codeblog Team on Vercel. A member of the Team first needs to authorize it. |
|
Hi, thank you so much for your contribution! @melvinchia3636 please review this PR and revert. |
melvinchia3636
left a comment
There was a problem hiding this comment.
There are a lot of great minor improvements on the codebase, especially in the error validation part. Your contribution is highly valued and appreciated.
However, since LifeForge doesn't support multiple users per server instance and is not planning to do so anytime soon, the per-user implementations might not be necessary.
Also, it would be great if you could split this huge monolithic PR into multiple into several smaller ones that focus on a specific topic each, so that it could be easier for us to review the code.
| // Security: Use configured CORS origins instead of wildcard | ||
| const origin = req.headers.origin | ||
| if (origin && CORS_ALLOWED_ORIGINS.includes(origin)) { | ||
| res.setHeader('Access-Control-Allow-Origin', origin) | ||
| } | ||
| res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin') |
There was a problem hiding this comment.
We already have the cors modules configured, so we shouldn't need to configure the cors header ourselves.
| } else if (value !== undefined && value !== null) { | ||
| // Handle other types - convert to string | ||
| isValid = await checkSingle(req.pb(module), collection, String(value)) |
There was a problem hiding this comment.
will there actually be a scenario where the value isn't a string?
| .callback(async () => challenge) | ||
| .callback(async ({ pb }) => { | ||
| const userId = pb.instance.authStore.record?.id | ||
|
|
||
| const challenge = generateChallenge() | ||
|
|
||
| // Store per-user challenge so subsequent calls (like generateAuthenticatorLink) | ||
| // can reuse the same challenge value for encryption/decryption. | ||
| if (userId) { | ||
| twoFAStates.set(userId, { | ||
| ...twoFAStates.get(userId), | ||
| challenge, | ||
| challengeExpiresAt: dayjs().add(5, 'minutes').toISOString() | ||
| }) | ||
| } | ||
|
|
||
| return challenge |
There was a problem hiding this comment.
LifeForge is a single-user-single-instance project, so is it necessary to implement per-user challenge?
| canDisable2FA = true | ||
| setTimeout( | ||
| () => { | ||
| canDisable2FA = false | ||
| }, | ||
| 1000 * 60 * 5 | ||
| ) | ||
| // Store per-user state instead of global state | ||
| const userId = pb.instance.authStore.record?.id | ||
| if (userId) { | ||
| twoFAStates.set(userId, { | ||
| canDisable: true, | ||
| expiresAt: dayjs().add(5, 'minutes').toISOString() | ||
| }) | ||
| } |
There was a problem hiding this comment.
LifeForge is a single-user-single-instance project, so is it necessary to implement per-user challenge?
| // Store per-user temp code instead of global | ||
| twoFAStates.set(userId, { | ||
| ...twoFAStates.get(userId), | ||
| tempCode, | ||
| tempCodeExpiresAt: dayjs().add(5, 'minutes').toISOString() | ||
| }) | ||
|
|
||
| // Prefer an existing per-user challenge created via `getChallenge()` | ||
| const existing = twoFAStates.get(userId) | ||
|
|
||
| const challenge = existing?.challenge || generateChallenge() | ||
|
|
||
| // If we generated a new challenge here, store it so the client can retrieve it | ||
| if (!existing) { | ||
| twoFAStates.set(userId, { | ||
| ...twoFAStates.get(userId), | ||
| challenge, | ||
| challengeExpiresAt: dayjs().add(5, 'minutes').toISOString() | ||
| }) | ||
| } | ||
|
|
There was a problem hiding this comment.
LifeForge is a single-user-single-instance project, so is it necessary to implement per-user challenge?
|
Perhaps the auth state management (challenges, twoFA states etc.) we could create a global state storage for modules to use, so that we don't need to reimplement the Map object in different modules that require one. |
|
@melvinchia3636 will it be good to have multiple users? IMO we already have user authentication system in the very first, so it's not tedious in future to implement multi users functionality. Perhaps, just leave the per-user challenge there. How do you think? |
Summary
Security fixes (path traversal, SSRF, global state):
Performance improvements:
Bug fixes:
checkExistenceBatchfrom database index (fixes server build)color/bgTempin UserPersonalizationProvider (fixes login crash)Files changed
server/src/core/functions/database/validation.ts- Added checkExistenceBatchserver/src/core/functions/database/index.ts- Export checkExistenceBatchserver/src/lib/corsAnywhere/index.ts- SSRF protectionserver/src/core/routes/index.ts- Path traversal protectionserver/src/lib/user/routes/twoFA.ts- Per-user stateserver/src/lib/user/routes/oauth.ts- Per-session stateclient/src/providers/features/UserPersonalizationProvider.tsx- Handle undefined values