From 01768b6d4d2cb5ab30b1d1f6820d796a76be878d Mon Sep 17 00:00:00 2001 From: Michael Bashurov Date: Wed, 4 Dec 2024 17:11:30 +0200 Subject: [PATCH] fix: [#1631] Set fetch credentials to same-origin for xhr when withCredentials is false To more accurately emulate xhr spec behaviour https://xhr.spec.whatwg.org/#the-withcredentials-attribute --- .../src/xml-http-request/XMLHttpRequest.ts | 2 +- .../xml-http-request/XMLHttpRequest.test.ts | 30 ++++++++++++++++ .../test/tests/XMLHttpRequest.test.js | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts index b49887308..20c075d4d 100644 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts @@ -216,7 +216,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget { method, headers, signal: this.#abortController.signal, - credentials: this.withCredentials ? 'include' : 'omit' + credentials: this.withCredentials ? 'include' : 'same-origin' }); this.#readyState = XMLHttpRequestReadyStateEnum.opened; diff --git a/packages/happy-dom/test/xml-http-request/XMLHttpRequest.test.ts b/packages/happy-dom/test/xml-http-request/XMLHttpRequest.test.ts index 4c64db067..40913fc85 100644 --- a/packages/happy-dom/test/xml-http-request/XMLHttpRequest.test.ts +++ b/packages/happy-dom/test/xml-http-request/XMLHttpRequest.test.ts @@ -523,6 +523,36 @@ describe('XMLHttpRequest', () => { }); }); + it('Sets credentials to same-origin if withCredentials is false to emulate xmlhttprequest behaviour', async () => { + await new Promise((resolve) => { + let requestArgs: { headers: { [name: string]: string } } | null = null; + + vi.spyOn(Fetch.prototype, 'send').mockImplementation(async function () { + requestArgs = { + headers: { + Authorization: + this.request.credentials === 'same-origin' + ? this.request.headers.get('Authorization') + : undefined + } + }; + return { headers: new Headers() }; + }); + + request.open('GET', REQUEST_URL, true); + expect(request.setRequestHeader('Authorization', 'Basic test')).toBe(true); + request.addEventListener('load', () => { + expect(requestArgs).toEqual({ + headers: { + Authorization: 'Basic test' + } + }); + resolve(null); + }); + request.send(); + }); + }); + it('Does not set forbidden headers.', () => { request.open('GET', REQUEST_URL, true); for (const header of FORBIDDEN_REQUEST_HEADERS) { diff --git a/packages/integration-test/test/tests/XMLHttpRequest.test.js b/packages/integration-test/test/tests/XMLHttpRequest.test.js index 1a895e581..fc03d820c 100644 --- a/packages/integration-test/test/tests/XMLHttpRequest.test.js +++ b/packages/integration-test/test/tests/XMLHttpRequest.test.js @@ -36,6 +36,42 @@ describe('XMLHttpRequest', () => { }); }); + it('Send Authorization header in case of same origin request', async () => { + await new Promise((resolve) => { + const window = new Window({ + url: 'http://localhost:3000/' + }); + const express = Express(); + + express.get('/get/json', (req, res) => { + if (req.get('Authorization') === 'Basic test') { + res.sendStatus(200); + } else { + res.sendStatus(401); + } + }); + + const server = express.listen(3000); + const request = new window.XMLHttpRequest(); + + request.open('GET', 'http://localhost:3000/get/json', true); + + request.setRequestHeader('Authorization', 'Basic test'); + + request.addEventListener('load', () => { + expect(request.status).toBe(200); + expect(request.statusText).toBe('OK'); + expect(request.responseURL).toBe('http://localhost:3000/get/json'); + + server.close(); + + resolve(null); + }); + + request.send(); + }); + }); + it('Can perform a real synchronous XMLHttpRequest request to Github.com', () => { const window = new Window({ url: 'https://raw.githubusercontent.com/'