Skip to content

Commit 61b5939

Browse files
authored
fix(blob): error early when trying to use conflicting characters (#769)
* fix(blob): error early when trying to use conflicting characters Trying to upload files containing ?, # or // will always result in a weird behavior where the pathname you sent won't be exactly the one in the resulting url. Rather than relying on this I propose we just error early on and advise to either not use them or encode them. BREAKING CHANGE: Previously working characters will now throw an error. * changeset
1 parent 736b3f6 commit 61b5939

File tree

5 files changed

+61
-2
lines changed

5 files changed

+61
-2
lines changed

.changeset/selfish-owls-thank.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@vercel/blob': minor
3+
---
4+
5+
BREAKING CHANGE, we're no more accepting non-encoded versions of ?, # and // in pathnames. If you want to use such characters in your pathnames then you will need to encode them.

packages/blob/src/copy.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { MAXIMUM_PATHNAME_LENGTH, requestApi } from './api';
22
import type { CommonCreateBlobOptions } from './helpers';
3-
import { BlobError } from './helpers';
3+
import { BlobError, disallowedPathnameCharacters } from './helpers';
44

55
export type CopyCommandOptions = CommonCreateBlobOptions;
66

@@ -41,6 +41,14 @@ export async function copy(
4141
);
4242
}
4343

44+
for (const invalidCharacter of disallowedPathnameCharacters) {
45+
if (toPathname.includes(invalidCharacter)) {
46+
throw new BlobError(
47+
`pathname cannot contain "${invalidCharacter}", please encode it if needed`,
48+
);
49+
}
50+
}
51+
4452
const headers: Record<string, string> = {};
4553

4654
if (options.addRandomSuffix !== undefined) {

packages/blob/src/helpers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,5 @@ export function isPlainObject(value: unknown): boolean {
8181
!(Symbol.iterator in value)
8282
);
8383
}
84+
85+
export const disallowedPathnameCharacters = ['#', '?', '//'];

packages/blob/src/index.node.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,42 @@ describe('blob client', () => {
586586
);
587587
});
588588

589+
it('throws when pathname contains #', async () => {
590+
await expect(
591+
put('foo#bar.txt', 'Test Body', {
592+
access: 'public',
593+
}),
594+
).rejects.toThrow(
595+
new Error(
596+
'Vercel Blob: pathname cannot contain "#", please encode it if needed',
597+
),
598+
);
599+
});
600+
601+
it('throws when pathname contains ?', async () => {
602+
await expect(
603+
put('foo?bar.txt', 'Test Body', {
604+
access: 'public',
605+
}),
606+
).rejects.toThrow(
607+
new Error(
608+
'Vercel Blob: pathname cannot contain "?", please encode it if needed',
609+
),
610+
);
611+
});
612+
613+
it('throws when pathname contains //', async () => {
614+
await expect(
615+
put('foo//bar.txt', 'Test Body', {
616+
access: 'public',
617+
}),
618+
).rejects.toThrow(
619+
new Error(
620+
'Vercel Blob: pathname cannot contain "//", please encode it if needed',
621+
),
622+
);
623+
});
624+
589625
const table: [string, (signal: AbortSignal) => Promise<unknown>][] = [
590626
[
591627
'put',

packages/blob/src/put-helpers.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import type { Readable } from 'stream';
33
import type { ClientCommonCreateBlobOptions } from './client';
44
import type { CommonCreateBlobOptions } from './helpers';
5-
import { BlobError } from './helpers';
5+
import { BlobError, disallowedPathnameCharacters } from './helpers';
66
import { MAXIMUM_PATHNAME_LENGTH } from './api';
77

88
export const putOptionHeaderMap = {
@@ -92,6 +92,14 @@ export async function createPutOptions<
9292
);
9393
}
9494

95+
for (const invalidCharacter of disallowedPathnameCharacters) {
96+
if (pathname.includes(invalidCharacter)) {
97+
throw new BlobError(
98+
`pathname cannot contain "${invalidCharacter}", please encode it if needed`,
99+
);
100+
}
101+
}
102+
95103
if (!options) {
96104
throw new BlobError('missing options, see usage');
97105
}

0 commit comments

Comments
 (0)