1818// - With allowlist: Only URLs matching a prefix in remoteModuleAllowlist are allowed
1919
2020describe ( "Remote Module Security" , function ( ) {
21-
21+
22+ // Hermetic by design: never touch live external hosts (esm.sh, TEST-NET
23+ // 192.0.2.1, example.com). HTTP module fetches are SYNCHRONOUS on the JS
24+ // thread (HMRSupport.mm: NSURLConnection sendSynchronousRequest), so a slow
25+ // or transitively-fanning remote import (e.g. esm.sh pulling a graph of
26+ // sub-modules) blocks the event loop and, accumulated, runs the whole suite
27+ // past its timeout on CI -> false "hang". A closed local port fails fast
28+ // with a connection-refused *network* error, which is exactly what every
29+ // debug-mode spec below asserts: the security gate lets the request through
30+ // (so it's NOT a security error) and the fetch then fails on the network.
31+ var DEAD_HTTP = "http://127.0.0.1:59999" ;
32+ var DEAD_HTTPS = "https://127.0.0.1:59999" ;
33+
2234 describe ( "Debug Mode Behavior" , function ( ) {
2335 // In debug mode (test environment), all remote modules should be allowed
2436 // regardless of security configuration
2537
2638 it ( "should allow HTTP module imports in debug mode" , function ( done ) {
27- // This test uses a known unreachable IP to trigger the HTTP loading path
28- // In debug mode, the security check passes and we get a network error
29- // (not a security error)
30- import ( "http://192.0.2.1:5173 /test-module.js") . then ( function ( module ) {
39+ // Uses a closed local port to trigger the HTTP loading path and fail
40+ // fast. In debug mode the security check passes and we get a network
41+ // error (connection refused), NOT a security error.
42+ import ( DEAD_HTTP + " /test-module.js") . then ( function ( module ) {
3143 // If we somehow succeed, that's fine too
3244 expect ( module ) . toBeDefined ( ) ;
3345 done ( ) ;
@@ -42,8 +54,8 @@ describe("Remote Module Security", function() {
4254 } ) ;
4355
4456 it ( "should allow HTTPS module imports in debug mode" , function ( done ) {
45- // Test HTTPS URL - should be allowed in debug mode
46- import ( "https://192.0.2.1:5173 /test-module.js") . then ( function ( module ) {
57+ // Test HTTPS URL - should be allowed in debug mode (closed local port -> fast network error)
58+ import ( DEAD_HTTPS + " /test-module.js") . then ( function ( module ) {
4759 expect ( module ) . toBeDefined ( ) ;
4860 done ( ) ;
4961 } ) . catch ( function ( error ) {
@@ -96,9 +108,9 @@ describe("Remote Module Security", function() {
96108 // code paths for HTTP loading are exercised correctly
97109
98110 it ( "should handle allowlisted URL prefixes" , function ( done ) {
99- // esm.sh is in our test allowlist
100- // In debug mode this is allowed anyway, but tests the path
101- import ( "https://esm.sh /test-module") . then ( function ( module ) {
111+ // In debug mode the allowlist is not enforced; this just exercises the
112+ // remote-load path and confirms we get a network (not security) error.
113+ import ( DEAD_HTTPS + " /test-module") . then ( function ( module ) {
102114 expect ( module ) . toBeDefined ( ) ;
103115 done ( ) ;
104116 } ) . catch ( function ( error ) {
@@ -111,7 +123,7 @@ describe("Remote Module Security", function() {
111123
112124 it ( "should handle non-allowlisted URLs in debug mode" , function ( done ) {
113125 // This URL is NOT in the allowlist, but debug mode allows it anyway
114- import ( "https:// not-in-allowlist.example.com /module.js") . then ( function ( module ) {
126+ import ( DEAD_HTTPS + "/ not-allowlisted /module.js") . then ( function ( module ) {
115127 expect ( module ) . toBeDefined ( ) ;
116128 done ( ) ;
117129 } ) . catch ( function ( error ) {
@@ -132,7 +144,7 @@ describe("Remote Module Security", function() {
132144 it ( "should allow importing from allowlisted static HTTP URLs" , function ( done ) {
133145 // This test verifies the ResolveModuleCallback security check path
134146 // is exercised for static imports
135- const testUrl = "https://esm.sh /@test/module";
147+ const testUrl = DEAD_HTTPS + " /@test/module";
136148
137149 import ( testUrl ) . then ( function ( module ) {
138150 expect ( module ) . toBeDefined ( ) ;
@@ -152,7 +164,7 @@ describe("Remote Module Security", function() {
152164 // handling code doesn't crash
153165
154166 it ( "should provide clear errors for failed HTTP imports" , function ( done ) {
155- import ( "http://invalid-url-test:9999 /module.js") . then ( function ( module ) {
167+ import ( DEAD_HTTP + " /module.js") . then ( function ( module ) {
156168 done ( ) ;
157169 } ) . catch ( function ( error ) {
158170 expect ( error ) . toBeDefined ( ) ;
@@ -188,7 +200,7 @@ describe("Remote Module Security", function() {
188200
189201 it ( "should handle URLs with special characters" , function ( done ) {
190202 // URLs with encoded characters should be handled properly
191- import ( "https://example.com /module%20with%20spaces.js") . then ( function ( module ) {
203+ import ( DEAD_HTTPS + " /module%20with%20spaces.js") . then ( function ( module ) {
192204 done ( ) ;
193205 } ) . catch ( function ( error ) {
194206 // Error handling should not crash
@@ -198,7 +210,7 @@ describe("Remote Module Security", function() {
198210 } ) ;
199211
200212 it ( "should handle URLs with query parameters" , function ( done ) {
201- import ( "https://esm.sh /lodash?target=es2020") . then ( function ( module ) {
213+ import ( DEAD_HTTPS + " /lodash?target=es2020") . then ( function ( module ) {
202214 done ( ) ;
203215 } ) . catch ( function ( error ) {
204216 // Query params should be preserved in URL handling
@@ -208,7 +220,7 @@ describe("Remote Module Security", function() {
208220 } ) ;
209221
210222 it ( "should handle URLs with fragments" , function ( done ) {
211- import ( "https://esm.sh /module#section") . then ( function ( module ) {
223+ import ( DEAD_HTTPS + " /module#section") . then ( function ( module ) {
212224 done ( ) ;
213225 } ) . catch ( function ( error ) {
214226 expect ( error ) . toBeDefined ( ) ;
0 commit comments