|
| 1 | +import { randomUUID } from 'node:crypto' |
1 | 2 | import { generateHS512JWK, SignedToken, signJWT, verifyJWT } from '@internal/auth' |
2 | 3 | import axios from 'axios' |
3 | 4 | import dotenv from 'dotenv' |
@@ -194,4 +195,53 @@ describe('image rendering routes', () => { |
194 | 195 | const body = response.json<{ error: string }>() |
195 | 196 | expect(body.error).toBe('InvalidSignature') |
196 | 197 | }) |
| 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 | + }) |
197 | 247 | }) |
0 commit comments