Files
safetynet/scripts/password-focus-smoke.cjs
2026-10-13 10:34:48 +02:00

31 lines
3.3 KiB
JavaScript

'use strict';
if(!process.versions.electron){
const {spawnSync}=require('node:child_process');const env={...process.env};delete env.ELECTRON_RUN_AS_NODE;
const result=spawnSync(require('electron'),[__filename],{env,stdio:'inherit',windowsHide:true,timeout:45000});process.exit(result.status??1);
}else{
const {app,BrowserWindow,WebContentsView,session}=require('electron');
const fs=require('node:fs'),path=require('node:path'),https=require('node:https'),assert=require('node:assert/strict');
const {X509Certificate}=require('node:crypto');const {fillScript}=require('../apps/browser/password-fill.cjs');
app.enableSandbox();app.disableHardwareAcceleration();
app.whenReady().then(async()=>{let server,win;const views=[];try{
const cert=fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),key=fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/key.pem'));
const fingerprint=new X509Certificate(cert).fingerprint256;
server=https.createServer({cert,key},(_req,res)=>{res.setHeader('content-type','text/html');res.end('<!doctype html><form method="post" action="/signin"><input name="username" autocomplete="username"><input type="password" autocomplete="current-password"></form>');}).listen(0,'127.0.0.1');await new Promise(r=>server.once('listening',r));
const origin=`https://127.0.0.1:${server.address().port}`,ses=session.fromPartition('password-focus-proof');
// Trust only this public synthetic certificate on loopback, in this fixture.
ses.setCertificateVerifyProc((request,callback)=>callback(request.hostname==='127.0.0.1'&&new X509Certificate(request.certificate.data).fingerprint256===fingerprint?0:-2));
win=new BrowserWindow({show:true,width:700,height:500});
for(let i=0;i<2;i++){const v=new WebContentsView({webPreferences:{session:ses,nodeIntegration:false,contextIsolation:true,sandbox:true}});views.push(v);win.contentView.addChildView(v);v.setBounds({x:0,y:0,width:700,height:500});await v.webContents.loadURL(origin+'/login');}
const a=views[0],b=views[1],credential={origin,username:'fixture-user',password:'synthetic-focus-proof-password'};
const execute=()=>a.webContents.executeJavaScriptInIsolatedWorld(999,[{code:fillScript(origin+'/login',credential)}],false);
b.setVisible(false);a.setVisible(true);win.focus();a.webContents.focus();
await new Promise(r=>setTimeout(r,200));assert.equal((await execute()).completed,true);
await a.webContents.executeJavaScript('document.querySelectorAll("input").forEach(e=>e.value="")');
a.setVisible(false);b.setVisible(true);b.webContents.focus();await new Promise(r=>setTimeout(r,200));
await assert.rejects(execute());
assert.equal(await a.webContents.executeJavaScript('document.querySelector("input[type=password]").value'),'');
fs.mkdirSync(path.join(__dirname,'../artifacts'),{recursive:true});fs.writeFileSync(path.join(__dirname,'../artifacts/password-focus-proof.json'),JSON.stringify({actualNativeViews:true,foregroundAllowed:true,hiddenBlurredFillDenied:true,noCredentialWriteToHiddenTab:true},null,2));
console.log('Native password foreground proof passed.');
}catch(error){console.error(error.message);process.exitCode=1;}finally{for(const v of views)if(!v.webContents.isDestroyed())v.webContents.close({waitForBeforeUnload:false});win?.destroy();if(server)await new Promise(r=>{server.closeAllConnections();server.close(r);});app.exit(process.exitCode||0);}});
}