2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-17 19:21:29 +03:00

fix(http): accept path-only url when socketPath is set (#6611) (#10930)

Co-authored-by: Jason Saayman <jasonsaayman@gmail.com>
This commit is contained in:
Devendra Reddy Pennabadi
2026-06-16 23:53:12 +05:30
committed by GitHub
parent 97fb8af785
commit 01e869dd28
3 changed files with 71 additions and 4 deletions
+5 -1
View File
@@ -4,4 +4,8 @@
## Bug Fixes
- **Proxy Agent Streams:** Guarded Node HTTP adapter TCP keep-alive setup so proxy agents that return generic Duplex streams do not throw when `setKeepAlive` is unavailable. (**#10917**, closes **#10908**)
- **HTTP Adapter - socketPath:** Path-only request URLs (e.g. `'/foo'`) now work again with `config.socketPath`, fixing the `TypeError [ERR_INVALID_URL]` regression introduced in 1.7.4 when `new URL()` was added to the dispatch path. A synthetic `http://localhost` base is supplied only when an own `socketPath` is set, so absolute URLs, non-socket requests, and prototype-polluted `socketPath` values are unaffected. (**#6611**)
## Release Tracking
- **Proxy Agent Streams:** Guarded Node HTTP adapter TCP keep-alive setup so proxy agents that return generic Duplex streams do not throw when `setKeepAlive` is unavailable. (**#10917**, closes **#10908**)
+9 -2
View File
@@ -477,6 +477,7 @@ export default isHttpAdapterSupported &&
let http2Options = own('http2Options');
const responseType = own('responseType');
const responseEncoding = own('responseEncoding');
const socketPath = own('socketPath');
const httpAgent = own('httpAgent');
const httpsAgent = own('httpsAgent');
const method = own('method').toUpperCase();
@@ -603,7 +604,14 @@ export default isHttpAdapterSupported &&
// Parse url
const fullPath = buildFullPath(own('baseURL'), own('url'), own('allowAbsoluteUrls'), config);
const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined);
// Unix-socket requests (own socketPath) commonly pass a path-only url
// like '/foo'; supply a synthetic base so new URL() can still parse it.
// Use the own-property value (not config.socketPath) so a polluted
// prototype cannot influence URL base selection.
const urlBase = socketPath
? 'http://localhost'
: (platform.hasBrowserEnv ? platform.origin : undefined);
const parsed = new URL(fullPath, urlBase);
const protocol = parsed.protocol || supportedProtocols[0];
if (protocol === 'data:') {
@@ -842,7 +850,6 @@ export default isHttpAdapterSupported &&
// cacheable-lookup integration hotfix
!utils.isUndefined(lookup) && (options.lookup = lookup);
const socketPath = own('socketPath');
if (socketPath) {
if (typeof socketPath !== 'string') {
return reject(
+57 -1
View File
@@ -6368,9 +6368,10 @@ describe('supports http with nodejs', () => {
: path.join(os.tmpdir(), `${pipe}.sock`);
}
function startUnixServer(socketPath) {
function startUnixServer(socketPath, onRequest) {
return new Promise((resolveStart, rejectStart) => {
const server = http.createServer((req, res) => {
onRequest && onRequest(req);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, url: req.url }));
});
@@ -6409,6 +6410,61 @@ describe('supports http with nodejs', () => {
}
});
it('accepts a path-only url when socketPath is set (regression #6611)', async () => {
const socketPath = makeSocketPath();
const server = await startUnixServer(socketPath);
try {
const res = await axios.get('/echo?q=1', { socketPath });
assert.strictEqual(res.status, 200);
assert.strictEqual(res.data.ok, true);
assert.strictEqual(res.data.url, '/echo?q=1');
} finally {
await stopUnixServer(server, socketPath);
}
});
it('accepts a path-only url when socketPath matches allowedSocketPaths', async () => {
const socketPath = makeSocketPath();
const server = await startUnixServer(socketPath);
try {
const res = await axios.get('/echo?q=1', {
socketPath,
allowedSocketPaths: [socketPath],
});
assert.strictEqual(res.status, 200);
assert.strictEqual(res.data.ok, true);
assert.strictEqual(res.data.url, '/echo?q=1');
} finally {
await stopUnixServer(server, socketPath);
}
});
it('ignores a prototype-polluted socketPath (security, regression #6611)', async () => {
const socketPath = makeSocketPath();
let requestCount = 0;
const server = await startUnixServer(socketPath, () => {
requestCount += 1;
});
// Pollute the prototype so `socketPath` is visible via the chain but is
// NOT an own property of the request config.
Object.prototype.socketPath = socketPath;
try {
// With no own socketPath, the polluted prototype value must not be
// honored: the path-only url gets no synthetic base and the request is
// never routed to the (attacker-controlled) socket, so it rejects
// instead of silently connecting.
await assert.rejects(axios.get('/echo?q=1'), (err) => {
assert.ok(err instanceof Error);
assert.strictEqual(err.code, AxiosError.ERR_INVALID_URL);
return true;
});
assert.strictEqual(requestCount, 0);
} finally {
delete Object.prototype.socketPath;
await stopUnixServer(server, socketPath);
}
});
it('allows socketPath when it matches an allowedSocketPaths string', async () => {
const socketPath = makeSocketPath();
const server = await startUnixServer(socketPath);