Skip to content

Commit 9fab01e

Browse files
committed
test(supabase): track #7262 permitting a whitespace-only path component
#7262 landed 515b951, narrowing safeUrlPath's empty-segment check from !segment.trim() to !segment — the fix this suite asked for after flagging the over-rejection. Rebased onto it. That changes behaviour under the Supabase storage key, so the suite is updated rather than left asserting the old error text: a/ /b -> a/%20/b (now permitted) a//b -> rejects: empty path segment (unchanged) The distinction is the point and both halves are now pinned. A component that is a single space is a legal, nameable key component; a genuinely empty one addresses a different object than the caller wrote. Collapsing them again in either direction is a silent correctness change — one makes a real key unreachable, the other retargets the request.
1 parent 319724f commit 9fab01e

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

apps/sim/tools/supabase/path_safety.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,32 @@ describe('colon handling inherited from safeUrlPath', () => {
222222
*/
223223
describe('empty segments in a storage key', () => {
224224
it.each(['/folder/x.png', 'folder//x.png', 'folder/x.png/'])('rejects %j', (value) => {
225-
expect(() => encodeStoragePath(value)).toThrow(/empty or whitespace-only path segment/)
225+
expect(() => encodeStoragePath(value)).toThrow(/empty path segment/)
226+
})
227+
228+
/**
229+
* A **whitespace-only** component is permitted, and that is not the same rule.
230+
*
231+
* `safeUrlPath` originally rejected `!segment.trim()`, which lumped `a/ /b` in
232+
* with `a//b`. #7262 narrowed it to `!segment` after this suite flagged the
233+
* over-rejection, and the distinction is exactly right for an object key: a
234+
* component that is a single space is a legal, nameable key component, while a
235+
* genuinely empty one addresses a *different* object than the caller wrote.
236+
*
237+
* Both halves are pinned here, because collapsing them again in either
238+
* direction is a silent correctness change — one direction makes a real key
239+
* unreachable, the other silently retargets the request.
240+
*/
241+
it.each(['a/ /b', 'a/ /b', 'folder/ /file.png'])(
242+
'permits the whitespace-only component in %j',
243+
(value) => {
244+
expect(decodeURIComponent(encodeStoragePath(value))).toBe(value)
245+
}
246+
)
247+
248+
it('keeps a whitespace-only component distinct from an empty one', () => {
249+
expect(encodeStoragePath('a/ /b')).toBe('a/%20/b')
250+
expect(() => encodeStoragePath('a//b')).toThrow(/empty path segment/)
226251
})
227252

228253
it('would otherwise have addressed a different object', () => {

apps/sim/tools/supabase/utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ export function encodeStorageSegment(segment: string, paramName = 'bucket'): str
4747
* normalizes its own trailing separator before joining `path` and `fileName`,
4848
* so the only way to produce an empty segment is a typo the caller wants to
4949
* hear about.
50+
*
51+
* A **whitespace-only** component is a different case and is permitted: `a/ /b`
52+
* encodes to `a/%20/b`. That is a legal, nameable object key component, whereas
53+
* `a//b` names something else entirely. `safeUrlPath` originally conflated the
54+
* two by testing `!segment.trim()`; #7262 narrowed it to `!segment` after this
55+
* suite flagged the over-rejection. Both halves are pinned in
56+
* `path_safety.test.ts`, because collapsing them again in either direction is a
57+
* silent correctness change — one makes a real key unreachable, the other
58+
* retargets the request.
5059
*/
5160
export function encodeStoragePath(path: string, paramName = 'path'): string {
5261
return safeUrlPath(path, paramName)

0 commit comments

Comments
 (0)