Skip to content

Commit 6a60ee5

Browse files
authored
@uppy/companion: fix crash on missing filename (#6010)
Better to reject early than generating a filename when missing which could break assumptions we don't foresee.
1 parent 9d2c7a9 commit 6a60ee5

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

.changeset/forty-buses-float.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@uppy/companion": patch
3+
---
4+
5+
Reject request early instead of crashing on missing `filename` for /s3/multipart and /s3/params endpoints

packages/@uppy/companion/src/server/controllers/s3.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ export default function s3(config) {
5858

5959
const { metadata = {}, filename } = req.query
6060

61+
// Validate filename is provided and non-empty
62+
if (typeof filename !== 'string' || filename === '') {
63+
res.status(400).json({
64+
error:
65+
's3: the "filename" query parameter is required and must be a non-empty string',
66+
})
67+
return
68+
}
69+
6170
const truncatedFilename = truncateFilename(
6271
filename,
6372
req.companion.options.maxFilenameLength,
@@ -126,6 +135,15 @@ export default function s3(config) {
126135

127136
const { type, metadata = {}, filename } = req.body
128137

138+
// Validate filename is provided and non-empty
139+
if (typeof filename !== 'string' || filename === '') {
140+
res.status(400).json({
141+
error:
142+
's3: the "filename" field is required and must be a non-empty string',
143+
})
144+
return
145+
}
146+
129147
const truncatedFilename = truncateFilename(
130148
filename,
131149
req.companion.options.maxFilenameLength,

packages/@uppy/companion/test/companion.test.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,3 +304,118 @@ describe('respects allowLocalUrls, valid hostname that resolves to localhost', (
304304
expect(res.body).toEqual({ message: 'failed to fetch URL' })
305305
})
306306
})
307+
308+
describe('S3 controller', () => {
309+
test('createMultipartUpload rejects missing filename', async () => {
310+
const server = await getServer({
311+
COMPANION_AWS_KEY: 'test_key',
312+
COMPANION_AWS_SECRET: 'test_secret',
313+
COMPANION_AWS_BUCKET: 'test-bucket',
314+
COMPANION_AWS_REGION: 'us-east-1',
315+
})
316+
317+
return request(server)
318+
.post('/s3/multipart')
319+
.send({
320+
type: 'image/png',
321+
metadata: {},
322+
// filename is intentionally missing
323+
})
324+
.expect(400)
325+
.then((res) =>
326+
expect(res.body.error).toBe(
327+
's3: the "filename" field is required and must be a non-empty string',
328+
),
329+
)
330+
})
331+
332+
test('createMultipartUpload rejects empty filename', async () => {
333+
const server = await getServer({
334+
COMPANION_AWS_KEY: 'test_key',
335+
COMPANION_AWS_SECRET: 'test_secret',
336+
COMPANION_AWS_BUCKET: 'test-bucket',
337+
COMPANION_AWS_REGION: 'us-east-1',
338+
})
339+
340+
return request(server)
341+
.post('/s3/multipart')
342+
.send({
343+
type: 'image/png',
344+
metadata: {},
345+
filename: '',
346+
})
347+
.expect(400)
348+
.then((res) =>
349+
expect(res.body.error).toBe(
350+
's3: the "filename" field is required and must be a non-empty string',
351+
),
352+
)
353+
})
354+
355+
test('createMultipartUpload rejects non-string filename', async () => {
356+
const server = await getServer({
357+
COMPANION_AWS_KEY: 'test_key',
358+
COMPANION_AWS_SECRET: 'test_secret',
359+
COMPANION_AWS_BUCKET: 'test-bucket',
360+
COMPANION_AWS_REGION: 'us-east-1',
361+
})
362+
363+
return request(server)
364+
.post('/s3/multipart')
365+
.send({
366+
type: 'image/png',
367+
metadata: {},
368+
filename: 12345,
369+
})
370+
.expect(400)
371+
.then((res) =>
372+
expect(res.body.error).toBe(
373+
's3: the "filename" field is required and must be a non-empty string',
374+
),
375+
)
376+
})
377+
378+
test('getUploadParameters rejects missing filename', async () => {
379+
const server = await getServer({
380+
COMPANION_AWS_KEY: 'test_key',
381+
COMPANION_AWS_SECRET: 'test_secret',
382+
COMPANION_AWS_BUCKET: 'test-bucket',
383+
COMPANION_AWS_REGION: 'us-east-1',
384+
})
385+
386+
return request(server)
387+
.get('/s3/params')
388+
.query({
389+
type: 'image/png',
390+
// filename is intentionally missing
391+
})
392+
.expect(400)
393+
.then((res) =>
394+
expect(res.body.error).toBe(
395+
's3: the "filename" query parameter is required and must be a non-empty string',
396+
),
397+
)
398+
})
399+
400+
test('getUploadParameters rejects empty filename', async () => {
401+
const server = await getServer({
402+
COMPANION_AWS_KEY: 'test_key',
403+
COMPANION_AWS_SECRET: 'test_secret',
404+
COMPANION_AWS_BUCKET: 'test-bucket',
405+
COMPANION_AWS_REGION: 'us-east-1',
406+
})
407+
408+
return request(server)
409+
.get('/s3/params')
410+
.query({
411+
type: 'image/png',
412+
filename: '',
413+
})
414+
.expect(400)
415+
.then((res) =>
416+
expect(res.body.error).toBe(
417+
's3: the "filename" query parameter is required and must be a non-empty string',
418+
),
419+
)
420+
})
421+
})

0 commit comments

Comments
 (0)