Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make uploadUrls recommended #3182

Merged
merged 4 commits into from Sep 30, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 an uploadUrls whitelist is a security risk if running in production', 'startup.uploadUrls')
mifi marked this conversation as resolved.
Show resolved Hide resolved
}
}
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 whitelist', async () => {
mifi marked this conversation as resolved.
Show resolved Hide resolved
const opts = {
endpoint: 'http://localhost/files',
companionOptions: { ...companionOptions, uploadUrls: [/^http:\/\/url.myendpoint.com\//] },
}

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

test('uploader respects uploadUrls whitelist, valid', async () => {
mifi marked this conversation as resolved.
Show resolved Hide resolved
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 whitelist, localhost', async () => {
mifi marked this conversation as resolved.
Show resolved Hide resolved
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(required)** - A secret string which Companion uses to generate authorization tokens.
mifi marked this conversation as resolved.
Show resolved Hide resolved

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(required)** - A whitelist (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.** Example: []
mifi marked this conversation as resolved.
Show resolved Hide resolved

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