@@ -352,6 +352,124 @@ describe('testing realpath', async () => {
352352 )
353353 } )
354354
355+ await it ( 'can resolve a symlink to a non-existing path' , async ( ) => {
356+ await withFixtures (
357+ {
358+ sandbox : { } ,
359+ execroot : { } ,
360+ otherroot : { file : 'contents' } ,
361+ } ,
362+ async ( fixturesDir ) => {
363+ fixturesDir = fs . realpathSync ( fixturesDir )
364+
365+ const revertPatches = patcher ( [
366+ path . join ( fixturesDir , 'sandbox' ) ,
367+ ] )
368+
369+ let brokenLinkPath = path . join (
370+ fixturesDir ,
371+ 'sandbox' ,
372+ 'broken-link'
373+ )
374+ fs . symlinkSync (
375+ path . join ( fixturesDir , 'doesnt-exist' ) ,
376+ brokenLinkPath
377+ )
378+
379+ assert . throws (
380+ ( ) => fs . realpathSync ( brokenLinkPath ) ,
381+ 'should throw because link is broken'
382+ )
383+
384+ let thrown
385+ try {
386+ await util . promisify ( fs . realpath . native ) ( brokenLinkPath )
387+ } catch ( e ) {
388+ thrown = e
389+ } finally {
390+ if ( ! thrown )
391+ assert . fail ( 'should throw if empty string is passed' )
392+ }
393+
394+ try {
395+ await fs . promises . realpath ( brokenLinkPath )
396+ } catch ( e ) {
397+ thrown = e
398+ } finally {
399+ if ( ! thrown )
400+ assert . fail ( 'should throw if empty string is passed' )
401+ }
402+
403+ revertPatches ( )
404+ }
405+ )
406+ } )
407+
408+ await it ( 'can resolve a symlink to a non-existing path after escaping' , async ( ) => {
409+ await withFixtures (
410+ {
411+ sandbox : { } ,
412+ execroot : { } ,
413+ otherroot : { file : 'contents' } ,
414+ } ,
415+ async ( fixturesDir ) => {
416+ fixturesDir = fs . realpathSync ( fixturesDir )
417+
418+ const nonSandboxedBrokenLink = path . join (
419+ fixturesDir ,
420+ 'broken-link'
421+ )
422+
423+ fs . symlinkSync (
424+ path . join ( fixturesDir , 'doesnt-exist' ) ,
425+ nonSandboxedBrokenLink
426+ )
427+
428+ const revertPatches = patcher ( [
429+ path . join ( fixturesDir , 'sandbox' ) ,
430+ ] )
431+
432+ let sandboxedLinkToBrokenLink = path . join (
433+ fixturesDir ,
434+ 'sandbox' ,
435+ 'indirect-link'
436+ )
437+ fs . symlinkSync (
438+ nonSandboxedBrokenLink ,
439+ sandboxedLinkToBrokenLink
440+ )
441+
442+ assert . throws (
443+ ( ) => fs . realpathSync ( sandboxedLinkToBrokenLink ) ,
444+ 'should throw because link is broken'
445+ )
446+
447+ let thrown
448+ try {
449+ await util . promisify ( fs . realpath . native ) (
450+ sandboxedLinkToBrokenLink
451+ )
452+ } catch ( e ) {
453+ thrown = e
454+ } finally {
455+ if ( ! thrown )
456+ assert . fail ( 'should throw if empty string is passed' )
457+ }
458+
459+ try {
460+ await fs . promises . realpath ( sandboxedLinkToBrokenLink )
461+ } catch ( e ) {
462+ thrown = e
463+ } finally {
464+ if ( ! thrown )
465+ assert . fail ( 'should throw if empty string is passed' )
466+ }
467+
468+ revertPatches ( )
469+ }
470+ )
471+ } )
472+
355473 await it ( 'can resolve symlink to a symlink in the sandbox if there is no corresponding location in the sandbox but is a realpath outside' , async ( ) => {
356474 await withFixtures (
357475 {
0 commit comments