@@ -282,17 +282,39 @@ if (useHttp) {
282282
283283 // Validate Origin header as required by MCP spec (for non-health endpoints)
284284 const origin = req . headers . origin
285+
286+ // Check if origin is from localhost (any port) - safe for local development
287+ const isLocalhostOrigin = ( originUrl : string ) : boolean => {
288+ try {
289+ const url = new URL ( originUrl )
290+ return url . hostname === 'localhost' || url . hostname === '127.0.0.1'
291+ } catch {
292+ return false
293+ }
294+ }
295+
285296 const allowedOrigins = [
286- 'http://localhost:3000' ,
287- 'http://127.0.0.1:3000' ,
288297 'https://mcp.socket.dev' ,
289298 'https://mcp.socket-staging.dev'
290299 ]
291300
292- const isValidOrigin = origin && allowedOrigins . includes ( origin )
301+ // Check if request is from localhost (for same-origin requests that don't send Origin header)
302+ // Use strict matching to prevent spoofing via subdomains like "malicious-localhost.evil.com"
303+ const host = req . headers . host || ''
304+ const isLocalhostHost = host === `localhost:${ port } ` ||
305+ host === `127.0.0.1:${ port } ` ||
306+ host === 'localhost' ||
307+ host === '127.0.0.1'
308+
309+ // Allow requests:
310+ // 1. With Origin header from localhost (any port) or production domains
311+ // 2. Without Origin header if they're from localhost (same-origin requests)
312+ const isValidOrigin = origin
313+ ? ( isLocalhostOrigin ( origin ) || allowedOrigins . includes ( origin ) )
314+ : isLocalhostHost
293315
294316 if ( ! isValidOrigin ) {
295- logger . warn ( `Rejected request from invalid origin: ${ origin } ` )
317+ logger . warn ( `Rejected request from invalid origin: ${ origin || 'missing' } (host: ${ host } ) ` )
296318 res . writeHead ( 403 , { 'Content-Type' : 'application/json' } )
297319 res . end ( JSON . stringify ( {
298320 jsonrpc : '2.0' ,
@@ -302,11 +324,13 @@ if (useHttp) {
302324 return
303325 }
304326
305- // Set CORS headers for valid origins
306- // Note: origin is guaranteed to be truthy here because isValidOrigin === true
307- res . setHeader ( 'Access-Control-Allow-Origin' , origin ! )
308- res . setHeader ( 'Access-Control-Allow-Methods' , 'POST, OPTIONS' )
309- res . setHeader ( 'Access-Control-Allow-Headers' , 'Content-Type, Accept' )
327+ // Set CORS headers for valid origins (only needed for cross-origin requests)
328+ // Same-origin requests don't send Origin header and don't need CORS headers
329+ if ( origin ) {
330+ res . setHeader ( 'Access-Control-Allow-Origin' , origin )
331+ res . setHeader ( 'Access-Control-Allow-Methods' , 'POST, OPTIONS' )
332+ res . setHeader ( 'Access-Control-Allow-Headers' , 'Content-Type, Accept' )
333+ }
310334
311335 if ( req . method === 'OPTIONS' ) {
312336 res . writeHead ( 200 )
@@ -325,6 +349,9 @@ if (useHttp) {
325349
326350 // If this is an initialize, reset the singleton transport so clients can (re)initialize cleanly
327351 if ( jsonData && jsonData . method === 'initialize' ) {
352+ const clientInfo = jsonData . params ?. clientInfo
353+ logger . info ( `Client connected: ${ clientInfo ?. name || 'unknown' } v${ clientInfo ?. version || 'unknown' } from ${ origin || host } ` )
354+
328355 if ( httpTransport ) {
329356 try { httpTransport . close ( ) } catch { }
330357 }
0 commit comments