mirror of
https://github.com/tenrok/axios.git
synced 2026-06-23 20:40:40 +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.
|
// Specifically not if we're in a web worker, or react-native.
|
||||||
|
|
||||||
if (platform.hasStandardBrowserEnv) {
|
if (platform.hasStandardBrowserEnv) {
|
||||||
withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig));
|
if (utils.isFunction(withXSRFToken)) {
|
||||||
|
withXSRFToken = withXSRFToken(newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(newConfig.url))) {
|
// Strict boolean check — prevents proto-pollution gadgets (e.g. Object.prototype.withXSRFToken = 1)
|
||||||
// Add xsrf header
|
// 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);
|
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
|
||||||
|
|
||||||
if (xsrfValue) {
|
if (xsrfValue) {
|
||||||
|
|||||||
@@ -179,4 +179,60 @@ describe('xsrf (vitest browser)', () => {
|
|||||||
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token);
|
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