diff --git a/documentation/2-options.md b/documentation/2-options.md index f5e10669a..e7385a265 100644 --- a/documentation/2-options.md +++ b/documentation/2-options.md @@ -918,6 +918,37 @@ By default, requests will not use [method rewriting](https://datatracker.ietf.or For example, when sending a `POST` request and receiving a `302`, it will resend the body to the new location using the same HTTP method (`POST` in this case). To rewrite the request as `GET`, set this option to `true`. +### `enableUnixSockets` + +**Type: `boolean`**\ +**Default: `true`** + +When enabled, requests can also be sent via [UNIX Domain Sockets](https://serverfault.com/questions/124517/what-is-the-difference-between-unix-sockets-and-tcp-ip-sockets). Please note that in the upcoming major release (Got v13) this default will be changed to `false` for security reasons. + +> **Warning** +> Make sure you do your own URL sanitizing if you accept untrusted user input for the URL. + +Use the following URL scheme: `PROTOCOL://unix:SOCKET:PATH` + +- `PROTOCOL` - `http` or `https` +- `SOCKET` - Absolute path to a UNIX domain socket, for example: `/var/run/docker.sock` +- `PATH` - Request path, for example: `/v2/keys` + +```js +import got from 'got'; + +await got('http://unix:/var/run/docker.sock:/containers/json', {enableUnixSockets: true}); + +// Or without protocol (HTTP by default) +await got('unix:/var/run/docker.sock:/containers/json', {enableUnixSockets: true}); + +// Disable Unix sockets +const gotUnixSocketsDisabled = got.extend({enableUnixSockets: false}); + +// RequestError: Using UNIX domain sockets but option `enableUnixSockets` is not enabled +await gotUnixSocketsDisabled('http://unix:/var/run/docker.sock:/containers/json'); +``` + ## Methods ### `options.merge(other: Options | OptionsInit)` diff --git a/documentation/migration-guides/axios.md b/documentation/migration-guides/axios.md index f4a457291..a17f606df 100644 --- a/documentation/migration-guides/axios.md +++ b/documentation/migration-guides/axios.md @@ -24,7 +24,7 @@ We deeply care about readability, so we renamed these options: - `httpAgent` → [`agent.http`](../2-options.md#agent) - `httpsAgent` → [`agent.https`](../2-options.md#agent) -- `socketPath` → [`url`](../tips.md#unix) +- `socketPath` → [`url`](../2-options.md#enableunixsockets) - `responseEncoding` → [`encoding`](../2-options.md#encoding) - `auth.username` → [`username`](../2-options.md#username) - `auth.password` → [`password`](../2-options.md#password) diff --git a/documentation/migration-guides/request.md b/documentation/migration-guides/request.md index b34329424..b6e80bea6 100644 --- a/documentation/migration-guides/request.md +++ b/documentation/migration-guides/request.md @@ -46,7 +46,7 @@ These Got options are the same as with Request: - [`localAddress`](../2-options.md#localaddress) - [`headers`](../2-options.md#headers) - [`createConnection`](../2-options.md#createconnection) -- [UNIX sockets](../tips.md#unixsockets): `http://unix:SOCKET:PATH` +- [UNIX sockets](../2-options.md#enableunixsockets): `http://unix:SOCKET:PATH` The `time` option does not exist, assume [it's always true](../6-timeout.md). diff --git a/documentation/tips.md b/documentation/tips.md index 014a28d94..1058e4188 100644 --- a/documentation/tips.md +++ b/documentation/tips.md @@ -92,21 +92,7 @@ for await (const commitData of pagination) { ### UNIX Domain Sockets -Requests can also be sent via [UNIX Domain Sockets](https://serverfault.com/questions/124517/what-is-the-difference-between-unix-sockets-and-tcp-ip-sockets).\ -Use the following URL scheme: `PROTOCOL://unix:SOCKET:PATH` - -- `PROTOCOL` - `http` or `https` -- `SOCKET` - Absolute path to a unix domain socket, for example: `/var/run/docker.sock` -- `PATH` - Request path, for example: `/v2/keys` - -```js -import got from 'got'; - -await got('http://unix:/var/run/docker.sock:/containers/json'); - -// Or without protocol (HTTP by default) -await got('unix:/var/run/docker.sock:/containers/json'); -``` +See the [`enableUnixSockets` option](./2-options.md#enableunixsockets). ### Testing diff --git a/readme.md b/readme.md index f06ccf3a5..0af7a6724 100644 --- a/readme.md +++ b/readme.md @@ -202,7 +202,7 @@ For advanced JSON usage, check out the [`parseJson`](documentation/2-options.md# - [x] [RFC compliant caching](documentation/cache.md) - [x] [Proxy support](documentation/tips.md#proxying) -- [x] [Unix Domain Sockets](documentation/tips.md#unix) +- [x] [Unix Domain Sockets](documentation/2-options.md#enableunixsockets) #### Integration diff --git a/source/core/options.ts b/source/core/options.ts index 33073636d..9fb461c50 100644 --- a/source/core/options.ts +++ b/source/core/options.ts @@ -827,6 +827,7 @@ const defaultInternals: Options['_internals'] = { }, setHost: true, maxHeaderSize: undefined, + enableUnixSockets: true, }; const cloneInternals = (internals: typeof defaultInternals) => { @@ -1428,6 +1429,10 @@ export default class Options { } if (url.hostname === 'unix') { + if (!this._internals.enableUnixSockets) { + throw new Error('Using UNIX domain sockets but option `enableUnixSockets` is not enabled'); + } + const matches = /(?.+?):(?.+)/.exec(`${url.pathname}${url.search}`); if (matches?.groups) { @@ -2345,6 +2350,16 @@ export default class Options { this._internals.maxHeaderSize = value; } + get enableUnixSockets() { + return this._internals.enableUnixSockets; + } + + set enableUnixSockets(value: boolean) { + assert.boolean(value); + + this._internals.enableUnixSockets = value; + } + // eslint-disable-next-line @typescript-eslint/naming-convention toJSON() { return {...this._internals}; diff --git a/test/redirects.ts b/test/redirects.ts index 1b6d4324b..927221aca 100644 --- a/test/redirects.ts +++ b/test/redirects.ts @@ -42,10 +42,12 @@ const unixHostname: Handler = (_request, response) => { response.end(); }; -test('cannot redirect to unix protocol', withServer, async (t, server, got) => { +test('cannot redirect to UNIX protocol when UNIX sockets are enabled', withServer, async (t, server, got) => { server.get('/protocol', unixProtocol); server.get('/hostname', unixHostname); + t.true(got.defaults.options.enableUnixSockets); + await t.throwsAsync(got('protocol'), { message: 'Cannot redirect to UNIX socket', instanceOf: RequestError, @@ -57,6 +59,25 @@ test('cannot redirect to unix protocol', withServer, async (t, server, got) => { }); }); +test('cannot redirect to UNIX protocol when UNIX sockets are not enabled', withServer, async (t, server, got) => { + server.get('/protocol', unixProtocol); + server.get('/hostname', unixHostname); + + const gotUnixSocketsDisabled = got.extend({enableUnixSockets: false}); + + t.false(gotUnixSocketsDisabled.defaults.options.enableUnixSockets); + + await t.throwsAsync(gotUnixSocketsDisabled('protocol'), { + message: 'Cannot redirect to UNIX socket', + instanceOf: RequestError, + }); + + await t.throwsAsync(gotUnixSocketsDisabled('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 059785ea7..4523ec76f 100644 --- a/test/unix-socket.ts +++ b/test/unix-socket.ts @@ -68,4 +68,29 @@ if (process.platform !== 'win32') { const url = format('http://unix:%s:%s', server.socketPath, '/'); t.is((await got(url)).body, 'ok'); }); + + test('`unix:` fails when UNIX sockets are not enabled', async t => { + const gotUnixSocketsDisabled = got.extend({enableUnixSockets: false}); + + t.false(gotUnixSocketsDisabled.defaults.options.enableUnixSockets); + await t.throwsAsync( + gotUnixSocketsDisabled('unix:'), + { + message: 'Using UNIX domain sockets but option `enableUnixSockets` is not enabled', + }, + ); + }); + + test('`http://unix:/` fails when UNIX sockets are not enabled', async t => { + const gotUnixSocketsDisabled = got.extend({enableUnixSockets: false}); + + t.false(gotUnixSocketsDisabled.defaults.options.enableUnixSockets); + + await t.throwsAsync( + gotUnixSocketsDisabled('http://unix:'), + { + message: 'Using UNIX domain sockets but option `enableUnixSockets` is not enabled', + }, + ); + }); }