mirror of
https://github.com/tenrok/axios.git
synced 2026-06-17 19:21:29 +03:00
fix: short-circuits on any truthy non-boolean in withXSRFToken (#10762)
This commit is contained in:
@@ -54,10 +54,18 @@ export default (config) => {
|
||||
// Specifically not if we're in a web worker, or react-native.
|
||||
|
||||
if (platform.hasStandardBrowserEnv) {
|
||||
withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig));
|
||||
if (utils.isFunction(withXSRFToken)) {
|
||||
withXSRFToken = withXSRFToken(newConfig);
|
||||
}
|
||||
|
||||
if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(newConfig.url))) {
|
||||
// Add xsrf header
|
||||
// Strict boolean check — prevents proto-pollution gadgets (e.g. Object.prototype.withXSRFToken = 1)
|
||||
// and misconfigurations (e.g. "false") from short-circuiting the same-origin check and leaking
|
||||
// the XSRF token cross-origin. See GHSA-xx6v-rp6x-q39c.
|
||||
const shouldSendXSRF =
|
||||
withXSRFToken === true ||
|
||||
(withXSRFToken == null && isURLSameOrigin(newConfig.url));
|
||||
|
||||
if (shouldSendXSRF) {
|
||||
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
|
||||
|
||||
if (xsrfValue) {
|
||||
|
||||
@@ -179,4 +179,60 @@ describe('xsrf (vitest browser)', () => {
|
||||
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token);
|
||||
});
|
||||
});
|
||||
|
||||
// GHSA-xx6v-rp6x-q39c: non-boolean truthy withXSRFToken must not short-circuit
|
||||
// the same-origin check and leak the XSRF token cross-origin.
|
||||
describe('GHSA-xx6v-rp6x-q39c non-boolean withXSRFToken', () => {
|
||||
afterEach(() => {
|
||||
delete Object.prototype.withXSRFToken;
|
||||
});
|
||||
|
||||
const leakCases = [
|
||||
['number 1', 1],
|
||||
['string "false"', 'false'],
|
||||
['empty object', {}],
|
||||
['empty array', []],
|
||||
];
|
||||
|
||||
leakCases.forEach(([label, value]) => {
|
||||
it(`should not send xsrf header cross-origin when withXSRFToken = ${label}`, async () => {
|
||||
setXsrfCookie('12345');
|
||||
|
||||
const request = await sendRequest('http://example.com/', {
|
||||
withXSRFToken: value,
|
||||
});
|
||||
|
||||
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not send xsrf header cross-origin when Object.prototype.withXSRFToken is polluted', async () => {
|
||||
Object.prototype.withXSRFToken = 1;
|
||||
setXsrfCookie('12345');
|
||||
|
||||
const request = await sendRequest('http://example.com/');
|
||||
|
||||
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should still send xsrf header cross-origin when withXSRFToken === true (strict)', async () => {
|
||||
const token = '12345';
|
||||
setXsrfCookie(token);
|
||||
|
||||
const request = await sendRequest('http://example.com/', {
|
||||
withXSRFToken: true,
|
||||
});
|
||||
|
||||
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token);
|
||||
});
|
||||
|
||||
it('should still send xsrf header same-origin when withXSRFToken is undefined', async () => {
|
||||
const token = '12345';
|
||||
setXsrfCookie(token);
|
||||
|
||||
const request = await sendRequest('/foo');
|
||||
|
||||
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user