diff --git a/examples/digitalocean-spaces/server.js b/examples/digitalocean-spaces/server.js index e799087ece..56f37d4a8b 100644 --- a/examples/digitalocean-spaces/server.js +++ b/examples/digitalocean-spaces/server.js @@ -2,6 +2,8 @@ const fs = require('node:fs') const path = require('node:path') const budo = require('budo') const router = require('router') +const crypto = require('node:crypto') + const companion = require('../../packages/@uppy/companion') /** @@ -32,7 +34,7 @@ const { app: companionApp } = companion.app({ s3: { // This is the crucial part; set an endpoint template for the service you want to use. endpoint: 'https://{region}.digitaloceanspaces.com', - getKey: (req, filename) => `uploads/${filename}`, + getKey: (req, filename) => `${crypto.randomUUID()}-${filename}`, key: process.env.COMPANION_AWS_KEY, secret: process.env.COMPANION_AWS_SECRET, diff --git a/packages/@uppy/companion/src/config/companion.js b/packages/@uppy/companion/src/config/companion.js index a1310455e3..7ceb01e935 100644 --- a/packages/@uppy/companion/src/config/companion.js +++ b/packages/@uppy/companion/src/config/companion.js @@ -2,6 +2,7 @@ const ms = require('ms') const fs = require('node:fs') const { isURL } = require('validator') const logger = require('../server/logger') +const { defaultGetKey } = require('../server/helpers/utils') const defaultOptions = { server: { @@ -13,7 +14,7 @@ const defaultOptions = { endpoint: 'https://{service}.{region}.amazonaws.com', conditions: [], useAccelerateEndpoint: false, - getKey: (req, filename) => filename, + getKey: defaultGetKey, expires: ms('5 minutes') / 1000, }, allowLocalUrls: false, diff --git a/packages/@uppy/companion/src/server/helpers/utils.js b/packages/@uppy/companion/src/server/helpers/utils.js index 750c9a04a2..572a9625f2 100644 --- a/packages/@uppy/companion/src/server/helpers/utils.js +++ b/packages/@uppy/companion/src/server/helpers/utils.js @@ -164,3 +164,5 @@ module.exports.requestStream = async (req, convertResponseToError) => { return { stream: resp } } + +module.exports.defaultGetKey = (req, filename) => `${crypto.randomUUID()}-${filename}` diff --git a/packages/@uppy/companion/src/standalone/helper.js b/packages/@uppy/companion/src/standalone/helper.js index 9076ecc047..d42940bb09 100644 --- a/packages/@uppy/companion/src/standalone/helper.js +++ b/packages/@uppy/companion/src/standalone/helper.js @@ -72,7 +72,7 @@ const getConfigFromEnv = () => { }, s3: { key: process.env.COMPANION_AWS_KEY, - getKey: (req, filename) => `${crypto.randomUUID()}-${filename}`, + getKey: utils.defaultGetKey, secret: getSecret('COMPANION_AWS_SECRET'), bucket: process.env.COMPANION_AWS_BUCKET, endpoint: process.env.COMPANION_AWS_ENDPOINT, diff --git a/website/src/docs/companion.md b/website/src/docs/companion.md index f2e6a5b63e..9a28fee239 100644 --- a/website/src/docs/companion.md +++ b/website/src/docs/companion.md @@ -326,7 +326,7 @@ const options = { }, }, s3: { - getKey: (req, filename, metadata) => filename, + getKey: (req, filename, metadata) => `${crypto.randomUUID()}-${filename}`, key: '***', secret: '***', bucket: 'bucket-name', @@ -453,24 +453,26 @@ Get the key name for a file. The key is the file path to which the file will be * `filename`, the original name of the uploaded file; * `metadata`, user-provided metadata for the file. See the [`@uppy/aws-s3`](https://uppy.io/docs/aws-s3/#metaFields) docs. The `@uppy/aws-s3-multipart` plugin unconditionally sends all metadata fields, so they all are available here. +If your bucket is public, you should include a cryptographically random token in the uploaded name for security (hence the default `crypto.randomUUID()`). + This function should return a string `key`. The `req` parameter can be used to upload to a user-specific folder in your bucket, for example: ```js app.use(authenticationMiddleware) app.use(companion.app({ s3: { - getKey: (req, filename, metadata) => `${req.user.id}/${filename}`, + getKey: (req, filename, metadata) => `${req.user.id}/${crypto.randomUUID()}-${filename}`, /* auth options */ }, })) ``` -The default implementation returns the `filename`, so all files will be uploaded to the root of the bucket as their original file name. +The default implementation uploads all files to the root of the bucket as their original file name, prefixed with a random UUID. ```js app.use(companion.app({ s3: { - getKey: (req, filename, metadata) => filename, + getKey: (req, filename, metadata) => `${crypto.randomUUID()}-${filename}`, }, })) ```