Add protected interaction evidence and measured debugging phases
This commit is contained in:
1 parent
ea448de2d4
commit
244f01630b
42 files changed
+640
-87
No files matched your search
@@ -1,11 +1,12 @@
|
||||
'use strict';
|
||||
const {randomUUID}=require('node:crypto');
|
||||
function createClipboardGuard({getContext,stage,validate,commit,discard,readClipboard,writeClipboard,inspect,audit,timeoutMs=10000}){
|
||||
function createClipboardGuard({getContext,stage,validate,commit,discard,readClipboard,writeClipboard,inspect,audit,onCompleted,timeoutMs=10000}){
|
||||
const targetFacts=value=>{if(!value||!['a','button','input','textarea','select','div','span','p','body','unknown'].includes(value.tag))return undefined;const target={tag:value.tag};if(['button','link','textbox','checkbox','radio','combobox','menuitem','option'].includes(value.role))target.role=value.role;if(['x','y'].every(k=>Number.isSafeInteger(value[k])&&value[k]>=0&&value[k]<=100000)) {target.x=value.x;target.y=value.y;}return target;};
|
||||
let pending=null;
|
||||
const current=c=>{const n=getContext(c.webContents);return n.ready&&n.tabId===c.tabId&&n.activation===c.activation&&n.policy===c.policy&&n.url===c.url&&n.navigationId===c.navigationId&&n.webContents===c.webContents&&n.webContents?.focusedFrame===n.webContents?.mainFrame&&Date.parse(n.policy?.expiresAt)>Date.now();};
|
||||
async function run(operation,webContents){
|
||||
const c=getContext(webContents),token=randomUUID();let text='',timer,expired=false;
|
||||
const denied=async ruleId=>{await audit('clipboard.block',ruleId);return{completed:false,ruleId};};
|
||||
const denied=async ruleId=>{await audit('clipboard.block',ruleId,{action:operation,sessionId:c.tabId,outcome:'denied'});return{completed:false,ruleId};};
|
||||
const work=async()=>{try{
|
||||
if(!['copy','cut','paste'].includes(operation)||!current(c))return denied('clipboard-context-denied');
|
||||
const limit=Math.min(c.policy.security?.clipboard?.maxTextBytes||0,c.policy.dlp.maxBytes);
|
||||
@@ -19,7 +20,7 @@ function createClipboardGuard({getContext,stage,validate,commit,discard,readClip
|
||||
if(!current(c)||expired)return denied('clipboard-context-changed');
|
||||
if(verdict?.decision!=='allow')return denied(verdict?.ruleId||'clipboard-inspection-failed');
|
||||
// Persist authorization before side effects; never audit raw clipboard text.
|
||||
await audit(`clipboard.${operation}`);
|
||||
await audit(`clipboard.${operation}`,undefined,{action:operation,sessionId:c.tabId,outcome:'authorized'});
|
||||
if(!current(c)||expired)return denied('clipboard-context-changed');
|
||||
const args={operation,token,url:c.url,text,limit,webContents:c.webContents};
|
||||
if(operation==='cut'){
|
||||
@@ -31,6 +32,10 @@ function createClipboardGuard({getContext,stage,validate,commit,discard,readClip
|
||||
const result=await commit(args);
|
||||
if(result?.completed!==true||!current(c)||expired)return denied('clipboard-target-changed');
|
||||
if(operation==='copy'){await writeClipboard(text);if(!current(c)||expired)return denied('clipboard-context-changed');}
|
||||
if(onCompleted && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(c.tabId||'')) {
|
||||
await onCompleted({action:operation,sessionId:c.tabId,pageUrl:c.url,outcome:'completed',sensitiveField:false,...(targetFacts(snapshot.target)?{target:targetFacts(snapshot.target)}:{}),bytes:Buffer.byteLength(text)});
|
||||
if(!current(c)||expired)return denied('clipboard-context-changed');
|
||||
}
|
||||
return {completed:true};
|
||||
}finally{try{await discard?.({token,webContents:c.webContents});}catch{}}};
|
||||
try{return await Promise.race([work(),new Promise(resolve=>{timer=setTimeout(()=>{expired=true;Promise.resolve(discard?.({token,webContents:c.webContents})).catch(()=>{});denied('clipboard-timeout').then(resolve,()=>resolve({completed:false,ruleId:'clipboard-timeout'}));},timeoutMs);})]);}
|
||||
|
||||
Reference in new issue
Block a user