@@ -84,7 +84,7 @@ function __uvHook(window) {
8484 __uv . meta . origin = location . origin ;
8585 __uv . location = client . location . emulate (
8686 ( href ) => {
87- if ( href === 'about:srcdoc' ) return new URL ( href ) ;
87+ if ( href . startsWith ( 'about:' ) ) return new URL ( href ) ;
8888 if ( href . startsWith ( 'blob:' ) ) href = href . slice ( 'blob:' . length ) ;
8989 return new URL ( __uv . sourceUrl ( href ) ) ;
9090 } ,
@@ -106,10 +106,6 @@ function __uvHook(window) {
106106 // websockets
107107 const bareClient = new Ultraviolet . BareClient ( __uv$bareURL , __uv$bareData ) ;
108108
109- if ( __uv . location . href === 'about:srcdoc' ) {
110- __uv . meta = window . parent . __uv . meta ;
111- }
112-
113109 if ( window . EventTarget ) {
114110 __uv . addEventListener = window . EventTarget . prototype . addEventListener ;
115111 __uv . removeListener = window . EventTarget . prototype . removeListener ;
@@ -502,7 +498,7 @@ function __uvHook(window) {
502498 } ) ;
503499
504500 client . node . on ( 'baseURI' , ( event ) => {
505- if ( event . data . value . startsWith ( window . location . origin ) )
501+ if ( event . data . value . startsWith ( __uv . meta . origin ) )
506502 event . data . value = __uv . sourceUrl ( event . data . value ) ;
507503 } ) ;
508504
@@ -718,32 +714,230 @@ function __uvHook(window) {
718714 'contentWindow'
719715 ) . get ;
720716
721- function uvInject ( that ) {
722- const win = contentWindowGet . call ( that ) ;
723-
717+ /**
718+ *
719+ * @param {typeof globalThis } win
720+ */
721+ function uvInject ( win ) {
724722 if ( ! win . __uv )
725723 try {
726724 __uvHook ( win ) ;
727- } catch ( e ) {
725+ } catch ( err ) {
728726 console . error ( 'catastrophic failure' ) ;
729- console . error ( e ) ;
727+ console . error ( err ) ;
730728 }
731729 }
732730
733731 client . element . hookProperty ( HTMLIFrameElement , 'contentWindow' , {
734732 get : ( target , that ) => {
735- uvInject ( that ) ;
736- return target . call ( that ) ;
733+ const win = contentWindowGet . call ( that ) ;
734+ uvInject ( win ) ;
735+ return sandboxWindow ( win ) ;
737736 } ,
738737 } ) ;
739738
740739 client . element . hookProperty ( HTMLIFrameElement , 'contentDocument' , {
741740 get : ( target , that ) => {
742- uvInject ( that ) ;
743- return target . call ( that ) ;
741+ const win = contentWindowGet . call ( that ) ;
742+ uvInject ( win ) ;
743+ try {
744+ return sandboxWindow ( win ) . document ;
745+ } catch ( err ) {
746+ // we are sandboxed, return null
747+ return null ;
748+ }
744749 } ,
745750 } ) ;
746751
752+ const sandboxed = new WeakMap ( ) ;
753+
754+ function illegalSandbox ( ) {
755+ throw new DOMException (
756+ `Blocked a frame with "${ __uv . location . origin } " from accessing a cross-origin frame.`
757+ ) ;
758+ }
759+
760+ /**
761+ *
762+ * @template T
763+ * @param {T } object
764+ * @returns {T }
765+ */
766+ function sandboxObject ( object ) {
767+ lockProperties ( object ) ;
768+ const target = { } ;
769+ Reflect . setPrototypeOf ( target , null ) ;
770+ return new Proxy ( target , {
771+ get : ( target , prop , receiver ) => {
772+ const descriptor = Reflect . getOwnPropertyDescriptor (
773+ object ,
774+ prop
775+ ) ;
776+
777+ if (
778+ ! ( prop in object ) ||
779+ ( ! ( 'value' in descriptor ) &&
780+ typeof descriptor . get !== 'function' )
781+ )
782+ illegalSandbox ( ) ;
783+
784+ return Reflect . get ( target , prop , receiver ) ;
785+ } ,
786+ set : ( target , prop , value ) => {
787+ const descriptor = Reflect . getOwnPropertyDescriptor (
788+ object ,
789+ prop
790+ ) ;
791+
792+ if ( ! ( prop in object ) || typeof descriptor . set !== 'function' )
793+ illegalSandbox ( ) ;
794+
795+ return Reflect . set ( target , prop , value ) ;
796+ } ,
797+ defineProperty : ( ) => {
798+ illegalSandbox ( ) ;
799+ } ,
800+ getOwnPropertyDescriptor : ( target , prop , descriptor ) => {
801+ if ( ! ( prop in object ) ) illegalSandbox ( ) ;
802+
803+ return Reflect . getOwnPropertyDescriptor (
804+ target ,
805+ prop ,
806+ descriptor
807+ ) ;
808+ } ,
809+ setPrototypeOf : ( ) => {
810+ illegalSandbox ( ) ;
811+ } ,
812+ has : ( target , prop ) => {
813+ if ( ! ( prop in object ) ) illegalSandbox ( ) ;
814+
815+ return true ;
816+ } ,
817+ } ) ;
818+ }
819+
820+ const unknownSandboxed = [
821+ 'then' ,
822+ Symbol . toStringTag ,
823+ Symbol . hasInstance ,
824+ Symbol . isConcatSpreadable ,
825+ ] ;
826+
827+ function lockProperties ( object ) {
828+ Reflect . setPrototypeOf ( object , null ) ;
829+
830+ for ( const unknown of unknownSandboxed )
831+ Reflect . defineProperty ( object , unknown , {
832+ value : undefined ,
833+ writable : false ,
834+ enumerable : false ,
835+ configurable : false ,
836+ } ) ;
837+
838+ for ( const [ key , descriptor ] of Object . entries (
839+ Object . getOwnPropertyDescriptors ( object )
840+ ) ) {
841+ if ( ! descriptor . configurable ) continue ;
842+
843+ descriptor . enumerable = false ;
844+ descriptor . configurable = true ;
845+
846+ if ( 'value' in descriptor ) {
847+ descriptor . writable = false ;
848+ /*if (typeof descriptor.value === 'function')
849+ restrict(descriptor.value);*/
850+ }
851+
852+ /*
853+ if ('get' in descriptor && typeof descriptor.get === 'function')
854+ descriptor.get = restrict(descriptor.get);
855+
856+ if ('set' in descriptor && typeof descriptor.set === 'function')
857+ descriptor.set = restrict(descriptor.set);
858+ */
859+
860+ Reflect . defineProperty ( object , key , descriptor ) ;
861+ }
862+ }
863+
864+ /**
865+ *
866+ * @param {typeof globalThis } win
867+ * @returns {Location }
868+ */
869+ function sandboxLocation ( win ) {
870+ return sandboxObject ( {
871+ set href ( value ) {
872+ win . __uv . location . href = value ;
873+ } ,
874+ replace ( value ) {
875+ win . __uv . location . replace ( value ) ;
876+ } ,
877+ } ) ;
878+ }
879+
880+ /**
881+ *
882+ * @param {typeof globalThis } win
883+ * @returns {typeof globalThis }
884+ */
885+ function sandboxWindow ( win ) {
886+ if ( sandboxed . has ( win ) ) return sandboxed . get ( win ) ;
887+ if (
888+ new URL ( win . __uv . meta . base ) . origin ===
889+ new URL ( window . __uv . meta . base ) . origin
890+ )
891+ return win ;
892+
893+ const obj = {
894+ get window ( ) {
895+ return sandboxedWin ;
896+ } ,
897+ get location ( ) {
898+ return loc ;
899+ } ,
900+ set location ( value ) {
901+ win . __uv . location . href = value ;
902+ } ,
903+ get closed ( ) {
904+ return win . closed ;
905+ } ,
906+ get frames ( ) {
907+ return sandboxedWin ;
908+ } ,
909+ get length ( ) {
910+ return win . length ;
911+ } ,
912+ get top ( ) {
913+ return win [ __uv . methods . top ] ;
914+ } ,
915+ get opener ( ) {
916+ return sandboxWindow ( win . opener ) ;
917+ } ,
918+ get parent ( ) {
919+ return sandboxWindow ( win . parent ) ;
920+ } ,
921+ blur ( ) {
922+ win . blur ( ) ;
923+ } ,
924+ close ( ) {
925+ win . close ( ) ;
926+ } ,
927+ focus ( ) {
928+ win . focus ( ) ;
929+ } ,
930+ postMessage ( ...args ) {
931+ // todo: remove old workaround for postMessage
932+ win . __uv$setSource ( __uv ) . postMessage ( ...args ) ;
933+ } ,
934+ } ;
935+ const loc = sandboxLocation ( win . location ) ;
936+
937+ const sandboxedWin = sandboxObject ( obj ) ;
938+ return sandboxedWin ;
939+ }
940+
747941 client . element . hookProperty ( HTMLIFrameElement , 'srcdoc' , {
748942 get : ( target , that ) => {
749943 return (
@@ -1543,7 +1737,7 @@ function __uvHook(window) {
15431737
15441738 if ( this === window ) {
15451739 try {
1546- return '__uv' in val ? val : this ;
1740+ return '__uv' in val ? sandboxWindow ( val ) : this ;
15471741 } catch ( e ) {
15481742 return this ;
15491743 }
0 commit comments