From 861ccd9ac2237df762a9e2beed7edd88c60782dc Mon Sep 17 00:00:00 2001 From: Szymon Marczak <36894700+szmarczak@users.noreply.github.com> Date: Wed, 25 May 2022 20:19:25 +0200 Subject: [PATCH] Disable redirects to UNIX sockets (#2047) --- source/core/index.ts | 6 +++++ source/core/utils/is-unix-socket-url.ts | 6 +++++ test/redirects.ts | 31 ++++++++++++++++++++++++- test/unix-socket.ts | 15 ++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 source/core/utils/is-unix-socket-url.ts diff --git a/source/core/index.ts b/source/core/index.ts index 63e2c5e7b..e521a1a56 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -23,6 +23,7 @@ import calculateRetryDelay from './calculate-retry-delay.js'; import Options, {OptionsError, OptionsInit} from './options.js'; import {isResponseOk, Response} from './response.js'; import isClientRequest from './utils/is-client-request.js'; +import isUnixSocketURL from './utils/is-unix-socket-url.js'; import { RequestError, ReadError, @@ -727,6 +728,11 @@ export default class Request extends Duplex implements RequestEvents { const redirectBuffer = Buffer.from(response.headers.location, 'binary').toString(); const redirectUrl = new URL(redirectBuffer, url); + if (!isUnixSocketURL(url as URL) && isUnixSocketURL(redirectUrl)) { + this._beforeError(new RequestError('Cannot redirect to UNIX socket', {}, this)); + return; + } + // Redirecting to a different site, clear sensitive data. if (redirectUrl.hostname !== (url as URL).hostname || redirectUrl.port !== (url as URL).port) { if ('host' in updatedOptions.headers) { diff --git a/source/core/utils/is-unix-socket-url.ts b/source/core/utils/is-unix-socket-url.ts new file mode 100644 index 000000000..af1dc3d09 --- /dev/null +++ b/source/core/utils/is-unix-socket-url.ts @@ -0,0 +1,6 @@ +import {URL} from 'url'; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export default function isUnixSocketURL(url: URL) { + return url.protocol === 'unix:' || url.hostname === 'unix'; +} diff --git a/test/redirects.ts b/test/redirects.ts index 37d679270..1b6d4324b 100644 --- a/test/redirects.ts +++ b/test/redirects.ts @@ -2,7 +2,7 @@ import {Buffer} from 'buffer'; import test from 'ava'; import {Handler} from 'express'; import nock from 'nock'; -import got, {MaxRedirectsError} from '../source/index.js'; +import got, {MaxRedirectsError, RequestError} from '../source/index.js'; import withServer, {withHttpsServer} from './helpers/with-server.js'; const reachedHandler: Handler = (_request, response) => { @@ -28,6 +28,35 @@ const relativeHandler: Handler = (_request, response) => { response.end(); }; +const unixProtocol: Handler = (_request, response) => { + response.writeHead(302, { + location: 'unix:/var/run/docker.sock:/containers/json', + }); + response.end(); +}; + +const unixHostname: Handler = (_request, response) => { + response.writeHead(302, { + location: 'http://unix:/var/run/docker.sock:/containers/json', + }); + response.end(); +}; + +test('cannot redirect to unix protocol', withServer, async (t, server, got) => { + server.get('/protocol', unixProtocol); + server.get('/hostname', unixHostname); + + await t.throwsAsync(got('protocol'), { + message: 'Cannot redirect to UNIX socket', + instanceOf: RequestError, + }); + + await t.throwsAsync(got('hostname'), { + message: 'Cannot redirect to UNIX socket', + instanceOf: RequestError, + }); +}); + test('follows redirect', withServer, async (t, server, got) => { server.get('/', reachedHandler); server.get('/finite', finiteHandler); diff --git a/test/unix-socket.ts b/test/unix-socket.ts index f175e6ac5..059785ea7 100644 --- a/test/unix-socket.ts +++ b/test/unix-socket.ts @@ -9,6 +9,13 @@ const okHandler: Handler = (_request, response) => { response.end('ok'); }; +const redirectHandler: Handler = (_request, response) => { + response.writeHead(302, { + location: 'foo', + }); + response.end(); +}; + if (process.platform !== 'win32') { test('works', withSocketServer, async (t, server) => { server.on('/', okHandler); @@ -53,4 +60,12 @@ if (process.platform !== 'win32') { const url = format('http://unix:%s:%s', server.socketPath, '/?a=1'); t.is((await got(url)).body, 'ok'); }); + + test('redirects work', withSocketServer, async (t, server) => { + server.on('/', redirectHandler); + server.on('/foo', okHandler); + + const url = format('http://unix:%s:%s', server.socketPath, '/'); + t.is((await got(url)).body, 'ok'); + }); }