import test from 'node:test';import assert from 'node:assert/strict';import {createRequire} from 'node:module';const require=createRequire(import.meta.url),{createCredentialSubmissionBroker}=require('../apps/browser/credential-submission.cjs'); function fixture(extra={}){let time=1000,current=true,generation=1;const frame={},wc={id:7,mainFrame:frame},vault={status:()=>({locked:false}),accessGeneration:()=>generation},policy={passwordManager:{controlledSubmission:{enabled:true,forms:[{id:'login',actionUrl:'https://example.com/session',usernameField:'user',passwordField:'pass'}]}}},context={policy,token:{},webContents:wc,mainFrame:frame,sourceURL:'https://example.com/login',actor:{id:'agent1'}};const broker=createCredentialSubmissionBroker({isCurrent:()=>current,now:()=>time,...extra}),body=Buffer.from('user=alice&pass=Strong%21Vault123¬e=public'),details={id:1,url:'https://example.com/session',method:'POST',resourceType:'mainFrame',webContentsId:7,frame,initiatorOrigin:'https://example.com',uploadData:[{bytes:body}],requestHeaders:{'Content-Type':'application/x-www-form-urlencoded','Accept':'text/html'}};const issue=()=>broker.issue({context,formId:'login',username:'alice',password:'Strong!Vault123',vault,vaultGeneration:1});return {broker,body,details,context,issue,expire:()=>time+=15001,revoke:()=>current=false,rotate:()=>generation++};} test('exact native submission residual inspects all other fields without credentials',()=>{const f=fixture();f.issue();const result=f.broker.claim(f.details,f.body,f.context);assert.equal(result.residual.includes(Buffer.from('alice')),false);assert.equal(result.residual.includes(Buffer.from('Strong')),false);assert.match(result.residual.toString(),/note=public/);assert.match(result.residual.toString(),/note\npublic/);assert.match(result.residual.toString(),/user\npass\nnote/);assert.throws(()=>f.broker.finalize(1,f.context,f.details));f.broker.validateHeaders(f.details,f.context);assert.equal(f.broker.finalize(1,f.context,f.details).authorized,true);assert.throws(()=>f.broker.finalize(1,f.context,f.details));f.broker.complete(1);assert.equal(f.broker.claim({...f.details,id:2},f.body,f.context),null);}); test('unrelated GET retains one-use prepared lease',()=>{const f=fixture();f.issue();assert.equal(f.broker.claim({...f.details,method:'GET',resourceType:'image'},Buffer.alloc(0),f.context),null);assert.ok(f.broker.claim(f.details,f.body,f.context));}); test('duplicate and malformed fields consume failed lease and remain blocked',()=>{for(const text of ['user=alice&pass=Strong%21Vault123&pass=x','user=alice&pass=Strong%ZZVault123','user=alice&pass=wrong','user=alice&pass=Strong%21Vault123¬e=Strong%21Vault123']){const f=fixture();f.issue();const bytes=Buffer.from(text),details={...f.details,uploadData:[{bytes}]};assert.throws(()=>f.broker.claim(details,bytes,f.context));assert.throws(()=>f.broker.claim(details,bytes,f.context));}}); test('request native frame, origin, policy and vault races fail closed',()=>{for(const change of [f=>f.details.frame={},f=>f.details.initiatorOrigin='https://evil.example',f=>f.context={...f.context,policy:{...f.context.policy}},f=>f.revoke(),f=>f.rotate()]){const f=fixture();f.issue();change(f);assert.throws(()=>f.broker.claim(f.details,f.body,f.context));}}); test('header content type, encoding, body mutation and embedded credentials fail closed',()=>{for(const change of [f=>f.details.requestHeaders['Content-Type']='application/json',f=>f.details.requestHeaders['Content-Encoding']='gzip',f=>f.details.requestHeaders['X-Secret']='prefix-Strong!Vault123-suffix',f=>f.details.uploadData=[{bytes:Buffer.from('modified')}],f=>f.revoke()]){const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);change(f);assert.throws(()=>f.broker.validateHeaders(f.details,f.context));assert.throws(()=>f.broker.finalize(1,f.context,f.details));}}); test('expiration, disable and redirect never grant replay exemption',()=>{const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);f.broker.validateHeaders(f.details,f.context);f.broker.finalize(1,f.context,f.details);f.broker.redirect(1);assert.throws(()=>f.broker.claim(f.details,f.body,f.context));const g=fixture();g.issue();g.expire();assert.equal(g.broker.claim(g.details,g.body,g.context),null);const h=fixture();h.context.policy.passwordManager.controlledSubmission.enabled=false;assert.throws(()=>h.issue());}); test('extra confidential fields remain intact in residual for normal DLP',()=>{const f=fixture();f.issue();const bytes=Buffer.from('user=alice&pass=Strong%21Vault123¬e=CORPORATE_SECRET&part1=CORPO&part2=RATE_SECRET');const r=f.broker.claim({...f.details,uploadData:[{bytes}]},bytes,f.context);assert.match(r.residual.toString(),/CORPORATE_SECRET/);assert.match(r.residual.toString(),/CORPORATE_SECRETCORPORATE_SECRET/);}); test('central disable and authority change immediately before release invalidate authorization',()=>{for(const change of [f=>f.context.policy.passwordManager.controlledSubmission.enabled=false,f=>f.revoke(),f=>f.rotate()]){const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);f.broker.validateHeaders(f.details,f.context);change(f);assert.throws(()=>f.broker.finalize(1,f.context,f.details));assert.throws(()=>f.broker.finalize(1,f.context,f.details));}}); function redirectFixture(){const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);f.broker.validateHeaders(f.details,f.context);f.broker.finalize(1,f.context,f.details);f.broker.redirect(1);f.details={...f.details,method:'GET',url:'https://example.com/home',uploadData:[],requestHeaders:{Accept:'text/html'}};return f;} test('302-style bodyless GET passes ordinary inspection without credential exemption',()=>{const f=redirectFixture();assert.equal(f.broker.has(1),false);assert.equal(f.broker.isBlocked(1),false);assert.equal(f.broker.validateRedirect(f.details,f.context),true);assert.equal(f.broker.claim(f.details,Buffer.alloc(0),f.context),null);assert.throws(()=>f.broker.finalize(1,f.context,f.details));f.broker.complete(1);assert.equal(f.broker.validateRedirect(f.details,f.context),false);}); test('307-style POST replay and redirected credential URL/header remain denied',()=>{for(const change of [f=>f.details.method='POST',f=>f.details.uploadData=[{bytes:f.body}],f=>f.details.url+='?secret=Strong%21Vault123',f=>f.details.requestHeaders['X-Value']='Strong!Vault123',f=>f.details.requestHeaders.accept='duplicate']){const f=redirectFixture();change(f);assert.throws(()=>f.broker.validateRedirect(f.details,f.context));assert.throws(()=>f.broker.claim(f.details,Buffer.alloc(0),f.context));}}); test('ordinary redirects do not become blocked and stale tombstones expire boundedly',()=>{const f=fixture();f.broker.redirect(999);assert.equal(f.broker.isBlocked(999),false);const g=redirectFixture();g.expire();assert.equal(g.broker.isBlocked(1),true);g.broker.complete(1);assert.equal(g.broker.isBlocked(1),false);}); test('duplicate header case and printable ASCII field names follow strict parsing',()=>{const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);f.details.requestHeaders['content-type']='application/x-www-form-urlencoded';assert.throws(()=>f.broker.validateHeaders(f.details,f.context));const g=fixture();g.context.policy.passwordManager.controlledSubmission.forms[0].usernameField='user[email]';g.context.policy.passwordManager.controlledSubmission.forms[0].passwordField='user[password]';g.issue();const bytes=Buffer.from('user%5Bemail%5D=alice&user%5Bpassword%5D=Strong%21Vault123');assert.ok(g.broker.claim({...g.details,uploadData:[{bytes}]},bytes,g.context));}); test('public background POST and XHR preserve prepared lease; credential XHR consumes and denies',()=>{const f=fixture();f.issue();const noise=Buffer.from('note=public');assert.equal(f.broker.claim({...f.details,id:2,url:'https://example.com/metrics',resourceType:'xhr',uploadData:[{bytes:noise}]},noise,f.context),null);assert.ok(f.broker.claim(f.details,f.body,f.context));const g=fixture();g.issue();assert.throws(()=>g.broker.claim({...g.details,resourceType:'xhr'},g.body,g.context));assert.equal(g.broker.isBlocked(1),true);assert.equal(g.broker.claim({...g.details,id:3},g.body,g.context),null);}); test('redirect checks invoke redirect authority phase and expiry timer wipes without traffic',()=>{const phases=[],f=fixture();let tick,cleared=false,time=1000;const broker=createCredentialSubmissionBroker({isCurrent:(_c,{phase})=>{phases.push(phase);return true;},now:()=>time,setIntervalImpl:callback=>{tick=callback;return {unref(){}};},clearIntervalImpl:()=>cleared=true});const issue=()=>broker.issue({context:f.context,formId:'login',username:'alice',password:'Strong!Vault123',vault:{status:()=>({locked:false}),accessGeneration:()=>1},vaultGeneration:1});issue();broker.claim(f.details,f.body,f.context);broker.validateHeaders(f.details,f.context);broker.finalize(1,f.context,f.details);broker.redirect(1);broker.validateRedirect({...f.details,method:'GET',url:'https://example.com/home',uploadData:[],requestHeaders:{}},f.context);assert.equal(phases.at(-1),'redirect');time+=15001;tick();assert.equal(broker.isBlocked(1),true);broker.close();assert.equal(cleared,true);assert.equal(broker.isBlocked(1),false);}); test('narrow creator cancel refuses cross-actor cancellation and blocks its pending request',()=>{const f=fixture();f.issue();f.broker.cancel({...f.context,actor:{id:'other'}});assert.ok(f.broker.claim(f.details,f.body,f.context));f.broker.cancel({...f.context,webContents:{id:7}});assert.equal(f.broker.has(1),true);f.broker.cancel(f.context);assert.equal(f.broker.has(1),false);assert.equal(f.broker.isBlocked(1),true);assert.throws(()=>f.broker.validateHeaders(f.details,f.context));const g=fixture();g.issue();g.broker.cancel(g.context);assert.equal(g.broker.claim(g.details,g.body,g.context),null);}); test('known credentials split across other fields cannot use the credential exemption',()=>{const f=fixture();f.issue();const bytes=Buffer.from('user=alice&pass=Strong%21Vault123&part1=Strong%21&part2=Vault123');assert.throws(()=>f.broker.claim({...f.details,uploadData:[{bytes}]},bytes,f.context));}); test('fixed internal diagnostic stages never replay or alter protected checks',()=>{for(const mode of ['before','after','twice']){const stages=[];const f=fixture({traceOperation:(operation,fn)=>{stages.push(operation);if(mode==='before')throw Error('Diagnostic failed');const result=fn();if(mode==='after')throw Error('Diagnostic failed');fn();return result;}});f.issue();const claim=f.broker.claim(f.details,f.body,f.context);assert.ok(claim);f.broker.validateHeaders(f.details,f.context);assert.equal(f.broker.finalize(1,f.context,f.details).authorized,true);for(const operation of ['credential.authority','credential.native','credential.parse','credential.match','credential.residual','credential.header-values'])assert.ok(stages.includes(operation),operation);assert.deepEqual(f.broker.complete(1),{released:true});assert.deepEqual(f.broker.complete(1),{released:false});}}); test('terminal released fact distinguishes no-release and finalized redirect completion',()=>{const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);assert.deepEqual(f.broker.complete(1),{released:false});const g=redirectFixture();assert.deepEqual(g.broker.complete(1),{released:true});assert.deepEqual(g.broker.complete(1),{released:false});}); test('credential split across headers remains denied in original and redirected requests',()=>{for(const redirect of [false,true]){const f=redirect?redirectFixture():fixture();if(!redirect){f.issue();f.broker.claim(f.details,f.body,f.context);}f.details.requestHeaders['X-Part1']='Strong!';f.details.requestHeaders['X-Part2']='Vault123';assert.throws(()=>redirect?f.broker.validateRedirect(f.details,f.context):f.broker.validateHeaders(f.details,f.context));}}); test('issued watch preserves public GET but consumes credential URL/header leak without exemption',()=>{for(const mode of ['query','header','split']){const f=fixture();f.issue();const d={...f.details,id:2,method:'GET',resourceType:'image',url:'https://example.com/image',uploadData:[],requestHeaders:{Accept:'image/png'}};assert.equal(f.broker.guardIssued(d,f.context),false);if(mode==='query')d.url+='?x=Strong%21Vault123';if(mode==='header')d.requestHeaders['X-Value']='Strong!Vault123';if(mode==='split'){d.requestHeaders['X-Part1']='Strong!';d.requestHeaders['X-Part2']='Vault123';}assert.throws(()=>f.broker.guardIssued(d,f.context));assert.equal(f.broker.isBlocked(2),true);assert.equal(f.broker.claim({...f.details,id:3},f.body,f.context),null);}}); test('issued watch rechecks live authority and invokes watch phase',()=>{let phase;const f=fixture({isCurrent:(_context,arg)=>{phase=arg.phase;return true;}});f.issue();assert.equal(f.broker.guardIssued({...f.details,id:2,method:'GET',url:'https://example.com/public',requestHeaders:{}},f.context),false);assert.equal(phase,'watch');const g=fixture();g.issue();g.revoke();assert.throws(()=>g.broker.guardIssued({...g.details,id:2,method:'GET',url:'https://example.com/public'},g.context));assert.equal(g.broker.isBlocked(2),true);}); test('issued watcher does not block an already claimed controlled request header phase',()=>{const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);assert.equal(f.broker.guardIssued(f.details,f.context),false);f.broker.validateHeaders(f.details,f.context);assert.equal(f.broker.finalize(1,f.context,f.details).authorized,true);}); test('public iframe requests preserve prepared lease while iframe credential URL is denied',()=>{const f=fixture();f.issue();const child={...f.details,id:2,resourceType:'subFrame',method:'GET',url:'https://example.com/embed',frame:{},uploadData:[],requestHeaders:{Accept:'text/html'}};assert.equal(f.broker.guardIssued(child,f.context),false);child.url+='?value=Strong%21Vault123';assert.throws(()=>f.broker.guardIssued(child,f.context));assert.equal(f.broker.isBlocked(2),true);assert.equal(f.broker.claim({...f.details,id:3},f.body,f.context),null);}); test('native reordered and interleaved two-fragment headers fail closed in all broker phases',()=>{for(const phase of ['issued','headers','redirect'])for(const reverse of [false,true]){const f=phase==='redirect'?redirectFixture():fixture();if(phase!=='redirect')f.issue();if(phase==='headers')f.broker.claim(f.details,f.body,f.context);f.details.requestHeaders={...(phase==='headers'?{'Content-Type':'application/x-www-form-urlencoded'}:{}),'X-Credential-One':reverse?'Vault123':'Strong!','sec-ch-ua-mobile':'?0','X-Credential-Two':reverse?'Strong!':'Vault123'};if(phase==='issued'){f.details.method='GET';f.details.url='https://example.com/image';assert.throws(()=>f.broker.guardIssued(f.details,f.context));}if(phase==='headers')assert.throws(()=>f.broker.validateHeaders(f.details,f.context));if(phase==='redirect')assert.throws(()=>f.broker.validateRedirect(f.details,f.context));}}); test('bounded percent-decoded pair checking rejects encoded split credentials',()=>{const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);f.details.requestHeaders={'Content-Type':'application/x-www-form-urlencoded','X-Second':'%56ault123','Accept':'text/html','X-First':'Strong%21'};assert.throws(()=>f.broker.validateHeaders(f.details,f.context));}); test('pair matching work and aggregate header size overflow fail closed',()=>{for(const mode of ['work','chars']){const f=fixture();f.issue();f.broker.claim(f.details,f.body,f.context);f.details.requestHeaders={'Content-Type':'application/x-www-form-urlencoded'};if(mode==='chars')f.details.requestHeaders['X-Large']='x'.repeat(131073);else for(let i=0;i<127;i++)f.details.requestHeaders['X-'+i]='%2541'+i;assert.throws(()=>f.broker.validateHeaders(f.details,f.context));}});