From c66145700cec4565a81356326216dcc16585b1bb Mon Sep 17 00:00:00 2001 From: Eric Mignot Date: Fri, 20 Apr 2018 08:11:06 -0700 Subject: [PATCH] xhr now expects Access-Control-Allow-Methods set for methods other than GET, HEAD, POST --- src/xhr.js | 12 ++++++-- test/xhr_test.js | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/xhr.js b/src/xhr.js index 70c7d4b30..85e3aa1ef 100644 --- a/src/xhr.js +++ b/src/xhr.js @@ -180,7 +180,15 @@ class XMLHttpRequest { if (this._cors) { const allowedOrigin = response.headers.get('Access-Control-Allow-Origin'); if (!(allowedOrigin === '*' || allowedOrigin === this._cors)) { - this._error = new DOMException(DOMException.SECURITY_ERR, 'Cannot make request to different domain'); + this._error = new DOMException(DOMException.SECURITY_ERR, 'Cannot make request to different domain'); + } + else if (!/^(GET|HEAD|POST)$/.test(this._method)) { + const allowedMethods = response.headers.get('Access-Control-Allow-Methods'); + if (!allowedMethods || allowedMethods.indexOf(request.method) == -1) { + this._error = new DOMException(DOMException.SECURITY_ERR, 'Cannot make request with not-allowed method('+this._method+')'); + } + } + if (this._error) { this._browser.errors.push(this._error); this._stateChanged(XMLHttpRequest.DONE); this._fire('progress'); @@ -188,7 +196,7 @@ class XMLHttpRequest { this._fire('loadend'); this.raise('error', this._error.message, { exception: this._error }); return; - } + } } // Store the response so getters have acess access it diff --git a/test/xhr_test.js b/test/xhr_test.js index 0499b3a9f..5923347b1 100644 --- a/test/xhr_test.js +++ b/test/xhr_test.js @@ -293,6 +293,23 @@ describe('XMLHttpRequest', function() { `); + brains.static('/cors-put/:path', ` + + + + + `); }); describe('no access control header', function() { @@ -333,6 +350,69 @@ describe('XMLHttpRequest', function() { }); }); + describe('no access control method header', function() { + before(async function() { + const cors = await thirdParty(); + cors.put('/access-star-no-method', function(req, res) { + res.header('Access-Control-Allow-Origin', '*'); + res.send('Access *'); + }); + }); + + it('should fail', async function() { + try { + await browser.visit('/cors-put/access-star-no-method'); + } catch (error) { + browser.assert.text('title', 'error'); + return; + } + assert(false, 'Error not propagated to window'); + }); + + it('should capture error', function() { + assert.equal(browser.errors[0].toString(), 'Cannot make request with not-allowed method(PUT): 18'); + }); + }); + + describe('access * with allowed method', function() { + before(async function() { + const cors = await thirdParty(); + cors.put('/access-star-with-method', function(req, res) { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Methods', 'PUT'); + res.send('Access * with PUT'); + }); + }); + + it('should allow access', async function() { + await browser.visit('/cors-put/access-star-with-method'); + browser.assert.text('title', 'Access * with PUT'); + }); + }); + + describe('no access with not simple request', function() { + before(async function() { + const cors = await thirdParty(); + cors.put('/put-request', function(req, res) { + res.send('Access with PUT'); + }); + }); + + it('should fail', async function() { + try { + await browser.visit('/cors-put/put-request'); + } catch (error) { + browser.assert.text('title', 'error'); + return; + } + assert(false, 'Error not propagated to window'); + }); + + it('should capture error', function() { + assert.equal(browser.errors[0].toString(), 'Cannot make request to different domain: 18'); + }); + }); + describe('access to origin', function() { before(async function() { const cors = await thirdParty();