diff --git a/packages/core/src/lavadome.mjs b/packages/core/src/lavadome.mjs index 14a6b09..3f82736 100644 --- a/packages/core/src/lavadome.mjs +++ b/packages/core/src/lavadome.mjs @@ -9,10 +9,24 @@ import { appendChild, replaceChildren, textContentSet, + navigation, + url, destination, includes, + preventDefault, stopPropagation, } from './native.mjs'; import {distraction, unselectable} from './element.mjs'; import {getShadow} from './shadow.mjs'; +// text-fragments links can be abused to leak shadow internals - block in-app redirection to them +navigation.addEventListener('navigate', event => { + const dest = url(destination(event)); + if (includes(dest, ':~:')) { + preventDefault(event); + stopPropagation(event); + throw new Error( + `LavaDomeCore: in-app redirection to text-fragments links is blocked to ensure security`); + } +}); + export function LavaDome(host, opts) { opts = options(opts); diff --git a/packages/core/src/native.mjs b/packages/core/src/native.mjs index 291345a..6945615 100644 --- a/packages/core/src/native.mjs +++ b/packages/core/src/native.mjs @@ -5,6 +5,7 @@ const { Function, Math, parseInt, WeakMap, Error, JSON, + navigation, } = globalThis; const { defineProperties, assign, @@ -26,13 +27,18 @@ export const appendChild = n(globalThis?.Node?.prototype, 'appendChild', 'value' export const textContentSet = n(globalThis?.Node?.prototype, 'textContent', 'set'); export const setAttribute = n(globalThis?.Element?.prototype, 'setAttribute', 'value'); export const toUpperCase = n(globalThis?.String?.prototype, 'toUpperCase', 'value'); +export const includes = n(globalThis?.String?.prototype, 'includes', 'value'); export const map = n(globalThis?.Array?.prototype, 'map', 'value'); export const join = n(globalThis?.Array?.prototype, 'join', 'value'); export const keys = n(globalThis?.Array?.prototype, 'keys', 'value'); export const at = n(globalThis?.Array?.prototype, 'at', 'value'); export const get = n(globalThis?.WeakMap?.prototype, 'get', 'value'); export const set = n(globalThis?.WeakMap?.prototype, 'set', 'value'); -export const toFixed = n(globalThis?.Number?.prototype, 'toFixed', 'value') +export const toFixed = n(globalThis?.Number?.prototype, 'toFixed', 'value'); +export const destination = n(globalThis?.NavigateEvent?.prototype, 'destination', 'get'); +export const url = n(globalThis?.NavigationDestination?.prototype, 'url', 'get'); +export const preventDefault = n(globalThis?.Event?.prototype, 'preventDefault', 'value'); +export const stopPropagation = n(globalThis?.Event?.prototype, 'stopPropagation', 'value'); export { // window @@ -40,6 +46,7 @@ export { Function, Math, parseInt, WeakMap, Error, JSON, + navigation, // Object defineProperties, assign, getOwnPropertyDescriptor,