const {app}=require('electron'); const {spawn}=require('node:child_process'); const path=require('node:path'); function createCapabilityCrypto({packaged=false,resourcesPath,root=path.join(__dirname,'../..'),launch=spawn,environment=process.env}={}){ const python=packaged?path.join(resourcesPath,'python/python.exe'):path.join(root,'.runtime/windows/python/python.exe'); const script=packaged?path.join(resourcesPath,'inspector/dpapi.py'):path.join(root,'apps/inspector/dpapi.py'); const active=new Set(); return function convert(operation,input){ const inputLimit=operation==='protect'?32768:65536; if(!['protect','unprotect'].includes(operation)||!(typeof input==='string'||Buffer.isBuffer(input))||!Buffer.byteLength(input)||Buffer.byteLength(input)>inputLimit)return Promise.reject(Error('Capability rejected')); if(active.size>=8)return Promise.reject(Error('Windows capability protection failed')); return new Promise((resolve,reject)=>{ // Python startup hooks and inherited secrets must not enter a capability // helper. Isolated mode also disables user-site and Python environment paths. const env={};for(const key of ['SystemRoot','WINDIR','TEMP','TMP','USERPROFILE','LOCALAPPDATA','APPDATA'])if(environment[key])env[key]=environment[key]; let child,timer,settled=false,terminal=false,bytes=0; const output=Buffer.alloc(operation==='protect'?65536:32768),slot={};active.add(slot); function fail(){ if(settled)return;settled=true;clearTimeout(timer);output.fill(0); // A rejected operation does not free the slot until actual child close. if(child&&!terminal){try{child.kill();}catch{}child.stdin?.destroy?.();child.stdout?.destroy?.();child.stderr?.destroy?.();} reject(Error('Windows capability protection failed')); } try{ child=launch(python,['-I','-B',script,operation],{windowsHide:true,shell:false,stdio:['pipe','pipe','pipe'],env}); child.once('close',code=>{ terminal=true;active.delete(slot); if(settled)return;if(code!==0||!bytes){fail();return;} settled=true;clearTimeout(timer);const result=output.subarray(0,bytes).toString('utf8');output.fill(0);resolve(result); }); child.on('error',fail);child.stdin.on('error',fail);child.stdout.on('error',fail);child.stderr.on('error',fail);child.stderr.on('data',()=>{}); child.stdout.on('data',data=>{if(settled)return;if(!Buffer.isBuffer(data)||bytes+data.length>output.length){fail();return;}data.copy(output,bytes);bytes+=data.length;}); timer=setTimeout(fail,5000);child.stdin.end(input); }catch{if(!child)active.delete(slot);fail();} }); }; } let sharedConverter; function capabilityCrypto(operation,input){sharedConverter??=createCapabilityCrypto({packaged:app.isPackaged,resourcesPath:process.resourcesPath});return sharedConverter(operation,input);} module.exports={capabilityCrypto,createCapabilityCrypto};