Skip to content

Commit 7b73c03

Browse files
authored
fix: Chrome browser console warning about unsafe header access-control-expose-headers when calling Cloud Function (#2095)
1 parent 0576f56 commit 7b73c03

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

src/RESTController.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,14 @@ const RESTController = {
111111
let response;
112112
try {
113113
response = JSON.parse(xhr.responseText);
114+
const availableHeaders = typeof xhr.getAllResponseHeaders === 'function' ? xhr.getAllResponseHeaders() : "";
114115
headers = {};
115-
if (typeof xhr.getResponseHeader === 'function' && xhr.getResponseHeader('access-control-expose-headers')) {
116+
if (typeof xhr.getResponseHeader === 'function' && availableHeaders?.indexOf('access-control-expose-headers') >= 0) {
116117
const responseHeaders = xhr.getResponseHeader('access-control-expose-headers').split(', ');
117118
responseHeaders.forEach(header => {
118-
headers[header] = xhr.getResponseHeader(header.toLowerCase());
119+
if (availableHeaders.indexOf(header.toLowerCase()) >= 0) {
120+
headers[header] = xhr.getResponseHeader(header.toLowerCase());
121+
}
119122
});
120123
}
121124
} catch (e) {

src/__tests__/RESTController-test.js

+63
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ describe('RESTController', () => {
221221
getResponseHeader: function (header) {
222222
return headers[header];
223223
},
224+
getAllResponseHeaders: function() {
225+
return Object.keys(headers).map(key => `${key}: ${headers[key]}`).join('\n');
226+
},
224227
send: function () {
225228
this.status = 200;
226229
this.responseText = '{}';
@@ -241,6 +244,9 @@ describe('RESTController', () => {
241244
getResponseHeader: function (header) {
242245
return headers[header];
243246
},
247+
getAllResponseHeaders: function() {
248+
return Object.keys(headers).map(key => `${key}: ${headers[key]}`).join('\n');
249+
},
244250
send: function () {
245251
this.status = 200;
246252
this.responseText = '{}';
@@ -253,6 +259,63 @@ describe('RESTController', () => {
253259
expect(response._headers['X-Parse-Push-Status-Id']).toBe('5678');
254260
});
255261

262+
it('does not call getRequestHeader with no headers or no getAllResponseHeaders', async () => {
263+
const XHR = function () {};
264+
XHR.prototype = {
265+
open: function () {},
266+
setRequestHeader: function () {},
267+
getResponseHeader: jest.fn(),
268+
send: function () {
269+
this.status = 200;
270+
this.responseText = '{"result":"hello"}';
271+
this.readyState = 4;
272+
this.onreadystatechange();
273+
},
274+
};
275+
RESTController._setXHR(XHR);
276+
await RESTController.request('GET', 'classes/MyObject', {}, {});
277+
expect(XHR.prototype.getResponseHeader.mock.calls.length).toBe(0);
278+
279+
XHR.prototype.getAllResponseHeaders = jest.fn();
280+
await RESTController.request('GET', 'classes/MyObject', {}, {});
281+
expect(XHR.prototype.getAllResponseHeaders.mock.calls.length).toBe(1);
282+
expect(XHR.prototype.getResponseHeader.mock.calls.length).toBe(0);
283+
});
284+
285+
it('does not invoke Chrome browser console error on getResponseHeader', async () => {
286+
const headers = {
287+
'access-control-expose-headers': 'a, b, c',
288+
'a' : 'value',
289+
'b' : 'value',
290+
'c' : 'value',
291+
}
292+
const XHR = function () {};
293+
XHR.prototype = {
294+
open: function () {},
295+
setRequestHeader: function () {},
296+
getResponseHeader: jest.fn(key => {
297+
if (Object.keys(headers).includes(key)) {
298+
return headers[key];
299+
}
300+
throw new Error("Chrome creates a console error here.");
301+
}),
302+
getAllResponseHeaders: jest.fn(() => {
303+
return Object.keys(headers).map(key => `${key}: ${headers[key]}`).join('\r\n');
304+
}),
305+
send: function () {
306+
this.status = 200;
307+
this.responseText = '{"result":"hello"}';
308+
this.readyState = 4;
309+
this.onreadystatechange();
310+
},
311+
};
312+
RESTController._setXHR(XHR);
313+
await RESTController.request('GET', 'classes/MyObject', {}, {});
314+
expect(XHR.prototype.getAllResponseHeaders.mock.calls.length).toBe(1);
315+
expect(XHR.prototype.getResponseHeader.mock.calls.length).toBe(4);
316+
});
317+
318+
256319
it('handles invalid header', async () => {
257320
const XHR = function () {};
258321
XHR.prototype = {

0 commit comments

Comments
 (0)