1
1
// Flags: --experimental-network-imports --dns-result-order=ipv4first
2
2
import * 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 ' ;
5
5
import assert from 'assert' ;
6
6
import http from 'http' ;
7
7
import os from 'os' ;
8
8
import util from 'util' ;
9
+ import { describe , it } from 'node:test' ;
9
10
10
11
if ( ! common . hasCrypto ) {
11
12
common . skip ( 'missing crypto' ) ;
12
13
}
14
+ tmpdir . refresh ( ) ;
13
15
14
16
const https = ( await import ( 'https' ) ) . default ;
15
17
@@ -18,8 +20,8 @@ const createHTTPServer = http.createServer;
18
20
// Needed to deal w/ test certs
19
21
process . env . NODE_TLS_REJECT_UNAUTHORIZED = '0' ;
20
22
const 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' )
23
25
} ;
24
26
25
27
const createHTTPSServer = https . createServer . bind ( null , options ) ;
@@ -136,72 +138,14 @@ for (const { protocol, createServer } of [
136
138
url . href + 'bar/baz.js'
137
139
) ;
138
140
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
-
198
141
const unsupportedMIME = new URL ( url . href ) ;
199
142
unsupportedMIME . searchParams . set ( 'mime' , 'application/node' ) ;
200
143
unsupportedMIME . searchParams . set ( 'body' , '' ) ;
201
144
await assert . rejects (
202
145
import ( unsupportedMIME . href ) ,
203
146
{ code : 'ERR_UNKNOWN_MODULE_FORMAT' }
204
147
) ;
148
+
205
149
const notFound = new URL ( url . href ) ;
206
150
notFound . pathname = '/not-found' ;
207
151
await assert . rejects (
@@ -216,6 +160,152 @@ for (const { protocol, createServer } of [
216
160
assert . deepStrictEqual ( Object . keys ( json ) , [ 'default' ] ) ;
217
161
assert . strictEqual ( json . default . x , 1 ) ;
218
162
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
+
219
309
server . close ( ) ;
220
310
}
221
311
}
0 commit comments