mirror of
https://github.com/tenrok/axios.git
synced 2026-06-17 19:21:29 +03:00
fix: header security issues (#10749)
* fix: improve regex in AxiosURLSearchParams * fix: header should check has own property * chore: implement cubic feedback in test case
This commit is contained in:
@@ -520,7 +520,8 @@ export default isHttpAdapterSupported &&
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
// support for https://www.npmjs.com/package/form-data api
|
// support for https://www.npmjs.com/package/form-data api
|
||||||
} else if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) {
|
} else if (utils.isFormData(data) && utils.isFunction(data.getHeaders) &&
|
||||||
|
data.getHeaders !== Object.prototype.getHeaders) {
|
||||||
headers.set(data.getHeaders());
|
headers.set(data.getHeaders());
|
||||||
|
|
||||||
if (!headers.hasContentLength()) {
|
if (!headers.hasContentLength()) {
|
||||||
|
|||||||
+10
-10
@@ -257,16 +257,16 @@ const G = getGlobal();
|
|||||||
const FormDataCtor = typeof G.FormData !== 'undefined' ? G.FormData : undefined;
|
const FormDataCtor = typeof G.FormData !== 'undefined' ? G.FormData : undefined;
|
||||||
|
|
||||||
const isFormData = (thing) => {
|
const isFormData = (thing) => {
|
||||||
let kind;
|
if (!thing) return false;
|
||||||
return thing && (
|
if (FormDataCtor && thing instanceof FormDataCtor) return true;
|
||||||
(FormDataCtor && thing instanceof FormDataCtor) || (
|
// Reject plain objects inheriting directly from Object.prototype so prototype-pollution gadgets can't spoof FormData (GHSA-6chq-wfr3-2hj9).
|
||||||
isFunction(thing.append) && (
|
const proto = getPrototypeOf(thing);
|
||||||
(kind = kindOf(thing)) === 'formdata' ||
|
if (!proto || proto === Object.prototype) return false;
|
||||||
// detect form-data instance
|
if (!isFunction(thing.append)) return false;
|
||||||
(kind === 'object' && isFunction(thing.toString) && thing.toString() === '[object FormData]')
|
const kind = kindOf(thing);
|
||||||
)
|
return kind === 'formdata' ||
|
||||||
)
|
// detect form-data instance
|
||||||
);
|
(kind === 'object' && isFunction(thing.toString) && thing.toString() === '[object FormData]');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2512,6 +2512,55 @@ describe('supports http with nodejs', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('prototype pollution (GHSA-6chq-wfr3-2hj9)', () => {
|
||||||
|
const pollutedKeys = ['getHeaders', 'append', 'pipe', 'on', 'once'];
|
||||||
|
const toStringTagSym = Symbol.toStringTag;
|
||||||
|
|
||||||
|
function pollute() {
|
||||||
|
Object.prototype[toStringTagSym] = 'FormData';
|
||||||
|
Object.prototype.append = () => {};
|
||||||
|
Object.prototype.getHeaders = () => ({
|
||||||
|
'x-injected': 'attacker',
|
||||||
|
'authorization': 'Bearer ATTACKER_TOKEN',
|
||||||
|
});
|
||||||
|
Object.prototype.pipe = function (d) { if (d && d.end) d.end(); return d; };
|
||||||
|
Object.prototype.on = function () { return this; };
|
||||||
|
Object.prototype.once = function () { return this; };
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
for (const k of pollutedKeys) delete Object.prototype[k];
|
||||||
|
delete Object.prototype[toStringTagSym];
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should not merge prototype-polluted getHeaders into outgoing request', async () => {
|
||||||
|
let receivedHeaders;
|
||||||
|
const server = await startHTTPServer(
|
||||||
|
(req, res) => {
|
||||||
|
receivedHeaders = req.headers;
|
||||||
|
res.end('{}');
|
||||||
|
},
|
||||||
|
{ port: SERVER_PORT }
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
pollute();
|
||||||
|
await axios.post(
|
||||||
|
`http://localhost:${server.address().port}/`,
|
||||||
|
{ userId: 42 },
|
||||||
|
{ headers: { 'Authorization': 'Bearer VALID_USER_TOKEN' } }
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanup();
|
||||||
|
await stopHTTPServer(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.ok(receivedHeaders, 'request did not reach server');
|
||||||
|
assert.strictEqual(receivedHeaders['x-injected'], undefined);
|
||||||
|
assert.notStrictEqual(receivedHeaders['authorization'], 'Bearer ATTACKER_TOKEN');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('toFormData helper', () => {
|
describe('toFormData helper', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user