2
2
3
3
import { glob } from 'typedoc/dist/lib/utils/fs.js'
4
4
import ContentFeature from '../content-feature.js'
5
- import { DDGProxy , getStackTraceOrigins , getStack , matchHostname , injectGlobalStyles , createStyleElement , postDebugMessage , taintSymbol , hasTaintedMethod , defineProperty } from '../utils.js'
5
+ import { DDGProxy , getStackTraceOrigins , getStack , matchHostname , injectGlobalStyles , createStyleElement , postDebugMessage , taintSymbol , hasTaintedMethod , defineProperty , getTabHostname } from '../utils.js'
6
6
import { wrapScriptCodeOverload } from './runtime-checks/script-overload.js'
7
7
import { Reflect } from '../captured-globals.js'
8
8
@@ -66,13 +66,11 @@ const jsMimeTypes = [
66
66
'text/x-javascript'
67
67
]
68
68
69
- function getTaintFromScope ( scope , args ) {
69
+ function getTaintFromScope ( scope , args , shouldStackCheck = false ) {
70
70
try {
71
71
scope = args . callee . caller
72
- } catch {
73
- console . log ( 'taint: failed to get callers scope' , args , scope )
74
- }
75
- return hasTaintedMethod ( scope )
72
+ } catch { }
73
+ return hasTaintedMethod ( scope , shouldStackCheck )
76
74
}
77
75
78
76
class DDGRuntimeChecks extends HTMLElement {
@@ -81,6 +79,7 @@ class DDGRuntimeChecks extends HTMLElement {
81
79
#listeners
82
80
#connected
83
81
#sinks
82
+ #debug
84
83
85
84
constructor ( ) {
86
85
super ( )
@@ -89,6 +88,7 @@ class DDGRuntimeChecks extends HTMLElement {
89
88
this . #listeners = [ ]
90
89
this . #connected = false
91
90
this . #sinks = { }
91
+ this . #debug = false
92
92
if ( shadowDomEnabled ) {
93
93
const shadow = this . attachShadow ( { mode : 'open' } )
94
94
const style = createStyleElement ( `
@@ -103,8 +103,9 @@ class DDGRuntimeChecks extends HTMLElement {
103
103
/**
104
104
* This method is called once and externally so has to remain public.
105
105
**/
106
- setTagName ( tagName ) {
106
+ setTagName ( tagName , debug = false ) {
107
107
this . #tagName = tagName
108
+ this . #debug = debug
108
109
109
110
// Clear the method so it can't be called again
110
111
// @ts -expect-error - error TS2790: The operand of a 'delete' operator must be optional.
@@ -178,6 +179,16 @@ class DDGRuntimeChecks extends HTMLElement {
178
179
if ( taintCheck ) {
179
180
// Add a symbol to the element so we can identify it as a runtime checked element
180
181
Object . defineProperty ( el , taintSymbol , { value : true , configurable : false , enumerable : false , writable : false } )
182
+ // Only show this attribute whilst debugging
183
+ if ( this . #debug) {
184
+ el . setAttribute ( 'data-ddg-runtime-checks' , 'true' )
185
+ }
186
+ try {
187
+ const origin = this . src && new URL ( this . src , window . location . href ) . hostname
188
+ if ( origin && navigator ?. duckduckgo ?. taintedOrigins ) {
189
+ navigator . duckduckgo . taintedOrigins . add ( origin )
190
+ }
191
+ } catch { }
181
192
}
182
193
183
194
// Reflect all attrs to the new element
@@ -461,7 +472,7 @@ function isInterrogatingDebugMessage (matchType, matchedStackDomain, stack, scri
461
472
} )
462
473
}
463
474
464
- function overrideCreateElement ( ) {
475
+ function overrideCreateElement ( debug ) {
465
476
const proxy = new DDGProxy ( featureName , Document . prototype , 'createElement' , {
466
477
apply ( fn , scope , args ) {
467
478
if ( args . length >= 1 ) {
@@ -470,7 +481,7 @@ function overrideCreateElement () {
470
481
if ( shouldInterrogate ( initialTagName ) ) {
471
482
args [ 0 ] = 'ddg-runtime-checks'
472
483
const el = Reflect . apply ( fn , scope , args )
473
- el . setTagName ( initialTagName )
484
+ el . setTagName ( initialTagName , debug )
474
485
return el
475
486
}
476
487
}
@@ -541,7 +552,7 @@ export default class RuntimeChecks extends ContentFeature {
541
552
replaceElement = this . getFeatureSettingEnabled ( 'replaceElement' ) || false
542
553
monitorProperties = this . getFeatureSettingEnabled ( 'monitorProperties' ) || true
543
554
544
- overrideCreateElement ( )
555
+ overrideCreateElement ( this . isDebug )
545
556
546
557
if ( this . getFeatureSettingEnabled ( 'overloadInstanceOf' ) ) {
547
558
overloadInstanceOfChecks ( HTMLScriptElement )
@@ -569,40 +580,40 @@ export default class RuntimeChecks extends ContentFeature {
569
580
injectGenericOverloads ( ) {
570
581
const genericOverloads = this . getFeatureSetting ( 'injectGenericOverloads' )
571
582
if ( 'Date' in genericOverloads ) {
572
- this . overloadDate ( )
583
+ this . overloadDate ( genericOverloads . Date )
573
584
}
574
585
if ( 'Date.prototype.getTimezoneOffset' in genericOverloads ) {
575
- this . overloadDateGetTimezoneOffset ( )
586
+ this . overloadDateGetTimezoneOffset ( genericOverloads [ 'Date.prototype.getTimezoneOffset' ] )
576
587
}
577
588
if ( 'NavigatorUAData.prototype.getHighEntropyValues' in genericOverloads ) {
578
- this . overloadHighEntropyValues ( )
589
+ this . overloadHighEntropyValues ( genericOverloads [ 'NavigatorUAData.prototype.getHighEntropyValues' ] )
579
590
}
580
591
[ 'localStorage' , 'sessionStorage' ] . forEach ( storageType => {
581
592
if ( storageType in genericOverloads ) {
582
593
const storageConfig = genericOverloads [ storageType ]
583
594
if ( storageConfig . scheme === 'memory' ) {
584
- this . overloadStorageWithMemory ( storageType )
595
+ this . overloadStorageWithMemory ( storageConfig , storageType )
585
596
} else if ( storageConfig . scheme === 'session' ) {
586
- this . overloadStorageWithSession ( storageType )
597
+ this . overloadStorageWithSession ( storageConfig , storageType )
587
598
}
588
599
}
589
600
} )
590
601
const breakpoints = this . getFeatureSetting ( 'breakpoints' )
591
602
const screenSize = { height : screen . height , width : screen . width } ;
592
- [ 'innerHeight' , 'innerWidth' , 'Screen.prototype.height' , 'Screen.prototype.width' ] . forEach ( sizing => {
603
+ [ 'innerHeight' , 'innerWidth' , 'outerHeight' , 'outerWidth' , ' Screen.prototype.height', 'Screen.prototype.width' ] . forEach ( sizing => {
593
604
if ( sizing in genericOverloads ) {
594
605
const sizingConfig = genericOverloads [ sizing ]
595
- this . overloadScreenSizes ( breakpoints , screenSize , sizing , sizingConfig . offset || 0 )
606
+ this . overloadScreenSizes ( sizingConfig , breakpoints , screenSize , sizing , sizingConfig . offset || 0 )
596
607
}
597
608
} )
598
609
}
599
610
600
- overloadDate ( ) {
611
+ overloadDate ( config ) {
601
612
const offset = ( new Date ( ) ) . getTimezoneOffset ( )
602
613
globalThis . Date = new Proxy ( globalThis . Date , {
603
614
construct ( target , args ) {
604
615
const constructed = Reflect . construct ( target , args )
605
- if ( getTaintFromScope ( this , arguments ) ) {
616
+ if ( getTaintFromScope ( this , arguments , config . stackCheck ) ) {
606
617
// Falible in that the page could brute force the offset to match. We should fix this.
607
618
if ( constructed . getTimezoneOffset ( ) === offset ) {
608
619
return constructed . getUTCDate ( )
@@ -613,22 +624,22 @@ export default class RuntimeChecks extends ContentFeature {
613
624
} )
614
625
}
615
626
616
- overloadDateGetTimezoneOffset ( ) {
627
+ overloadDateGetTimezoneOffset ( config ) {
617
628
const offset = ( new Date ( ) ) . getTimezoneOffset ( )
618
629
defineProperty ( globalThis . Date . prototype , 'getTimezoneOffset' , {
619
630
configurable : true ,
620
631
enumerable : true ,
621
632
writable : true ,
622
633
value ( ) {
623
- if ( getTaintFromScope ( this , arguments ) ) {
634
+ if ( getTaintFromScope ( this , arguments , config . stackCheck ) ) {
624
635
return 0
625
636
}
626
637
return offset
627
638
}
628
639
} )
629
640
}
630
641
631
- overloadHighEntropyValues ( ) {
642
+ overloadHighEntropyValues ( config ) {
632
643
if ( ! ( 'NavigatorUAData' in globalThis ) ) {
633
644
return
634
645
}
@@ -640,7 +651,7 @@ export default class RuntimeChecks extends ContentFeature {
640
651
writable : true ,
641
652
value ( hints ) {
642
653
let hintsOut = hints
643
- if ( getTaintFromScope ( this , arguments ) ) {
654
+ if ( getTaintFromScope ( this , arguments , config . stackCheck ) ) {
644
655
// If tainted override with default values (using empty array)
645
656
hintsOut = [ ]
646
657
}
@@ -649,7 +660,7 @@ export default class RuntimeChecks extends ContentFeature {
649
660
} )
650
661
}
651
662
652
- overloadStorageWithMemory ( key ) {
663
+ overloadStorageWithMemory ( config , key ) {
653
664
class MemoryStorage {
654
665
#data = { }
655
666
@@ -680,19 +691,19 @@ export default class RuntimeChecks extends ContentFeature {
680
691
}
681
692
682
693
const storage = new MemoryStorage ( )
683
- this . overrideStorage ( key , storage )
694
+ this . overrideStorage ( config , key , storage )
684
695
}
685
696
686
- overloadStorageWithSession ( key ) {
697
+ overloadStorageWithSession ( key , config ) {
687
698
const storage = globalThis . sessionStorage
688
- this . overrideStorage ( key , storage )
699
+ this . overrideStorage ( config , key , storage )
689
700
}
690
701
691
- overrideStorage ( key , storage ) {
702
+ overrideStorage ( config , key , storage ) {
692
703
const originalStorage = globalThis [ key ]
693
704
defineProperty ( globalThis , key , {
694
705
get ( ) {
695
- if ( getTaintFromScope ( this , arguments ) ) {
706
+ if ( getTaintFromScope ( this , arguments , config . stackCheck ) ) {
696
707
return storage
697
708
}
698
709
return originalStorage
@@ -713,7 +724,7 @@ export default class RuntimeChecks extends ContentFeature {
713
724
* @param {string } key
714
725
* @param {number } [offset]
715
726
*/
716
- overloadScreenSizes ( breakpoints , screenSize , key , offset ) {
727
+ overloadScreenSizes ( config , breakpoints , screenSize , key , offset ) {
717
728
const closest = findClosestBreakpoint ( breakpoints , screenSize )
718
729
if ( ! closest ) {
719
730
return
@@ -725,9 +736,11 @@ export default class RuntimeChecks extends ContentFeature {
725
736
let receiver
726
737
switch ( key ) {
727
738
case 'innerHeight' :
739
+ case 'outerHeight' :
728
740
returnVal = closest . height - offset
729
741
break
730
742
case 'innerWidth' :
743
+ case 'outerWidth' :
731
744
returnVal = closest . width - offset
732
745
break
733
746
case 'Screen.prototype.height' :
@@ -746,7 +759,7 @@ export default class RuntimeChecks extends ContentFeature {
746
759
const defaultVal = Reflect . get ( scope , overrideKey , receiver )
747
760
defineProperty ( scope , overrideKey , {
748
761
get ( ) {
749
- if ( getTaintFromScope ( this , arguments ) ) {
762
+ if ( getTaintFromScope ( this , arguments , config . stackCheck ) ) {
750
763
return returnVal
751
764
}
752
765
return defaultVal
0 commit comments