@@ -246,4 +246,135 @@ describe("unit tests", async () => {
246246 const response = await worker . fetch ( request , env , ctx ) ;
247247 expect ( await response . text ( ) ) . toEqual ( "hello from user worker" ) ;
248248 } ) ;
249+
250+ it ( "blocks /_next/image requests with remote URLs when not fetched as image" , async ( ) => {
251+ const request = new Request (
252+ "https://example.com/_next/image?url=https://evil.com/ssrf"
253+ ) ;
254+ const ctx = createExecutionContext ( ) ;
255+
256+ const env = {
257+ CONFIG : {
258+ has_user_worker : true ,
259+ invoke_user_worker_ahead_of_assets : true ,
260+ } ,
261+ USER_WORKER : {
262+ async fetch ( _ : Request ) : Promise < Response > {
263+ return new Response ( "<!DOCTYPE html><html></html>" , {
264+ headers : { "content-type" : "text/html" } ,
265+ } ) ;
266+ } ,
267+ } ,
268+ } as Env ;
269+
270+ const response = await worker . fetch ( request , env , ctx ) ;
271+ expect ( response . status ) . toBe ( 403 ) ;
272+ expect ( await response . text ( ) ) . toBe ( "Blocked" ) ;
273+ } ) ;
274+
275+ it ( "allows /_next/image requests with remote URLs when fetched as image" , async ( ) => {
276+ const request = new Request (
277+ "https://example.com/_next/image?url=https://example.com/image.jpg" ,
278+ {
279+ headers : { "sec-fetch-dest" : "image" } ,
280+ }
281+ ) ;
282+ const ctx = createExecutionContext ( ) ;
283+
284+ const env = {
285+ CONFIG : {
286+ has_user_worker : true ,
287+ invoke_user_worker_ahead_of_assets : true ,
288+ } ,
289+ USER_WORKER : {
290+ async fetch ( _ : Request ) : Promise < Response > {
291+ return new Response ( "fake image data" , {
292+ headers : { "content-type" : "image/jpeg" } ,
293+ } ) ;
294+ } ,
295+ } ,
296+ } as Env ;
297+
298+ const response = await worker . fetch ( request , env , ctx ) ;
299+ expect ( response . status ) . toBe ( 200 ) ;
300+ expect ( await response . text ( ) ) . toBe ( "fake image data" ) ;
301+ } ) ;
302+
303+ it ( "allows /_next/image with remote URL and image header regardless of response content" , async ( ) => {
304+ const request = new Request (
305+ "https://example.com/_next/image?url=https://example.com/image.jpg" ,
306+ {
307+ headers : { "sec-fetch-dest" : "image" } ,
308+ }
309+ ) ;
310+ const ctx = createExecutionContext ( ) ;
311+
312+ const env = {
313+ CONFIG : {
314+ has_user_worker : true ,
315+ invoke_user_worker_ahead_of_assets : true ,
316+ } ,
317+ USER_WORKER : {
318+ async fetch ( _ : Request ) : Promise < Response > {
319+ return new Response ( "<!DOCTYPE html><html></html>" , {
320+ headers : { "content-type" : "text/html" } ,
321+ } ) ;
322+ } ,
323+ } ,
324+ } as Env ;
325+
326+ const response = await worker . fetch ( request , env , ctx ) ;
327+ expect ( response . status ) . toBe ( 200 ) ;
328+ expect ( await response . text ( ) ) . toBe ( "<!DOCTYPE html><html></html>" ) ;
329+ } ) ;
330+
331+ it ( "allows /_next/image requests with local URLs" , async ( ) => {
332+ const request = new Request (
333+ "https://example.com/_next/image?url=/local/image.jpg"
334+ ) ;
335+ const ctx = createExecutionContext ( ) ;
336+
337+ const env = {
338+ CONFIG : {
339+ has_user_worker : true ,
340+ invoke_user_worker_ahead_of_assets : true ,
341+ } ,
342+ USER_WORKER : {
343+ async fetch ( _ : Request ) : Promise < Response > {
344+ return new Response ( "local image data" , {
345+ headers : { "content-type" : "image/jpeg" } ,
346+ } ) ;
347+ } ,
348+ } ,
349+ } as Env ;
350+
351+ const response = await worker . fetch ( request , env , ctx ) ;
352+ expect ( response . status ) . toBe ( 200 ) ;
353+ expect ( await response . text ( ) ) . toBe ( "local image data" ) ;
354+ } ) ;
355+
356+ it ( "allows /_next/image requests with 304 status" , async ( ) => {
357+ const request = new Request (
358+ "https://example.com/_next/image?url=https://example.com/image.jpg"
359+ ) ;
360+ const ctx = createExecutionContext ( ) ;
361+
362+ const env = {
363+ CONFIG : {
364+ has_user_worker : true ,
365+ invoke_user_worker_ahead_of_assets : true ,
366+ } ,
367+ USER_WORKER : {
368+ async fetch ( _ : Request ) : Promise < Response > {
369+ return new Response ( null , {
370+ status : 304 ,
371+ headers : { "content-type" : "text/html" } ,
372+ } ) ;
373+ } ,
374+ } ,
375+ } as Env ;
376+
377+ const response = await worker . fetch ( request , env , ctx ) ;
378+ expect ( response . status ) . toBe ( 304 ) ;
379+ } ) ;
249380} ) ;
0 commit comments