Skip to content

Commit 6083600

Browse files
authored
Modify Origin header validation in validateRequestHeaders (streamableHttp.ts and sse.ts) to allow requests without an Origin, as they are not relevant to server DNS rebinding protection. (#1205)
1 parent a6ee2cb commit 6083600

File tree

4 files changed

+46
-2
lines changed

4 files changed

+46
-2
lines changed

src/server/sse.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,27 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
547547
expect(mockHandleRes.end).toHaveBeenCalledWith('Accepted');
548548
});
549549

550+
it('should accept requests without origin headers', async () => {
551+
const mockRes = createMockResponse();
552+
const transport = new SSEServerTransport('/messages', mockRes, {
553+
allowedOrigins: ['http://localhost:3000', 'https://example.com'],
554+
enableDnsRebindingProtection: true
555+
});
556+
await transport.start();
557+
558+
const mockReq = createMockRequest({
559+
headers: {
560+
'content-type': 'application/json'
561+
}
562+
});
563+
const mockHandleRes = createMockResponse();
564+
565+
await transport.handlePostMessage(mockReq, mockHandleRes, { jsonrpc: '2.0', method: 'test' });
566+
567+
expect(mockHandleRes.writeHead).toHaveBeenCalledWith(202);
568+
expect(mockHandleRes.end).toHaveBeenCalledWith('Accepted');
569+
});
570+
550571
it('should reject requests with disallowed origin headers', async () => {
551572
const mockRes = createMockResponse();
552573
const transport = new SSEServerTransport('/messages', mockRes, {

src/server/sse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class SSEServerTransport implements Transport {
7979
// Validate Origin header if allowedOrigins is configured
8080
if (this._options.allowedOrigins && this._options.allowedOrigins.length > 0) {
8181
const originHeader = req.headers.origin;
82-
if (!originHeader || !this._options.allowedOrigins.includes(originHeader)) {
82+
if (originHeader && !this._options.allowedOrigins.includes(originHeader)) {
8383
return `Invalid Origin header: ${originHeader}`;
8484
}
8585
}

src/server/streamableHttp.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2678,6 +2678,29 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
26782678
const body = await response.json();
26792679
expect(body.error.message).toBe('Invalid Origin header: http://evil.com');
26802680
});
2681+
2682+
it('should accept requests without origin headers', async () => {
2683+
const result = await createTestServerWithDnsProtection({
2684+
sessionIdGenerator: undefined,
2685+
allowedOrigins: ['http://localhost:3000', 'https://example.com'],
2686+
enableDnsRebindingProtection: true
2687+
});
2688+
server = result.server;
2689+
transport = result.transport;
2690+
baseUrl = result.baseUrl;
2691+
2692+
const response = await fetch(baseUrl, {
2693+
method: 'POST',
2694+
headers: {
2695+
'Content-Type': 'application/json',
2696+
Accept: 'application/json, text/event-stream'
2697+
},
2698+
body: JSON.stringify(TEST_MESSAGES.initialize)
2699+
});
2700+
2701+
// Should pass even with no Origin headers because requests that do not come from browsers may not have Origin and DNS rebinding attacks can only be performed via browsers
2702+
expect(response.status).toBe(200);
2703+
});
26812704
});
26822705

26832706
describe('enableDnsRebindingProtection option', () => {

src/server/streamableHttp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ export class StreamableHTTPServerTransport implements Transport {
228228
// Validate Origin header if allowedOrigins is configured
229229
if (this._allowedOrigins && this._allowedOrigins.length > 0) {
230230
const originHeader = req.headers.origin;
231-
if (!originHeader || !this._allowedOrigins.includes(originHeader)) {
231+
if (originHeader && !this._allowedOrigins.includes(originHeader)) {
232232
return `Invalid Origin header: ${originHeader}`;
233233
}
234234
}

0 commit comments

Comments
 (0)