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 ) ;
@@ -131,79 +133,174 @@ for (const { protocol, createServer } of [
131
133
url . href + 'bar/baz.js'
132
134
) ;
133
135
134
- const crossProtocolRedirect = new URL ( url . href ) ;
135
- crossProtocolRedirect . searchParams . set ( 'redirect' , JSON . stringify ( {
136
- status : 302 ,
137
- location : 'data:text/javascript,'
138
- } ) ) ;
139
- await assert . rejects (
140
- import ( crossProtocolRedirect . href ) ,
141
- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
142
- ) ;
143
-
144
- const deps = new URL ( url . href ) ;
145
- deps . searchParams . set ( 'body' , `
146
- export {data} from 'data:text/javascript,export let data = 1';
147
- import * as http from ${ JSON . stringify ( url . href ) } ;
148
- export {http};
149
- ` ) ;
150
- const depsNS = await import ( deps . href ) ;
151
- assert . strict . deepStrictEqual ( Object . keys ( depsNS ) , [ 'data' , 'http' ] ) ;
152
- assert . strict . equal ( depsNS . data , 1 ) ;
153
- assert . strict . equal ( depsNS . http , ns ) ;
154
-
155
- const relativeDeps = new URL ( url . href ) ;
156
- relativeDeps . searchParams . set ( 'body' , `
157
- import * as http from "./";
158
- export {http};
159
- ` ) ;
160
- const relativeDepsNS = await import ( relativeDeps . href ) ;
161
- assert . strict . deepStrictEqual ( Object . keys ( relativeDepsNS ) , [ 'http' ] ) ;
162
- assert . strict . equal ( relativeDepsNS . http , ns ) ;
163
- const fileDep = new URL ( url . href ) ;
164
- const { href } = pathToFileURL ( path ( '/es-modules/message.mjs' ) ) ;
165
- fileDep . searchParams . set ( 'body' , `
166
- import ${ JSON . stringify ( href ) } ;
167
- export default 1;` ) ;
168
- await assert . rejects (
169
- import ( fileDep . href ) ,
170
- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
171
- ) ;
172
-
173
- const builtinDep = new URL ( url . href ) ;
174
- builtinDep . searchParams . set ( 'body' , `
175
- import 'node:fs';
176
- export default 1;
177
- ` ) ;
178
- await assert . rejects (
179
- import ( builtinDep . href ) ,
180
- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
181
- ) ;
182
-
183
- const unprefixedBuiltinDep = new URL ( url . href ) ;
184
- unprefixedBuiltinDep . searchParams . set ( 'body' , `
185
- import 'fs';
186
- export default 1;
187
- ` ) ;
188
- await assert . rejects (
189
- import ( unprefixedBuiltinDep . href ) ,
190
- { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
191
- ) ;
192
-
193
136
const unsupportedMIME = new URL ( url . href ) ;
194
137
unsupportedMIME . searchParams . set ( 'mime' , 'application/node' ) ;
195
138
unsupportedMIME . searchParams . set ( 'body' , '' ) ;
196
139
await assert . rejects (
197
140
import ( unsupportedMIME . href ) ,
198
141
{ code : 'ERR_UNKNOWN_MODULE_FORMAT' }
199
142
) ;
143
+
200
144
const notFound = new URL ( url . href ) ;
201
145
notFound . pathname = '/not-found' ;
202
146
await assert . rejects (
203
147
import ( notFound . href ) ,
204
148
{ code : 'ERR_MODULE_NOT_FOUND' } ,
205
149
) ;
206
150
151
+ const jsonUrl = new URL ( url . href + 'json' ) ;
152
+ jsonUrl . searchParams . set ( 'mime' , 'application/json' ) ;
153
+ jsonUrl . searchParams . set ( 'body' , '{"x": 1}' ) ;
154
+ const json = await import ( jsonUrl . href , { with : { type : 'json' } } ) ;
155
+ assert . deepStrictEqual ( Object . keys ( json ) , [ 'default' ] ) ;
156
+ assert . strictEqual ( json . default . x , 1 ) ;
157
+
158
+ await describe ( 'guarantee data url will not bypass import restriction' , ( ) => {
159
+ it ( 'should not be bypassed by cross protocol redirect' , async ( ) => {
160
+ const crossProtocolRedirect = new URL ( url . href ) ;
161
+ crossProtocolRedirect . searchParams . set ( 'redirect' , JSON . stringify ( {
162
+ status : 302 ,
163
+ location : 'data:text/javascript,'
164
+ } ) ) ;
165
+ await assert . rejects (
166
+ import ( crossProtocolRedirect . href ) ,
167
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
168
+ ) ;
169
+ } ) ;
170
+
171
+ it ( 'should not be bypassed by data URL' , async ( ) => {
172
+ const deps = new URL ( url . href ) ;
173
+ deps . searchParams . set ( 'body' , `
174
+ export {data} from 'data:text/javascript,export let data = 1';
175
+ import * as http from ${ JSON . stringify ( url . href ) } ;
176
+ export {http};
177
+ ` ) ;
178
+ await assert . rejects (
179
+ import ( deps . href ) ,
180
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
181
+ ) ;
182
+ } ) ;
183
+
184
+ it ( 'should not be bypassed by encodedURI import' , async ( ) => {
185
+ const deepDataImport = new URL ( url . href ) ;
186
+ deepDataImport . searchParams . set ( 'body' , `
187
+ import 'data:text/javascript,import${ encodeURIComponent ( JSON . stringify ( 'data:text/javascript,import "os"' ) ) } ';
188
+ ` ) ;
189
+ await assert . rejects (
190
+ import ( deepDataImport . href ) ,
191
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
192
+ ) ;
193
+ } ) ;
194
+
195
+ it ( 'should not be bypassed by relative deps import' , async ( ) => {
196
+ const relativeDeps = new URL ( url . href ) ;
197
+ relativeDeps . searchParams . set ( 'body' , `
198
+ import * as http from "./";
199
+ export {http};
200
+ ` ) ;
201
+ const relativeDepsNS = await import ( relativeDeps . href ) ;
202
+ assert . strict . deepStrictEqual ( Object . keys ( relativeDepsNS ) , [ 'http' ] ) ;
203
+ assert . strict . equal ( relativeDepsNS . http , ns ) ;
204
+ } ) ;
205
+
206
+ it ( 'should not be bypassed by file dependency import' , async ( ) => {
207
+ const fileDep = new URL ( url . href ) ;
208
+ const { href } = fixtures . fileURL ( '/es-modules/message.mjs' ) ;
209
+ fileDep . searchParams . set ( 'body' , `
210
+ import ${ JSON . stringify ( href ) } ;
211
+ export default 1;` ) ;
212
+ await assert . rejects (
213
+ import ( fileDep . href ) ,
214
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
215
+ ) ;
216
+ } ) ;
217
+
218
+ it ( 'should not be bypassed by builtin dependency import' , async ( ) => {
219
+ const builtinDep = new URL ( url . href ) ;
220
+ builtinDep . searchParams . set ( 'body' , `
221
+ import 'node:fs';
222
+ export default 1;
223
+ ` ) ;
224
+ await assert . rejects (
225
+ import ( builtinDep . href ) ,
226
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
227
+ ) ;
228
+ } ) ;
229
+
230
+
231
+ it ( 'should not be bypassed by unprefixed builtin dependency import' , async ( ) => {
232
+ const unprefixedBuiltinDep = new URL ( url . href ) ;
233
+ unprefixedBuiltinDep . searchParams . set ( 'body' , `
234
+ import 'fs';
235
+ export default 1;
236
+ ` ) ;
237
+ await assert . rejects (
238
+ import ( unprefixedBuiltinDep . href ) ,
239
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
240
+ ) ;
241
+ } ) ;
242
+
243
+ it ( 'should not be bypassed by indirect network import' , async ( ) => {
244
+ const indirect = new URL ( url . href ) ;
245
+ indirect . searchParams . set ( 'body' , `
246
+ import childProcess from 'data:text/javascript,export { default } from "node:child_process"'
247
+ export {childProcess};
248
+ ` ) ;
249
+ await assert . rejects (
250
+ import ( indirect . href ) ,
251
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
252
+ ) ;
253
+ } ) ;
254
+
255
+ it ( 'data: URL can always import other data:' , async ( ) => {
256
+ const data = new URL ( 'data:text/javascript,' ) ;
257
+ data . searchParams . set ( 'body' ,
258
+ 'import \'data:text/javascript,import \'data:\''
259
+ ) ;
260
+ // doesn't throw
261
+ const empty = await import ( data . href ) ;
262
+ assert . ok ( empty ) ;
263
+ } ) ;
264
+
265
+ it ( 'data: URL cannot import file: or builtin' , async ( ) => {
266
+ const data1 = new URL ( url . href ) ;
267
+ data1 . searchParams . set ( 'body' ,
268
+ 'import \'file:///some/file.js\''
269
+ ) ;
270
+ await assert . rejects (
271
+ import ( data1 . href ) ,
272
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
273
+ ) ;
274
+
275
+ const data2 = new URL ( url . href ) ;
276
+ data2 . searchParams . set ( 'body' ,
277
+ 'import \'node:fs\''
278
+ ) ;
279
+ await assert . rejects (
280
+ import ( data2 . href ) ,
281
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
282
+ ) ;
283
+ } ) ;
284
+
285
+ it ( 'data: URL cannot import HTTP URLs' , async ( ) => {
286
+ const module = fixtures . fileURL ( '/es-modules/import-data-url.mjs' ) ;
287
+ try {
288
+ await import ( module ) ;
289
+ } catch ( err ) {
290
+ // We only want the module to load, we don't care if the module throws an
291
+ // error as long as the loader does not.
292
+ assert . notStrictEqual ( err ?. code , 'ERR_MODULE_NOT_FOUND' ) ;
293
+ }
294
+ const data1 = new URL ( url . href ) ;
295
+ const dataURL = 'data:text/javascript;export * from "node:os"' ;
296
+ data1 . searchParams . set ( 'body' , `export * from ${ JSON . stringify ( dataURL ) } ;` ) ;
297
+ await assert . rejects (
298
+ import ( data1 ) ,
299
+ { code : 'ERR_NETWORK_IMPORT_DISALLOWED' }
300
+ ) ;
301
+ } ) ;
302
+ } ) ;
303
+
207
304
server . close ( ) ;
208
305
}
209
306
}
1 commit comments
eligrey commentedon Jul 8, 2024
These changes are incorrect.
data:
URI imports are not network requests. I don't understand why you're making this distinction, especially given that you don't consider file: URIs as network requests.It's actually quite ironic since you can make network requests directly with file: URIs but not data: URIs (in browsers - no idea about node.js)