2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-23 20:40:40 +03:00

fix: guard socketPath with own() to prevent prototype pollution SSRF (#10901)

* fix: guard socketPath with own() to prevent prototype pollution SSRF

CVE-2026-42264 fix introduced the own() helper to guard config reads,
but socketPath and allowedSocketPaths were missed. An attacker who can
pollute Object.prototype.socketPath (via another dependency) can
redirect all axios requests to a Unix socket (e.g. Docker daemon),
enabling SSRF and container escape.

Fix: use own('socketPath') and own('allowedSocketPaths') instead of
direct config property access.

Ref: GHSA-72mg-mc2j-cwf6
Fixes: CVE-2026-42264 (complete)

* docs: add socketPath security release note

---------

Co-authored-by: Jay <jasonsaayman@gmail.com>
This commit is contained in:
Nezuko Agent
2026-05-25 00:05:48 +07:00
committed by GitHub
parent 7b3369a9c4
commit 140a17944a
2 changed files with 12 additions and 9 deletions
+1
View File
@@ -9,6 +9,7 @@
## Bug Fixes ## Bug Fixes
- **AxiosHeaders:** Silently skip empty response header names emitted by some React Native Android responses instead of throwing. (**#6959**, **#10875**) - **AxiosHeaders:** Silently skip empty response header names emitted by some React Native Android responses instead of throwing. (**#6959**, **#10875**)
- **HTTP Adapter - Socket Path:** Ignore inherited `socketPath` and `allowedSocketPaths` config values when building Node.js requests, preventing prototype-pollution SSRF via Unix sockets. (**#10901**)
- **React Native FormData:** Clear the default `Content-Type` header for React Native `FormData` requests so Android can build multipart bodies with the correct boundary. (**#10898**) - **React Native FormData:** Clear the default `Content-Type` header for React Native `FormData` requests so Android can build multipart bodies with the correct boundary. (**#10898**)
- **Request Data:** Preserve enumerable symbol keys when merging plain request data before `transformRequest`. (**#6392**) - **Request Data:** Preserve enumerable symbol keys when merging plain request data before `transformRequest`. (**#6392**)
+11 -9
View File
@@ -790,19 +790,21 @@ export default isHttpAdapterSupported &&
// cacheable-lookup integration hotfix // cacheable-lookup integration hotfix
!utils.isUndefined(lookup) && (options.lookup = lookup); !utils.isUndefined(lookup) && (options.lookup = lookup);
if (config.socketPath) { const socketPath = own('socketPath');
if (typeof config.socketPath !== 'string') { if (socketPath) {
if (typeof socketPath !== 'string') {
return reject( return reject(
new AxiosError('socketPath must be a string', AxiosError.ERR_BAD_OPTION_VALUE, config) new AxiosError('socketPath must be a string', AxiosError.ERR_BAD_OPTION_VALUE, config)
); );
} }
if (config.allowedSocketPaths != null) { const allowedSocketPaths = own('allowedSocketPaths');
const allowed = Array.isArray(config.allowedSocketPaths) if (allowedSocketPaths != null) {
? config.allowedSocketPaths const allowed = Array.isArray(allowedSocketPaths)
: [config.allowedSocketPaths]; ? allowedSocketPaths
: [allowedSocketPaths];
const resolvedSocket = resolvePath(config.socketPath); const resolvedSocket = resolvePath(socketPath);
const isAllowed = allowed.some( const isAllowed = allowed.some(
(entry) => typeof entry === 'string' && resolvePath(entry) === resolvedSocket (entry) => typeof entry === 'string' && resolvePath(entry) === resolvedSocket
); );
@@ -810,7 +812,7 @@ export default isHttpAdapterSupported &&
if (!isAllowed) { if (!isAllowed) {
return reject( return reject(
new AxiosError( new AxiosError(
`socketPath "${config.socketPath}" is not permitted by allowedSocketPaths`, `socketPath "${socketPath}" is not permitted by allowedSocketPaths`,
AxiosError.ERR_BAD_OPTION_VALUE, AxiosError.ERR_BAD_OPTION_VALUE,
config config
) )
@@ -818,7 +820,7 @@ export default isHttpAdapterSupported &&
} }
} }
options.socketPath = config.socketPath; options.socketPath = socketPath;
} else { } else {
options.hostname = parsed.hostname.startsWith('[') options.hostname = parsed.hostname.startsWith('[')
? parsed.hostname.slice(1, -1) ? parsed.hostname.slice(1, -1)