@@ -2,6 +2,7 @@ import { generateHS512JWK, SignedToken, signJWT, verifyJWT } from '@internal/aut
22import axios from 'axios'
33import dotenv from 'dotenv'
44import { FastifyInstance } from 'fastify'
5+ import { randomUUID } from 'node:crypto'
56import fs from 'fs/promises'
67import path from 'path'
78import app from '../app'
@@ -194,4 +195,53 @@ describe('image rendering routes', () => {
194195 const body = response . json < { error : string } > ( )
195196 expect ( body . error ) . toBe ( 'InvalidSignature' )
196197 } )
198+
199+ it ( 'will reject double-encoded signed render paths' , async ( ) => {
200+ const objectName = `authenticated/render-double-${ randomUUID ( ) } -일이삼.png`
201+ const encodedObjectName = objectName
202+ . split ( '/' )
203+ . map ( ( segment ) => encodeURIComponent ( segment ) )
204+ . join ( '/' )
205+
206+ const uploadResponse = await appInstance . inject ( {
207+ method : 'POST' ,
208+ url : `/object/bucket2/${ encodedObjectName } ` ,
209+ payload : Buffer . from ( 'render double-encoded test' ) ,
210+ headers : {
211+ authorization : `Bearer ${ process . env . SERVICE_KEY } ` ,
212+ 'content-type' : 'image/png' ,
213+ 'x-upsert' : 'true' ,
214+ } ,
215+ } )
216+ expect ( uploadResponse . statusCode ) . toBe ( 200 )
217+
218+ const signURLResponse = await appInstance . inject ( {
219+ method : 'POST' ,
220+ url : `/object/sign/bucket2/${ encodedObjectName } ` ,
221+ payload : {
222+ expiresIn : 60000 ,
223+ transform : {
224+ width : 100 ,
225+ height : 100 ,
226+ resize : 'contain' ,
227+ } ,
228+ } ,
229+ headers : {
230+ authorization : `Bearer ${ process . env . SERVICE_KEY } ` ,
231+ } ,
232+ } )
233+ expect ( signURLResponse . statusCode ) . toBe ( 200 )
234+
235+ const signedURL = signURLResponse . json < { signedURL : string } > ( ) . signedURL
236+ const signedURLParsed = new URL ( signedURL , 'http://localhost' )
237+ const doubleEncodedPath = signedURLParsed . pathname . replaceAll ( '%' , '%25' )
238+ const doubleEncodedResponse = await appInstance . inject ( {
239+ method : 'GET' ,
240+ url : `${ doubleEncodedPath } ${ signedURLParsed . search } ` ,
241+ } )
242+
243+ expect ( doubleEncodedResponse . statusCode ) . toBe ( 400 )
244+ const body = doubleEncodedResponse . json < { error : string } > ( )
245+ expect ( body . error ) . toBe ( 'InvalidSignature' )
246+ } )
197247} )
0 commit comments