Skip to content

fix: tighten cors logic for socket io#2688

Merged
Salazareo merged 3 commits intomainfrom
DS/main
Mar 19, 2026
Merged

fix: tighten cors logic for socket io#2688
Salazareo merged 3 commits intomainfrom
DS/main

Conversation

@Salazareo
Copy link
Copy Markdown
Member

@Salazareo Salazareo commented Mar 18, 2026

changing some headers to allow sticky session cookies better

@Salazareo Salazareo changed the title fix: tighten up cors for socket io fix: tighten cors logic for socket io Mar 19, 2026
@Salazareo Salazareo requested a review from Copilot March 19, 2026 19:06
@Salazareo Salazareo merged commit a94620d into main Mar 19, 2026
6 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 withCredentials on 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-js version.

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.

Comment on lines +80 to 86
allowRequest: (req, callback) => {
if ( isApiRequest(req) ) {
callback(null, true);
return;
}
callback(null, isUiOriginAllowed(req));
},
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.

if ( ! socket_id ) {
this.log.warn('missing socket id', { metadata });
console.warn('missing socket id', { metadata });
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
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 });
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +1587 to +1589
throw new Error('jwt_auth requires authService');
}

Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +705 to 715
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');
}
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +78
cors: (req, callback) => {
if ( isApiRequest(req) ) {
callback(null, {
origin: true,
credentials: true,
});
return;
}

callback(null, {
origin: isUiOriginAllowed(req),
credentials: true,
});
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Salazareo added a commit that referenced this pull request Mar 19, 2026
Salazareo added a commit that referenced this pull request Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants