@@ -221,9 +221,32 @@ function is_ip_internal(ip) {
221221 }
222222 if ( parsed . kind ( ) === "ipv6" ) {
223223 const range = parsed . range ( ) ;
224- return ( range === "loopback" ||
225- range === "linkLocal" ||
226- range === "uniqueLocal" ) ;
224+ if ( range !== "unicast" ) {
225+ // Check for IPv4-mapped or IPv4-compatible addresses
226+ if ( range === "ipv4Mapped" || range === "rfc6145" ) {
227+ try {
228+ // @ts -ignore
229+ const ipv4 = parsed . toIPv4Address ( ) ;
230+ return is_ip_internal ( ipv4 . toString ( ) ) ;
231+ }
232+ catch {
233+ return true ;
234+ }
235+ }
236+ return true ;
237+ }
238+ // Additional manual blocks for IPv6
239+ const extraBadRanges = [
240+ ipaddr . parseCIDR ( "64:ff9b:1::/48" ) ,
241+ ipaddr . parseCIDR ( "5f00::/8" ) ,
242+ ipaddr . parseCIDR ( "3fff::/20" ) ,
243+ ipaddr . parseCIDR ( "fec0::/10" ) ,
244+ ] ;
245+ for ( const [ range , bits ] of extraBadRanges ) {
246+ // @ts -ignore
247+ if ( parsed . match ( range , bits ) )
248+ return true ;
249+ }
227250 }
228251 return false ;
229252}
@@ -409,7 +432,7 @@ async function is_url_safe(url) {
409432 if ( ! is_proto_safe ( schema ) )
410433 return false ;
411434 const parsed = new URL ( u ) ;
412- const hostname = parsed . hostname ;
435+ const hostname = parsed . hostname . replace ( / ^ \[ | \] $ / g , "" ) ;
413436 // IPv4 validation
414437 if ( / ^ \d { 1 , 3 } ( \. \d { 1 , 3 } ) { 3 } $ / . test ( hostname ) ) {
415438 try {
@@ -457,7 +480,7 @@ async function is_url_safe_debug(url) { try {
457480 }
458481 console . log ( "STEP 7 proto safe" ) ;
459482 const parsed = new URL ( u ) ;
460- const hostname = parsed . hostname ;
483+ const hostname = parsed . hostname . replace ( / ^ \[ | \] $ / g , "" ) ;
461484 console . log ( "STEP 8 hostname:" , hostname ) ;
462485 if ( / ^ \d { 1 , 3 } ( \. \d { 1 , 3 } ) { 3 } $ / . test ( hostname ) ) {
463486 try {
0 commit comments