Conversation
There was a problem hiding this comment.
Pull request overview
Updates client requests and backend CORS/socket.io handling to better support credentialed cross-origin requests (e.g., sticky-session cookies) and “tighten” socket.io origin checks.
Changes:
- Enable
withCredentialson several XHR flows (upload/download + generic XHR helper). - Adjust backend HTTP CORS headers to optionally allow credentials for API/DAV requests.
- Rework socket.io CORS/origin gating and simplify socket-targeted emits; bump
puter-jsversion.
Reviewed changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/puter-js/src/modules/FileSystem/operations/upload.js | Sends upload XHRs with credentials (cookies) enabled. |
| src/puter-js/src/lib/utils.js | Enables credentials on library XHR helper; minor formatting changes. |
| src/puter-js/package.json | Bumps @heyputer/puter.js version to 2.2.14. |
| src/puter-js/package-lock.json | Updates lockfile version for puter-js package bump. |
| src/gui/src/helpers/download.js | Sends download XHRs with credentials enabled. |
| src/backend/src/services/WSPushService.js | Refactors event handler formatting; changes logging + socket send behavior. |
| src/backend/src/routers/_default.js | Updates jwt_auth call to pass authService. |
| src/backend/src/modules/web/WebServerService.js | Changes CORS header logic, including enabling Allow-Credentials for API/DAV. |
| src/backend/src/modules/web/SocketioService.js | Adds host/origin normalization and new socket.io CORS/allowRequest logic; changes socket-id emit behavior. |
| src/backend/src/helpers.js | Changes jwt_auth signature to require injected authService. |
| package-lock.json | Updates root lockfile to reflect puter-js version bump. |
Files not reviewed (1)
- src/puter-js/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)
src/backend/src/helpers.js:1597
- The user-actor type check is broken due to operator precedence:
!actor.type?.constructor?.name === 'UserActorType'negates the string before comparing, so the condition is effectively always false and unsupported actor types will not be rejected. Invert the comparison explicitly (e.g., compare the constructor name and then negate the result).
if ( !actor.type?.constructor?.name === 'UserActorType' ) {
throw ({
message: APIError.create('token_unsupported')
.serialize(),
});
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| allowRequest: (req, callback) => { | ||
| if ( isApiRequest(req) ) { | ||
| callback(null, true); | ||
| return; | ||
| } | ||
| callback(null, isUiOriginAllowed(req)); | ||
| }, |
There was a problem hiding this comment.
allowRequest rejects any connection without an Origin header (isUiOriginAllowed returns false when origin is missing). This can break non-browser / internal socket.io-client connections (which often omit Origin, e.g. the backend broadcast link). If those clients must keep working, allow missing-Origin requests for trusted cases (e.g. internal path like /wssinternal, mTLS, or when Host matches an expected internal host) instead of blanket-rejecting them.
|
|
||
| if ( ! socket_id ) { | ||
| this.log.warn('missing socket id', { metadata }); | ||
| console.warn('missing socket id', { metadata }); |
There was a problem hiding this comment.
Switching from this.log.warn/error to console.warn/error bypasses the service logging infrastructure (levels, formatting, routing) that BaseService sets up via this.log. Use this.log.warn / this.log.error here to keep logs consistent and observable in production.
| await kvStore.set({ key: key, value: ts }); | ||
| } catch ( error ) { | ||
| this.log.error('Failed to update user timestamp in kvstore', { user_id, error: error.message }); | ||
| console.error('Failed to update user timestamp in kvstore', { user_id, error: error.message }); |
There was a problem hiding this comment.
Switching this error path to console.error bypasses the service logger (this.log) and can make these failures harder to trace/aggregate. Prefer this.log.error(...) to preserve structured logging and log-level controls.
| throw new Error('jwt_auth requires authService'); | ||
| } | ||
|
|
There was a problem hiding this comment.
jwt_auth now requires an authService argument, but there are still callers that invoke it with only req/socket (e.g. the socket.io auth middleware in WebServerService). This will throw at runtime and break authentication. Either keep backwards compatibility (default to Context.get('services').get('auth') when not provided) or update all call sites to pass the auth service explicitly.
| if ( isCrossOriginAuthRoute || isApiOrDavRequest ) { | ||
| res.setHeader('Access-Control-Allow-Origin', origin ?? '*'); | ||
| if ( origin ) { | ||
| res.vary('Origin'); | ||
| } | ||
| } | ||
| // Website(s) to allow to connect | ||
| if ( | ||
| config.experimental_no_subdomain || | ||
| req.subdomains[req.subdomains.length - 1] === 'api' || | ||
| req.subdomains[req.subdomains.length - 1] === 'dav' | ||
| ) { | ||
| res.setHeader('Access-Control-Allow-Origin', origin ?? '*'); | ||
|
|
||
| // Allow browser credentials on API/DAV cross-origin requests. | ||
| if ( isApiOrDavRequest && origin ) { | ||
| res.setHeader('Access-Control-Allow-Credentials', 'true'); | ||
| } |
There was a problem hiding this comment.
This CORS middleware reflects any provided Origin and (for API/DAV) also enables Access-Control-Allow-Credentials: true. That combination effectively allows credentialed cross-origin reads from arbitrary origins, which is a serious CORS misconfiguration if any auth/session cookies are in play (including “sticky session” cookies). Restrict Access-Control-Allow-Origin to an explicit allowlist (and only set Allow-Credentials for allowed origins), rather than echoing unvalidated origins.
| cors: (req, callback) => { | ||
| if ( isApiRequest(req) ) { | ||
| callback(null, { | ||
| origin: true, | ||
| credentials: true, | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| callback(null, { | ||
| origin: isUiOriginAllowed(req), | ||
| credentials: true, | ||
| }); |
There was a problem hiding this comment.
socketioOptions.cors is being set to a function (req, callback) => { ... }, but Socket.IO v4 expects cors to be an options object (where origin can be a function). Passing a function here will likely be treated as an invalid options shape and can break CORS handling at runtime. Consider using cors: { origin: (origin, cb) => { ... }, credentials: true } and keep request-level gating in allowRequest.
This reverts commit a94620d.
changing some headers to allow sticky session cookies better