Files
safetynet/apps/browser/site-preload.cjs
T

50 lines
4.3 KiB
JavaScript
Raw Normal View History

2026-10-13 10:33:31 +02:00
const {ipcRenderer}=require('electron');
if(process.sandboxed!==true||process.contextIsolated!==true){ipcRenderer.send('managed:clipboard-sandbox-failure');window.stop();throw Error('Managed renderer sandbox and isolation are required');}
// Native shortcuts and guarded menus are the only supported clipboard transfer paths.
// Prevent page handlers/legacy execCommand from replacing approved plaintext with hidden data.
// Freeze the legacy command primitive in the page realm. Document.open() removes
// DOM listeners, but cannot replace this non-configurable prototype property.
try {
require('electron').contextBridge.executeInMainWorld({func:()=>{
const prototype=Document.prototype,existing=Object.getOwnPropertyDescriptor(prototype,'execCommand');
if(existing?.value?._managedClipboardGuard===true&&!existing.configurable)return;
const original=prototype.execCommand,apply=Reflect.apply,test=RegExp.prototype.test.bind(/^(copy|cut|paste)/i);
function managedClipboardCommand(command,...args){
if(typeof command!=='string'||test(command))return false;
return apply(original,this,[command,...args]);
}
Object.defineProperty(managedClipboardCommand,'_managedClipboardGuard',{value:true});
Object.defineProperty(prototype,'execCommand',{value:managedClipboardCommand,writable:false,configurable:false});
// Replacement clears security listeners; these unsupported legacy APIs fail closed.
const SecurityException=DOMException;
const denyReplacement=()=>{throw new SecurityException('Managed document replacement is disabled','SecurityError');};
for(const name of ['open','write','writeln'])Object.defineProperty(prototype,name,{value:denyReplacement,writable:false,configurable:false});
}});
} catch {
require('electron').ipcRenderer.send('managed:clipboard-sandbox-failure');
window.stop();
throw Error('Managed clipboard primitive could not be protected');
}
for(const name of ['copy','cut','paste','dragstart','dragover','drop'])window.addEventListener(name,event=>{event.preventDefault();event.stopImmediatePropagation();},true);
window.addEventListener('beforeinput',event=>{if(/^(insertFromPaste|insertFromDrop|deleteByCut)/.test(event.inputType||'')){event.preventDefault();event.stopImmediatePropagation();}},true);
let visualEnabled=ipcRenderer.sendSync('managed:visual-policy').enabled;
const documentToken=typeof crypto.randomUUID==='function'?crypto.randomUUID():Array.from(crypto.getRandomValues(new Uint8Array(16)),value=>value.toString(16).padStart(2,'0')).join('');let visualRevision=0,visualApproved=!visualEnabled,visualScheduled=false;
ipcRenderer.on('managed:visual-config',(_event,input)=>{visualEnabled=input.enabled===true;visualApproved=!visualEnabled;visualRevision++;visualChanged();ipcRenderer.send('managed:visual-configured',input.token);});
function visualChanged(){
if(!visualEnabled)return;visualRevision++;visualApproved=false;
if(visualScheduled)return;visualScheduled=true;
setTimeout(async()=>{visualScheduled=false;const revision=visualRevision,url=location.href;
try{const result=await ipcRenderer.invoke('managed:visual-check',{token:documentToken,revision,url});if(revision===visualRevision&&url===location.href)visualApproved=result.decision==='allow';}catch{visualApproved=false;}
if(!visualApproved&&revision===visualRevision)setTimeout(visualChanged,1000);
},150);
}
for(const name of ['keydown','beforeinput','paste','submit','pointerdown'])window.addEventListener(name,event=>{
if(!visualEnabled||visualApproved)return;
const element=event.target;
if(name==='submit'||element?.closest?.('input,textarea,[contenteditable="true"],button[type="submit"]')){event.preventDefault();event.stopImmediatePropagation();visualChanged();}
},true);
// This isolated-world preload exposes no bridge or automation capability to sites.
let scheduled=false,last='';
function inspect(){scheduled=false;const text=(document.body?.innerText||'').slice(0,250000);if(text===last)return;last=text;ipcRenderer.send('managed:page-content',{text});}
function schedule(){if(!scheduled){scheduled=true;setTimeout(inspect,350);}}
window.addEventListener('DOMContentLoaded',()=>{schedule();visualChanged();new MutationObserver(()=>{schedule();visualChanged();}).observe(document,{childList:true,subtree:true,characterData:true,attributes:true});});