@@ -9,11 +9,13 @@ import {
99 appendChild ,
1010 replaceChildren ,
1111 textContentSet ,
12+ addEventListener ,
13+ ownerDocument ,
1214 navigation ,
1315 url , destination , includes ,
1416 preventDefault , stopPropagation ,
1517} from './native.mjs' ;
16- import { distraction , hardened } from './element.mjs' ;
18+ import { distraction , loadable , hardened } from './element.mjs' ;
1719import { getShadow } from './shadow.mjs' ;
1820
1921// text-fragments links can be abused to leak shadow internals - block in-app redirection to them
@@ -37,6 +39,18 @@ export function LavaDome(host, opts) {
3739 const shadow = getShadow ( host , opts ) ;
3840 replaceChildren ( shadow ) ;
3941
42+ // fire every time instance is reloaded and abort loading for non-top documents
43+ const iframe = loadable ( ) ;
44+ addEventListener ( iframe , 'load' , ( ) => {
45+ const ownerDoc = ownerDocument ( iframe ) ;
46+ if ( ownerDoc !== document ) {
47+ replaceChildren ( shadow ) ;
48+ throw new Error ( `LavaDomeCore: ` +
49+ `The document to which LavaDome was originally introduced ` +
50+ `must be the same as the one this instance is inserted to` ) ;
51+ }
52+ } ) ;
53+
4054 // child of the shadow, where the secret is set, must be hardened
4155 const child = hardened ( ) ;
4256 appendChild ( shadow , child ) ;
@@ -54,6 +68,9 @@ export function LavaDome(host, opts) {
5468 return textContentSet ( child , text ) ;
5569 }
5670
71+ // attach loadable only once per instance to avoid excessive load firing
72+ appendChild ( shadow , iframe ) ;
73+
5774 // place each char of the secret in its own LavaDome protection instance
5875 map ( from ( text ) , char => {
5976 const span = createElement ( document , 'span' ) ;
@@ -66,4 +83,4 @@ export function LavaDome(host, opts) {
6683 // add a distraction against side channel leaks attack attempts
6784 appendChild ( child , distraction ( ) ) ;
6885 }
69- }
86+ }
0 commit comments