@@ -332,8 +332,175 @@ function clonePolicyContainer () {
332
332
333
333
// https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
334
334
function determineRequestsReferrer ( request ) {
335
- // TODO
336
- return 'no-referrer'
335
+ // 1. Let policy be request's referrer policy.
336
+ const policy = request . referrerPolicy
337
+
338
+ // Return no-referrer when empty or policy says so
339
+ if ( policy == null || policy === '' || policy === 'no-referrer' ) {
340
+ return 'no-referrer'
341
+ }
342
+
343
+ // 2. Let environment be the request client
344
+ const environment = request . client
345
+ let referrerSource = null
346
+
347
+ /**
348
+ * 3, Switch on request’s referrer:
349
+ "client"
350
+ If environment’s global object is a Window object, then
351
+ Let document be the associated Document of environment’s global object.
352
+ If document’s origin is an opaque origin, return no referrer.
353
+ While document is an iframe srcdoc document,
354
+ let document be document’s browsing context’s browsing context container’s node document.
355
+ Let referrerSource be document’s URL.
356
+
357
+ Otherwise, let referrerSource be environment’s creation URL.
358
+
359
+ a URL
360
+ Let referrerSource be request’s referrer.
361
+ */
362
+ if ( request . referrer === 'client' ) {
363
+ // Not defined in Node but part of the spec
364
+ if ( request . client ?. globalObject ?. constructor ?. name === 'Window' ) { // eslint-disable-line
365
+ const origin = environment . globalObject . self ?. origin ?? environment . globalObject . location ?. origin
366
+
367
+ // If document’s origin is an opaque origin, return no referrer.
368
+ if ( origin == null || origin === 'null' ) return 'no-referrer'
369
+
370
+ // Let referrerSource be document’s URL.
371
+ referrerSource = new URL ( environment . globalObject . location . href )
372
+ } else {
373
+ // 3(a)(II) If environment's global object is not Window,
374
+ // Let referrerSource be environments creationURL
375
+ if ( environment ?. globalObject ?. location == null ) {
376
+ return 'no-referrer'
377
+ }
378
+
379
+ referrerSource = new URL ( environment . globalObject . location . href )
380
+ }
381
+ } else if ( request . referrer instanceof URL ) {
382
+ // 3(b) If requests's referrer is a URL instance, then make
383
+ // referrerSource be requests's referrer.
384
+ referrerSource = request . referrer
385
+ } else {
386
+ // If referrerSource neither client nor instance of URL
387
+ // then return "no-referrer".
388
+ return 'no-referrer'
389
+ }
390
+
391
+ const urlProtocol = referrerSource . protocol
392
+
393
+ // If url's scheme is a local scheme (i.e. one of "about", "data", "javascript", "file")
394
+ // then return "no-referrer".
395
+ if (
396
+ urlProtocol === 'about:' || urlProtocol === 'data:' ||
397
+ urlProtocol === 'blob:'
398
+ ) {
399
+ return 'no-referrer'
400
+ }
401
+
402
+ let temp
403
+ let referrerOrigin
404
+ // 4. Let requests's referrerURL be the result of stripping referrer
405
+ // source for use as referrer (using util function, without origin only)
406
+ const referrerUrl = ( temp = stripURLForReferrer ( referrerSource ) ) . length > 4096
407
+ // 5. Let referrerOrigin be the result of stripping referrer
408
+ // source for use as referrer (using util function, with originOnly true)
409
+ ? ( referrerOrigin = stripURLForReferrer ( referrerSource , true ) )
410
+ // 6. If result of seralizing referrerUrl is a string whose length is greater than
411
+ // 4096, then set referrerURL to referrerOrigin
412
+ : temp
413
+ const areSameOrigin = sameOrigin ( request , referrerUrl )
414
+ const isNonPotentiallyTrustWorthy = isURLPotentiallyTrustworthy ( referrerUrl ) &&
415
+ ! isURLPotentiallyTrustworthy ( request . url )
416
+
417
+ // NOTE: How to treat step 7?
418
+ // 8. Execute the switch statements corresponding to the value of policy:
419
+ switch ( policy ) {
420
+ case 'origin' : return referrerOrigin != null ? referrerOrigin : stripURLForReferrer ( referrerSource , true )
421
+ case 'unsafe-url' : return referrerUrl
422
+ case 'same-origin' :
423
+ return areSameOrigin ? referrerOrigin : 'no-referrer'
424
+ case 'origin-when-cross-origin' :
425
+ return areSameOrigin ? referrerUrl : referrerOrigin
426
+ case 'strict-origin-when-cross-origin' :
427
+ /**
428
+ * 1. If the origin of referrerURL and the origin of request’s current URL are the same,
429
+ * then return referrerURL.
430
+ * 2. If referrerURL is a potentially trustworthy URL and request’s current URL is not a
431
+ * potentially trustworthy URL, then return no referrer.
432
+ * 3. Return referrerOrigin
433
+ */
434
+ if ( areSameOrigin ) return referrerOrigin
435
+ // else return isNonPotentiallyTrustWorthy ? 'no-referrer' : referrerOrigin
436
+ case 'strict-origin' : // eslint-disable-line
437
+ /**
438
+ * 1. If referrerURL is a potentially trustworthy URL and
439
+ * request’s current URL is not a potentially trustworthy URL,
440
+ * then return no referrer.
441
+ * 2. Return referrerOrigin
442
+ */
443
+ case 'no-referrer-when-downgrade' : // eslint-disable-line
444
+ /**
445
+ * 1. If referrerURL is a potentially trustworthy URL and
446
+ * request’s current URL is not a potentially trustworthy URL,
447
+ * then return no referrer.
448
+ * 2. Return referrerOrigin
449
+ */
450
+
451
+ default : // eslint-disable-line
452
+ return isNonPotentiallyTrustWorthy ? 'no-referrer' : referrerOrigin
453
+ }
454
+
455
+ function stripURLForReferrer ( url , originOnly = false ) {
456
+ const urlObject = new URL ( url . href )
457
+ urlObject . username = ''
458
+ urlObject . password = ''
459
+ urlObject . hash = ''
460
+
461
+ return originOnly ? urlObject . origin : urlObject . href
462
+ }
463
+ }
464
+
465
+ function isURLPotentiallyTrustworthy ( url ) {
466
+ if ( ! ( url instanceof URL ) ) {
467
+ return false
468
+ }
469
+
470
+ // If child of about, return true
471
+ if ( url . href === 'about:blank' || url . href === 'about:srcdoc' ) {
472
+ return true
473
+ }
474
+
475
+ // If scheme is data, return true
476
+ if ( url . protocol === 'data:' ) return true
477
+
478
+ // If file, return true
479
+ if ( url . protocol === 'file:' ) return true
480
+
481
+ return isOriginPotentiallyTrustworthy ( url . origin )
482
+
483
+ function isOriginPotentiallyTrustworthy ( origin ) {
484
+ // If origin is explicitly null, return false
485
+ if ( origin == null || origin === 'null' ) return false
486
+
487
+ const originAsURL = new URL ( origin )
488
+
489
+ // If secure, return true
490
+ if ( originAsURL . protocol === 'https:' || originAsURL . protocol === 'wss:' ) {
491
+ return true
492
+ }
493
+
494
+ // If localhost or variants, return true
495
+ if ( / ^ 1 2 7 (?: \. [ 0 - 9 ] + ) { 0 , 2 } \. [ 0 - 9 ] + $ | ^ \[ (?: 0 * : ) * ?: ? 0 * 1 \] $ / . test ( originAsURL . hostname ) ||
496
+ ( originAsURL . hostname === 'localhost' || originAsURL . hostname . includes ( 'localhost.' ) ) ||
497
+ ( originAsURL . hostname . endsWith ( '.localhost' ) ) ) {
498
+ return true
499
+ }
500
+
501
+ // If any other, return false
502
+ return false
503
+ }
337
504
}
338
505
339
506
/**
@@ -617,6 +784,7 @@ module.exports = {
617
784
responseURL,
618
785
responseLocationURL,
619
786
isBlobLike,
787
+ isURLPotentiallyTrustworthy,
620
788
isValidReasonPhrase,
621
789
sameOrigin,
622
790
normalizeMethod,
0 commit comments