2
0
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:
Jay
2026-04-19 16:24:36 +02:00
committed by GitHub
parent 42eb721eeb
commit 1728aa1b15
2 changed files with 67 additions and 3 deletions
+11 -3
View File
@@ -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) {
+56
View File
@@ -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);
});
});
});