Harden Windows DLP enforcement and Electron runtime packaging

This commit is contained in:
Karl committed 2026-10-13 23:19:22 +02:00
1 parent e7d778aeac
commit 8a253eda89
120 files changed
+3569 -135

No files matched your search

+1 -1
View File
@@ -6,7 +6,7 @@ app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardware
app.whenReady().then(()=>{const requests=session.fromPartition('persist:safetynet-managed').webRequest,original=requests.onBeforeSendHeaders.bind(requests);requests.onBeforeSendHeaders=(filter,listener)=>original(filter,(details,callback)=>{if(details.url.endsWith('/public')){const entries=Object.entries(details.requestHeaders),one=entries.find(([name])=>name.toLowerCase()==='x-credential-one'),two=entries.find(([name])=>name.toLowerCase()==='x-credential-two'),known='fixtureControlledCredentialDelta0123456789';if(one&&two){headerFacts.push({names:entries.map(([name])=>name),fragmentOneIndex:entries.indexOf(one),fragmentTwoIndex:entries.indexOf(two),fullJoinMatch:entries.map(([,value])=>value).join('').includes(known),pairJoinMatch:(one[1]+two[1])===known,reversePairJoinMatch:(two[1]+one[1])===known});fs.writeFileSync(path.resolve('artifacts/controlled-login-header-facts.json'),JSON.stringify(headerFacts,null,2));}}if(fixtureCredentialHeader&&details.resourceType==='mainFrame'&&details.method==='POST')details.requestHeaders['X-Synthetic-Credential']='sk-syntheticControlledLoginCredential0123456789';listener(details,callback);});});require('../apps/browser/main.cjs');
const wait=ms=>new Promise(r=>setTimeout(r,ms)),base=process.env.SAFETYNET_SMOKE_SERVER,origin=process.env.SAFETYNET_SMOKE_ORIGIN,headers={authorization:`Bearer ${process.env.SAFETYNET_SMOKE_ADMIN}`,'content-type':'application/json'};
async function until(fn){for(let i=0;i<200;i++){const r=await fn();if(r)return r;await wait(100);}throw Error('Controlled login fixture timeout');}
app.whenReady().then(async()=>{const pages=[];let stage='startup';try{const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;session.fromPartition('persist:safetynet-managed').setCertificateVerifyProc((req,callback)=>callback(req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint?0:-2));const ui=async(method,value)=>{const result=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);if(!result.ok)throw Object.assign(Error('Native UI operation failed'),{operation:method,reason:/not managed|not ready|paused/i.test(result.error||'')?'not-ready':/context.*changed/i.test(result.error||'')?'context-changed':/BLOCKED_BY_CLIENT/i.test(result.error||'')?'native-request-blocked':/ERR_ABORTED/i.test(result.error||'')?'native-navigation-aborted':'ui-operation-refused'});return result.data;};const overview=async()=>(await fetch(base+'/api/admin/overview',{headers})).json();stage='enrollment';await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});const page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;pages.push(page);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login');const a=await ui('agentConnection','vault-a'),b=await ui('agentConnection','vault-b'),requestFile=path.join(app.getPath('userData'),'controlled-request.json');async function call(connection,action,args={}){fs.writeFileSync(requestFile,JSON.stringify({action,args}));const child=require('node:child_process').spawn(process.execPath,['scripts/agent-client.cjs','--connection',connection.credentialPath,'--request',requestFile],{windowsHide:true,stdio:['ignore','pipe','pipe']});let output='';child.stdout.on('data',v=>output+=v);child.stderr.on('data',()=>{});const exit=await new Promise(r=>{child.once('exit',r);child.once('error',()=>r(1));});return {exit,result:exit===0?JSON.parse(output).result:undefined};}const sessionId=(await call(a,'sessions')).result[0].id,master='Synthetic master password for controlled native proof',password='sk-syntheticControlledLoginCredential0123456789',username='public-native-user',alternatePassword='fixtureControlledCredentialDelta0123456789';stage='human unlock';await ui('vaultCreate',master);const saved=await ui('vaultSave',{origin,username,password,label:'Approved synthetic login'});const reset=async(mode='200')=>{await ui('navigate',origin+'/login/'+mode);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login/'+mode);await ui('vaultUnlock',master);win.focus();page.focus();await wait(100);};const stats=()=>new Promise((resolve,reject)=>{const req=require('node:https').get(origin+'/stats',{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},res=>{let body='';res.on('data',v=>body+=v);res.on('end',()=>resolve(JSON.parse(body)));});req.on('error',reject);});const inventory=async()=>{const r=await call(a,'vault:list',{sessionId});assert.equal(r.exit,0);return r.result[0].credentialRef;};const submit=async()=>{const terminal=new Promise((resolve,reject)=>{const timer=setTimeout(()=>{page.removeListener('did-stop-loading',done);reject(Error('Native form navigation terminal timeout'));},22000);const done=()=>{clearTimeout(timer);resolve();};page.once('did-stop-loading',done);});await page.executeJavaScript("document.querySelector('form').requestSubmit()");await terminal;await wait(200);};const prepare=async()=>{const ref=await inventory(),r=await call(a,'vault:prepare-submit',{sessionId,credentialRef:ref});assert.equal(r.exit,0,'Agent prepare failed');assert.deepEqual(r.result,{completed:true,prepared:true});return ref;};const count=async()=>{const result=await fetch(base+'/api/admin/overview',{headers});assert.equal(result.status,200);return (await result.json()).events.filter(e=>e.kind==='vault.submission-authorized').length;};
app.whenReady().then(async()=>{const pages=[];let stage='startup';try{const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;session.fromPartition('persist:safetynet-managed').setCertificateVerifyProc((req,callback)=>callback(req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint?0:-2));const ui=async(method,value)=>{const result=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);if(!result.ok)throw Object.assign(Error('Native UI operation failed'),{operation:method,reason:/not managed|not ready|paused/i.test(result.error||'')?'not-ready':/context.*changed/i.test(result.error||'')?'context-changed':/BLOCKED_BY_CLIENT/i.test(result.error||'')?'native-request-blocked':/ERR_ABORTED/i.test(result.error||'')?'native-navigation-aborted':'ui-operation-refused'});return result.data;};const overview=async()=>(await fetch(base+'/api/admin/overview',{headers})).json();stage='enrollment';await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});const page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;pages.push(page);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login');const a=await ui('agentConnection','vault-a'),b=await ui('agentConnection','vault-b'),requestFile=path.join(app.getPath('userData'),'controlled-request.json');async function call(connection,action,args={}){fs.writeFileSync(requestFile,JSON.stringify({action,args}));const child=require('node:child_process').spawn(process.execPath,['scripts/agent-client.cjs','--connection',connection.credentialPath,'--request',requestFile],{windowsHide:true,stdio:['ignore','pipe','pipe']});let output='';child.stdout.on('data',v=>output+=v);child.stderr.on('data',()=>{});const exit=await new Promise(r=>{child.once('exit',r);child.once('error',()=>r(1));});return {exit,result:exit===0?JSON.parse(output).result:undefined};}const sessionId=(await call(a,'sessions')).result[0].id,master='Synthetic master password for controlled native proof',password='sk-syntheticControlledLoginCredential0123456789',username='public-native-user',alternatePassword='fixtureControlledCredentialDelta0123456789';stage='human unlock';await ui('vaultCreate',master);const saved=await ui('vaultSave',{origin,username,password,label:'Approved synthetic login'});const reset=async(mode='200')=>{await ui('navigate',origin+'/login/'+mode);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login/'+mode);await ui('vaultUnlock',master);win.focus();page.focus();await wait(100);};const stats=()=>new Promise((resolve,reject)=>{const req=require('node:https').get(origin+'/stats',{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},res=>{let body='';res.on('data',v=>body+=v);res.on('end',()=>resolve(JSON.parse(body)));});req.on('error',reject);});const inventory=async()=>{const r=await call(a,'vault:list',{sessionId});assert.equal(r.exit,0);return r.result[0].credentialRef;};const submit=async()=>{const terminal=new Promise((resolve,reject)=>{const timer=setTimeout(()=>{page.removeListener('did-stop-loading',done);reject(Error('Native form navigation terminal timeout'));},22000);const done=()=>{clearTimeout(timer);resolve();};page.once('did-stop-loading',done);});await page.executeJavaScript("document.querySelector('form').requestSubmit()");await terminal;await wait(200);};const prepare=async()=>{const ref=await inventory(),r=await call(a,'vault:prepare-submit',{sessionId,credentialRef:ref});assert.equal(r.exit,0,'Agent prepare failed');assert.deepEqual(r.result,{completed:true,prepared:true});return ref;};
stage='ordinary fill remains DLP blocked';await reset();assert.equal((await call(a,'vault:fill',{sessionId,credentialRef:await inventory()})).exit,0);await submit();assert.equal((await stats()).submitted,0);
stage='agent prepare does not submit';await reset();let ref=await prepare();assert.equal((await stats()).submitted,0);assert.equal((await call(a,'vault:prepare-submit',{sessionId,credentialRef:ref})).exit,1);assert.equal((await call(b,'vault:prepare-submit',{sessionId,credentialRef:await inventory()})).exit,1);await submit();await until(()=>!page.isLoading()&&page.getURL()===origin+'/signin');assert.equal((await stats()).submitted,1);
stage='controlled302 safeGET';await reset('302');await prepare();await submit();await until(()=>!page.isLoading()&&page.getURL()===origin+'/final');assert.equal((await stats()).submitted,2);assert.equal((await stats()).finalGets,1);