2026-10-13 10:33:31 +02:00
import test from 'node:test' ;
import assert from 'node:assert/strict' ;
import vm from 'node:vm' ;
import { createRequire } from 'node:module' ;
2026-10-13 21:00:41 +02:00
const require = createRequire ( import . meta . url ),{ fillScript , prepareFillScript } = require ( '../apps/browser/password-fill.cjs' );
2026-10-13 10:33:31 +02:00
const credential = { origin : 'https://work.example' , username : 'employee' , password : 'fixture-only-generated-password' };
function fixture ({ url = 'https://work.example/login' , action = 'https://work.example/signin' , method = 'post' , target = '' , visible = true , frame = false , multiple = false , override , focused = true , visibility = 'visible' } = {}){
class Input { constructor ( type ){ this . type = type ; this . name = type === 'password' ? 'password' : 'username' ; this . autocomplete = '' ; this . disabled = false ; this . readOnly = false ;} set value ( value ){ this . stored = value ;} get value (){ return this . stored ;} closest (){ return null ;} getClientRects (){ return visible ? [{}] : [];} dispatchEvent (){}}
const user = new Input ( 'text' ), password = new Input ( 'password' );
2026-10-13 21:00:41 +02:00
const form = { action , method , target , enctype : 'application/x-www-form-urlencoded' , querySelectorAll : ()=>[ user , password ]}; user . form = form ; password . form = form ;
2026-10-13 10:33:31 +02:00
form . elements = override ? [ user , password ,{ getAttribute : name => override [ name ] ?? null }] : [ user , password ];
const window = { isSecureContext : true }; window . top = frame ? {} : window ;
2026-10-13 21:00:41 +02:00
return { user , password , context : vm . createContext ({ window , location : { href : url , origin : new URL ( url ). origin }, document : { hasFocus : ()=> focused , visibilityState : visibility , querySelectorAll : ()=> multiple ? [ password , new Input ( 'password' )] : [ password ]}, getComputedStyle : element =>({ display : 'block' , visibility : 'visible' , opacity : element . _opacity ?? '1' }), HTMLInputElement : Input , Event : class {}, URL })};
2026-10-13 10:33:31 +02:00
}
test ( 'explicit fill sets exact-origin visible login fields without submitting' ,()=>{
const f = fixture (); assert . equal ( vm . runInContext ( fillScript ( 'https://work.example/login' , credential ), f . context ). completed , true ); assert . equal ( f . user . stored , credential . username ); assert . equal ( f . password . stored , credential . password );
});
test ( 'HTTP, subdomains and alternate ports cannot reuse saved credentials' ,()=>{
for ( const url of [ 'http://work.example/login' , 'https://other.work.example/login' , 'https://work.example:8443/login' ]) assert . throws (()=> fillScript ( url , credential ));
});
test ( 'navigation, frames, cross-origin forms, GET, new-window, hidden and ambiguous targets fail before writing' ,()=>{
for ( const options of [{ url : 'https://work.example/changed' },{ frame : true },{ action : 'https://evil.example/capture' },{ action : 'http://work.example/login' },{ method : 'get' },{ target : '_blank' },{ visible : false },{ multiple : true },{ override : { formaction : 'https://evil.example/capture' }},{ override : { formmethod : 'get' }},{ override : { formtarget : '_blank' }}]){
const f = fixture ( options ); assert . throws (()=> vm . runInContext ( fillScript ( 'https://work.example/login' , credential ), f . context )); assert . equal ( f . user . stored , undefined ); assert . equal ( f . password . stored , undefined );
}
});
test ( 'expired policy cannot inject a password' ,()=>{ assert . throws (()=> fillScript ( 'https://work.example/login' , credential , Date . now () - 1 ));});
test ( 'queued fill cannot write into a hidden or blurred target document' ,()=>{
for ( const options of [{ focused : false },{ visibility : 'hidden' }]){ const f = fixture ( options ); assert . throws (()=> vm . runInContext ( fillScript ( 'https://work.example/login' , credential ), f . context ), /foreground/ ); assert . equal ( f . user . stored , undefined ); assert . equal ( f . password . stored , undefined );}
});
2026-10-13 21:00:41 +02:00
const approved = [{ id : 'company-login' , actionUrl : 'https://work.example/signin' , usernameField : 'username' , passwordField : 'password' }];
test ( 'transparent ancestors and section-prefixed new-password fields cannot receive saved login secrets' ,()=>{ for ( const mutate of [ f => f . password . parentElement = { _opacity : '0' }, f => f . password . autocomplete = 'section-login NEW-PASSWORD' , f => f . password . closest = ()=>({ disabled : true })]){ const f = fixture (); mutate ( f ); assert . throws (()=> vm . runInContext ( fillScript ( 'https://work.example/login' , credential ), f . context )); assert . equal ( f . password . stored , undefined );}});
test ( 'controlled fill reports only an approved form ID without submitting or returning secrets' ,()=>{ const f = fixture (), result = vm . runInContext ( prepareFillScript ( 'https://work.example/login' , credential , approved ), f . context ); assert . deepEqual ( JSON . parse ( JSON . stringify ( result )),{ completed : true , formId : 'company-login' }); assert . equal ( f . password . stored , credential . password ); assert . equal ( JSON . stringify ( result ). includes ( credential . password ), false );});
test ( 'controlled fill rejects changed action, encoding, duplicated fields and submit overrides before writing' ,()=>{
for ( const mutate of [ f => f . password . form . action = 'https://work.example/other' , f => f . password . form . enctype = 'multipart/form-data' , f => f . password . form . elements . push ({ name : 'password' }), f => f . password . form . elements . push ({ getAttribute : n => n === 'formaction' ? 'https://work.example/other' : null }), f => f . password . form . elements . push ({ getAttribute : n => n === 'formenctype' ? 'text/plain' : null })]){ const f = fixture (); mutate ( f ); assert . throws (()=> vm . runInContext ( prepareFillScript ( 'https://work.example/login' , credential , approved ), f . context )); assert . equal ( f . password . stored , undefined );}
});