11// Flags: --experimental-network-imports --dns-result-order=ipv4first
22import * as common from '../common/index.mjs' ;
3- import { path , readKey } from '../common/fixtures.mjs' ;
4- import { pathToFileURL } from 'url ' ;
3+ import * as fixtures from '../common/fixtures.mjs' ;
4+ import tmpdir from '../common/tmpdir.js ' ;
55import assert from 'assert' ;
66import http from 'http' ;
77import os from 'os' ;
88import util from 'util' ;
9+ import { describe , it } from 'node:test' ;
910
1011if ( ! common . hasCrypto ) {
1112 common . skip ( 'missing crypto' ) ;
1213}
14+ tmpdir . refresh ( ) ;
1315
1416const https = ( await import ( 'https' ) ) . default ;
1517
@@ -18,8 +20,8 @@ const createHTTPServer = http.createServer;
1820// Needed to deal w/ test certs
1921process . env . NODE_TLS_REJECT_UNAUTHORIZED = '0' ;
2022const options = {
21- key : readKey ( 'agent1-key.pem' ) ,
22- cert : readKey ( 'agent1-cert.pem' )
23+ key : fixtures . readKey ( 'agent1-key.pem' ) ,
24+ cert : fixtures . readKey ( 'agent1-cert.pem' )
2325} ;
2426
2527const createHTTPSServer = https . createServer . bind ( null , options ) ;
@@ -136,72 +138,14 @@ for (const { protocol, createServer } of [
136138 url . href + 'bar/baz.js'
137139 ) ;
138140
139- const crossProtocolRedirect = new URL ( url . href ) ;
140- crossProtocolRedirect . searchParams . set ( 'redirect' , JSON . stringify ( {
141- status : 302 ,
142- location : 'data:text/javascript,'
143- } ) ) ;
144- await assert . rejects (
145- import ( crossProtocolRedirect . href ) ,
146- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
147- ) ;
148-
149- const deps = new URL ( url . href ) ;
150- deps . searchParams . set ( 'body' , `
151- export {data} from 'data:text/javascript,export let data = 1';
152- import * as http from ${ JSON . stringify ( url . href ) } ;
153- export {http};
154- ` ) ;
155- const depsNS = await import ( deps . href ) ;
156- assert . strict . deepStrictEqual ( Object . keys ( depsNS ) , [ 'data' , 'http' ] ) ;
157- assert . strict . equal ( depsNS . data , 1 ) ;
158- assert . strict . equal ( depsNS . http , ns ) ;
159-
160- const relativeDeps = new URL ( url . href ) ;
161- relativeDeps . searchParams . set ( 'body' , `
162- import * as http from "./";
163- export {http};
164- ` ) ;
165- const relativeDepsNS = await import ( relativeDeps . href ) ;
166- assert . strict . deepStrictEqual ( Object . keys ( relativeDepsNS ) , [ 'http' ] ) ;
167- assert . strict . equal ( relativeDepsNS . http , ns ) ;
168- const fileDep = new URL ( url . href ) ;
169- const { href } = pathToFileURL ( path ( '/es-modules/message.mjs' ) ) ;
170- fileDep . searchParams . set ( 'body' , `
171- import ${ JSON . stringify ( href ) } ;
172- export default 1;` ) ;
173- await assert . rejects (
174- import ( fileDep . href ) ,
175- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
176- ) ;
177-
178- const builtinDep = new URL ( url . href ) ;
179- builtinDep . searchParams . set ( 'body' , `
180- import 'node:fs';
181- export default 1;
182- ` ) ;
183- await assert . rejects (
184- import ( builtinDep . href ) ,
185- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
186- ) ;
187-
188- const unprefixedBuiltinDep = new URL ( url . href ) ;
189- unprefixedBuiltinDep . searchParams . set ( 'body' , `
190- import 'fs';
191- export default 1;
192- ` ) ;
193- await assert . rejects (
194- import ( unprefixedBuiltinDep . href ) ,
195- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
196- ) ;
197-
198141 const unsupportedMIME = new URL ( url . href ) ;
199142 unsupportedMIME . searchParams . set ( 'mime' , 'application/node' ) ;
200143 unsupportedMIME . searchParams . set ( 'body' , '' ) ;
201144 await assert . rejects (
202145 import ( unsupportedMIME . href ) ,
203146 { code : 'ERR_UNKNOWN_MODULE_FORMAT' }
204147 ) ;
148+
205149 const notFound = new URL ( url . href ) ;
206150 notFound . pathname = '/not-found' ;
207151 await assert . rejects (
@@ -216,6 +160,152 @@ for (const { protocol, createServer } of [
216160 assert . deepStrictEqual ( Object . keys ( json ) , [ 'default' ] ) ;
217161 assert . strictEqual ( json . default . x , 1 ) ;
218162
163+ await describe ( 'guarantee data url will not bypass import restriction' , ( ) => {
164+ it ( 'should not be bypassed by cross protocol redirect' , async ( ) => {
165+ const crossProtocolRedirect = new URL ( url . href ) ;
166+ crossProtocolRedirect . searchParams . set ( 'redirect' , JSON . stringify ( {
167+ status : 302 ,
168+ location : 'data:text/javascript,'
169+ } ) ) ;
170+ await assert . rejects (
171+ import ( crossProtocolRedirect . href ) ,
172+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
173+ ) ;
174+ } ) ;
175+
176+ it ( 'should not be bypassed by data URL' , async ( ) => {
177+ const deps = new URL ( url . href ) ;
178+ deps . searchParams . set ( 'body' , `
179+ export {data} from 'data:text/javascript,export let data = 1';
180+ import * as http from ${ JSON . stringify ( url . href ) } ;
181+ export {http};
182+ ` ) ;
183+ await assert . rejects (
184+ import ( deps . href ) ,
185+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
186+ ) ;
187+ } ) ;
188+
189+ it ( 'should not be bypassed by encodedURI import' , async ( ) => {
190+ const deepDataImport = new URL ( url . href ) ;
191+ deepDataImport . searchParams . set ( 'body' , `
192+ import 'data:text/javascript,import${ encodeURIComponent ( JSON . stringify ( 'data:text/javascript,import "os"' ) ) } ';
193+ ` ) ;
194+ await assert . rejects (
195+ import ( deepDataImport . href ) ,
196+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
197+ ) ;
198+ } ) ;
199+
200+ it ( 'should not be bypassed by relative deps import' , async ( ) => {
201+ const relativeDeps = new URL ( url . href ) ;
202+ relativeDeps . searchParams . set ( 'body' , `
203+ import * as http from "./";
204+ export {http};
205+ ` ) ;
206+ const relativeDepsNS = await import ( relativeDeps . href ) ;
207+ assert . strict . deepStrictEqual ( Object . keys ( relativeDepsNS ) , [ 'http' ] ) ;
208+ assert . strict . equal ( relativeDepsNS . http , ns ) ;
209+ } ) ;
210+
211+ it ( 'should not be bypassed by file dependency import' , async ( ) => {
212+ const fileDep = new URL ( url . href ) ;
213+ const { href } = fixtures . fileURL ( '/es-modules/message.mjs' ) ;
214+ fileDep . searchParams . set ( 'body' , `
215+ import ${ JSON . stringify ( href ) } ;
216+ export default 1;` ) ;
217+ await assert . rejects (
218+ import ( fileDep . href ) ,
219+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
220+ ) ;
221+ } ) ;
222+
223+ it ( 'should not be bypassed by builtin dependency import' , async ( ) => {
224+ const builtinDep = new URL ( url . href ) ;
225+ builtinDep . searchParams . set ( 'body' , `
226+ import 'node:fs';
227+ export default 1;
228+ ` ) ;
229+ await assert . rejects (
230+ import ( builtinDep . href ) ,
231+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
232+ ) ;
233+ } ) ;
234+
235+
236+ it ( 'should not be bypassed by unprefixed builtin dependency import' , async ( ) => {
237+ const unprefixedBuiltinDep = new URL ( url . href ) ;
238+ unprefixedBuiltinDep . searchParams . set ( 'body' , `
239+ import 'fs';
240+ export default 1;
241+ ` ) ;
242+ await assert . rejects (
243+ import ( unprefixedBuiltinDep . href ) ,
244+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
245+ ) ;
246+ } ) ;
247+
248+ it ( 'should not be bypassed by indirect network import' , async ( ) => {
249+ const indirect = new URL ( url . href ) ;
250+ indirect . searchParams . set ( 'body' , `
251+ import childProcess from 'data:text/javascript,export { default } from "node:child_process"'
252+ export {childProcess};
253+ ` ) ;
254+ await assert . rejects (
255+ import ( indirect . href ) ,
256+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
257+ ) ;
258+ } ) ;
259+
260+ it ( 'data: URL can always import other data:' , async ( ) => {
261+ const data = new URL ( 'data:text/javascript,' ) ;
262+ data . searchParams . set ( 'body' ,
263+ 'import \'data:text/javascript,import \'data:\''
264+ ) ;
265+ // doesn't throw
266+ const empty = await import ( data . href ) ;
267+ assert . ok ( empty ) ;
268+ } ) ;
269+
270+ it ( 'data: URL cannot import file: or builtin' , async ( ) => {
271+ const data1 = new URL ( url . href ) ;
272+ data1 . searchParams . set ( 'body' ,
273+ 'import \'file:///some/file.js\''
274+ ) ;
275+ await assert . rejects (
276+ import ( data1 . href ) ,
277+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
278+ ) ;
279+
280+ const data2 = new URL ( url . href ) ;
281+ data2 . searchParams . set ( 'body' ,
282+ 'import \'node:fs\''
283+ ) ;
284+ await assert . rejects (
285+ import ( data2 . href ) ,
286+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
287+ ) ;
288+ } ) ;
289+
290+ it ( 'data: URL cannot import HTTP URLs' , async ( ) => {
291+ const module = fixtures . fileURL ( '/es-modules/import-data-url.mjs' ) ;
292+ try {
293+ await import ( module ) ;
294+ } catch ( err ) {
295+ // We only want the module to load, we don't care if the module throws an
296+ // error as long as the loader does not.
297+ assert . notStrictEqual ( err ?. code , 'ERR_MODULE_NOT_FOUND' ) ;
298+ }
299+ const data1 = new URL ( url . href ) ;
300+ const dataURL = 'data:text/javascript;export * from "node:os"' ;
301+ data1 . searchParams . set ( 'body' , `export * from ${ JSON . stringify ( dataURL ) } ;` ) ;
302+ await assert . rejects (
303+ import ( data1 ) ,
304+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
305+ ) ;
306+ } ) ;
307+ } ) ;
308+
219309 server . close ( ) ;
220310 }
221311}
0 commit comments