Skip to content

Commit

Permalink
Make uploadUrls recommended (#3182)
Browse files Browse the repository at this point in the history
* Make uploadUrls recommended

- warn on startup if uploadUrls is not specified as not specifying it is a security risk
- improve docs to make it more clear why uploadUrls should be specified (say that uploadUrls is required even though it is not, due to backward compatibility)
- no longer require_tld (it gives a false security) - fixes #2831

* Apply suggestions from code review

* Apply suggestions from code review

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>

* remove `example: []`

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
mifi and aduh95 committed Sep 30, 2021
1 parent 4ff584c commit 1ae19a2
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 19 deletions.
4 changes: 4 additions & 0 deletions packages/@uppy/companion/src/companion.js
Expand Up @@ -249,4 +249,8 @@ const validateConfig = (companionOptions) => {
}
})
}

if (companionOptions.uploadUrls == null || companionOptions.uploadUrls.length === 0) {
logger.warn('Running without uploadUrls specified is a security risk if running in production', 'startup.uploadUrls')
}
}
2 changes: 1 addition & 1 deletion packages/@uppy/companion/src/server/Uploader.js
Expand Up @@ -201,7 +201,7 @@ class Uploader {
return false
}

const validatorOpts = { require_protocol: true, require_tld: !options.companionOptions.debug }
const validatorOpts = { require_protocol: true, require_tld: false }
return [options.endpoint, options.uploadUrl].every((url) => {
if (url && !validator.isURL(url, validatorOpts)) {
this._errRespMessage = 'invalid destination url'
Expand Down
30 changes: 29 additions & 1 deletion packages/@uppy/companion/test/__tests__/uploader.js
Expand Up @@ -7,10 +7,38 @@ const Uploader = require('../../src/server/Uploader')
const socketClient = require('../mocksocket')
const standalone = require('../../src/standalone')

const { companionOptions } = standalone()

describe('uploader with tus protocol', () => {
test('uploader respects uploadUrls', async () => {
const opts = {
endpoint: 'http://localhost/files',
companionOptions: { ...companionOptions, uploadUrls: [/^http:\/\/url.myendpoint.com\//] },
}

expect(new Uploader(opts).hasError()).toBe(true)
})

test('uploader respects uploadUrls, valid', async () => {
const opts = {
endpoint: 'http://url.myendpoint.com/files',
companionOptions: { ...companionOptions, uploadUrls: [/^http:\/\/url.myendpoint.com\//] },
}

expect(new Uploader(opts).hasError()).toBe(false)
})

test('uploader respects uploadUrls, localhost', async () => {
const opts = {
endpoint: 'http://localhost:1337/',
companionOptions: { ...companionOptions, uploadUrls: [/^http:\/\/localhost:1337\//] },
}

expect(new Uploader(opts).hasError()).toBe(false)
})

test('upload functions with tus protocol', () => {
const fileContent = Buffer.from('Some file content')
const { companionOptions } = standalone()
const opts = {
companionOptions,
endpoint: 'http://url.myendpoint.com/files',
Expand Down
34 changes: 17 additions & 17 deletions website/src/docs/companion.md
Expand Up @@ -236,7 +236,7 @@ export COMPANION_DOMAINS="sub1.domain.com,sub2.domain.com,sub3.domain.com"
export COMPANION_SELF_ENDPOINT="THIS SHOULD BE SAME AS YOUR DOMAIN + PATH"

# comma-separated URLs
# corresponds to the uploadUrls option
# corresponds to the uploadUrls option (comma-separated)
export COMPANION_UPLOAD_URLS="http://tusd.tusdemo.net/files/,https://tusd.tusdemo.net/files/"
```

Expand Down Expand Up @@ -285,38 +285,38 @@ const options = {
filePath: 'path/to/download/folder',
sendSelfEndpoint: 'localhost:3020',
secret: 'mysecret',
uploadUrls: ['https://myuploadurl.com', 'http://myuploadurl2.com'],
uploadUrls: ['https://myuploadurl.com', /^http:\/\/myuploadurl2.com\//],
debug: true,
metrics: false,
}
```

1. **filePath(required)** - Full path to the directory to which provider files would be downloaded temporarily.

2. **redisUrl(optional)** - URL to running Redis server. If this is set, the state of uploads would be stored temporarily. This helps for resumed uploads after a browser crash from the client. The stored upload would be sent back to the client on reconnection.
2. **secret(recommended)** - A secret string which Companion uses to generate authorization tokens.

3. **redisOptions(optional)** - An object of [options supported by redis client](https://www.npmjs.com/package/redis#options-object-properties). This option can be used in place of `redisUrl`.
3. **uploadUrls(recommended)** - An allowlist (array) of strings (exact URLs) or regular expressions. If specified, Companion will only accept uploads to these URLs. This is needed to make sure a Companion instance is only allowed to upload to your servers. **Omitting this leaves your system open to potential [SSRF](https://en.wikipedia.org/wiki/Server-side_request_forgery) attacks, and may throw an error in future `@uppy/companion` releases.**

4. **redisPubSubScope(optional)** - Use a scope for the companion events at the Redis server. Setting this option will prefix all events with the name provided and a colon.
4. **redisUrl(optional)** - URL to running Redis server. If this is set, the state of uploads would be stored temporarily. This helps for resumed uploads after a browser crash from the client. The stored upload would be sent back to the client on reconnection.

5. **providerOptions(optional)** - An object containing credentials (`key` and `secret`) for each provider you would like to enable. Please see [the list of supported providers](#Supported-providers).
5. **redisOptions(optional)** - An object of [options supported by redis client](https://www.npmjs.com/package/redis#options-object-properties). This option can be used in place of `redisUrl`.

6. **server(optional)** - An object with details, mainly used to carry out oauth authentication from any of the enabled providers above. Though it is optional, it is required if you would be enabling any of the supported providers. The following are the server options you may set:
6. **redisPubSubScope(optional)** - Use a scope for the companion events at the Redis server. Setting this option will prefix all events with the name provided and a colon.

- protocol - `http | https`
- host(required) - your server host (e.g localhost:3020, mydomain.com)
- path - the server path to where the Uppy app is sitting (e.g if Companion is at `mydomain.com/companion`, then the path would be `/companion`).
- oauthDomain - if you have multiple instances of Companion with different (and perhaps dynamic) subdomains, you can set a single fixed domain (e.g `sub1.mydomain.com`) to handle your oauth authentication for you. This would then redirect back to the correct instance with the required credentials on completion. This way you only need to configure a single callback URL for OAuth providers.
- validHosts - if you are setting an `oauthDomain`, you need to set a list of valid hosts, so the oauth handler can validate the host of the Uppy instance requesting the authentication. This is basically a list of valid domains running your Companion instances. The list may also contain regex patterns. e.g `['sub2.mydomain.com', 'sub3.mydomain.com', '(\\w+).mydomain.com']`
- implicitPath - if the URL path to your Companion server is set in your NGINX server (or any other Http server) instead of your express app, then you need to set this path as `implicitPath`. So if your Companion URL is `mydomain.com/mypath/companion`. Where the path `/mypath` is defined in your NGINX server, while `/companion` is set in your express app. Then you need to set the option `implicitPath` to `/mypath`, and set the `path` option to `/companion`.
7. **server(optional)** - An object with details, mainly used to carry out oauth authentication from any of the enabled providers above. Though it is optional, it is required if you would be enabling any of the supported providers. The following are the server options you may set:

7. **sendSelfEndpoint(optional)** - This is basically the same as the `server.host + server.path` attributes. The major reason for this attribute is that, when set, it adds the value as the `i-am` header of every request response.
- `protocol` - `http | https`
- `host` (required) - your server host (e.g localhost:3020, mydomain.com)
- `path` - the server path to where the Uppy app is sitting (e.g if Companion is at `mydomain.com/companion`, then the path would be `/companion`).
- `oauthDomain` - if you have multiple instances of Companion with different (and perhaps dynamic) subdomains, you can set a single fixed domain (e.g `sub1.mydomain.com`) to handle your oauth authentication for you. This would then redirect back to the correct instance with the required credentials on completion. This way you only need to configure a single callback URL for OAuth providers.
- `validHosts` - if you are setting an `oauthDomain`, you need to set a list of valid hosts, so the oauth handler can validate the host of the Uppy instance requesting the authentication. This is basically a list of valid domains running your Companion instances. The list may also contain regex patterns. e.g `['sub2.mydomain.com', 'sub3.mydomain.com', '(\\w+).mydomain.com']`
- `implicitPath` - if the URL path to your Companion server is set in your NGINX server (or any other Http server) instead of your express app, then you need to set this path as `implicitPath`. So if your Companion URL is `mydomain.com/mypath/companion`. Where the path `/mypath` is defined in your NGINX server, while `/companion` is set in your express app. Then you need to set the option `implicitPath` to `/mypath`, and set the `path` option to `/companion`.

8. **customProviders(optional)** - This option enables you to add custom providers along with the already supported providers. See [Adding Custom Providers](#Adding-custom-providers) for more information.
8. **sendSelfEndpoint(optional)** - This is basically the same as the `server.host + server.path` attributes. The major reason for this attribute is that, when set, it adds the value as the `i-am` header of every request response.

9. **uploadUrls(optional)** - An array of URLs (full paths). If specified, Companion will only accept uploads to these URLs (useful when you want to make sure a Companion instance is only allowed to upload to your servers, for example).
9. **providerOptions(optional)** - An object containing credentials (`key` and `secret`) for each provider you would like to enable. Please see [the list of supported providers](#Supported-providers).

10. **secret(required)** - A secret string which Companion uses to generate authorization tokens.
10. **customProviders(optional)** - This option enables you to add custom providers along with the already supported providers. See [Adding Custom Providers](#Adding-custom-providers) for more information.

11. **debug(optional)** - A boolean flag to tell Companion whether or not to log useful debug information while running.

Expand Down

0 comments on commit 1ae19a2

Please sign in to comment.