Audit native transports and retain secure export diagnostics
This commit is contained in:
1 parent
8a253eda89
commit
0a5df7398e
28 files changed
+636
-53
No files matched your search
@@ -24,7 +24,7 @@ function deadline(operation){let timer;return Promise.race([operation,new Promis
|
||||
async function attachPageClipboardGuard(contents,onFailure){
|
||||
const api=contents.debugger;
|
||||
if(!api||api.isAttached()||owners.has(contents))throw Error('Page security initialization unavailable');
|
||||
const owner={api,healthy:false};
|
||||
const owner={api,healthy:false,onFailure};
|
||||
const detached=()=>{owners.delete(contents);owner.healthy=false;if(!contents.isDestroyed())onFailure();};
|
||||
owner.detached=detached;
|
||||
api.on('detach',detached);
|
||||
@@ -49,9 +49,41 @@ function guardedScreenshot(contents,parameters){
|
||||
}
|
||||
function disposePageClipboardGuard(contents){
|
||||
const owner=owners.get(contents);if(!owner)return;
|
||||
owner.releaseTransport?.();
|
||||
owners.delete(contents);owner.healthy=false;owner.api.removeListener('detach',owner.detached);
|
||||
if(owner.api.isAttached())owner.api.detach();
|
||||
}
|
||||
function createNativeTransportChannel(contents){
|
||||
const owner=owners.get(contents),api=owner?.api;
|
||||
if(!owner?.healthy||api!==contents.debugger||!api.isAttached()||owner.transportLease)throw Error('Private transport channel unavailable');
|
||||
const lease=Symbol('native-transport'),subscriptions=new Set(),targets=new Set();let released=false;
|
||||
owner.transportLease=lease;
|
||||
const healthy=()=>!released&&owners.get(contents)===owner&&owner.healthy&&owner.transportLease===lease&&!contents.isDestroyed()&&api.isAttached();
|
||||
const session=value=>value===undefined||typeof value==='string'&&value.length>0&&value.length<=128&&/^[a-zA-Z0-9._:-]+$/.test(value);
|
||||
const command=(method,parameters,sessionId)=>{if(!healthy()||!session(sessionId))return Promise.reject(Error('Private transport channel unavailable'));return api.sendCommand(method,parameters,sessionId);};
|
||||
const subscribe=(name,listener)=>{if(!healthy()||typeof listener!=='function')throw Error('Private transport subscription unavailable');api.on(name,listener);let removed=false;const remove=()=>{if(removed)return;removed=true;api.removeListener(name,listener);subscriptions.delete(remove);};subscriptions.add(remove);return remove;};
|
||||
const messages=new Set(['Target.attachedToTarget','Target.detachedFromTarget','Network.webTransportCreated','Network.webTransportConnectionEstablished','Network.webTransportClosed']);
|
||||
function release(){
|
||||
if(released)return;released=true;for(const remove of [...subscriptions])remove();
|
||||
if(owner.transportLease===lease){owner.transportLease=null;owner.releaseTransport=null;}
|
||||
// Release private observation without detaching the clipboard security owner.
|
||||
// Fresh workers may still be waiting for startup; resume before dropping targets.
|
||||
if(api.isAttached()){
|
||||
const send=(method,parameters,id)=>{try{return Promise.resolve(api.sendCommand(method,parameters,id));}catch{return Promise.reject(Error('Private transport cleanup unavailable'));}};
|
||||
const pending=[];for(const id of targets)pending.push(send('Runtime.runIfWaitingForDebugger',{},id));
|
||||
pending.push(send('Target.setAutoAttach',{autoAttach:false,waitForDebuggerOnStart:false,flatten:true}));
|
||||
void Promise.allSettled(pending).then(results=>{if(results.some(result=>result.status==='rejected')&&owners.get(contents)===owner&&owner.healthy&&!contents.isDestroyed())owner.onFailure();});
|
||||
}
|
||||
targets.clear();
|
||||
}
|
||||
owner.releaseTransport=release;
|
||||
return Object.freeze({isHealthy:healthy,
|
||||
enableNetwork:sessionId=>command('Network.enable',{maxPostDataSize:0},sessionId),
|
||||
autoAttachTargets:sessionId=>command('Target.setAutoAttach',{autoAttach:true,waitForDebuggerOnStart:true,flatten:true},sessionId),
|
||||
resumeTarget:sessionId=>sessionId===undefined?Promise.reject(Error('Private transport target required')):command('Runtime.runIfWaitingForDebugger',{},sessionId),
|
||||
subscribeMessage:listener=>subscribe('message',(event,method,params,sessionId)=>{if(!messages.has(method))return;if(method==='Target.attachedToTarget'&&session(params?.sessionId)&¶ms.sessionId!==undefined)targets.add(params.sessionId);if(method==='Target.detachedFromTarget')targets.delete(params?.sessionId);listener(event,method,params,sessionId);}),
|
||||
subscribeDetach:listener=>subscribe('detach',()=>listener()),release});
|
||||
}
|
||||
async function guardedVisualStructure(contents){
|
||||
const owner=owners.get(contents);
|
||||
if(!owner?.healthy||!owner.api.isAttached()||contents.isDestroyed())throw Error('Managed visual export refused');
|
||||
@@ -71,4 +103,4 @@ async function guardedVisualStructure(contents){
|
||||
}
|
||||
return {safe:true,nodes};
|
||||
}
|
||||
module.exports={attachPageClipboardGuard,guardedScreenshot,guardedVisualStructure,disposePageClipboardGuard};
|
||||
module.exports={attachPageClipboardGuard,guardedScreenshot,guardedVisualStructure,disposePageClipboardGuard,createNativeTransportChannel};
|
||||
Reference in new issue
Block a user