'use strict'; // Runs only in the isolated main-frame world. No arbitrary selectors or scripts from callers. function fillDocument(expectedUrl,origin,username,password,expiresAt){ if(Date.now()>=expiresAt||window.top!==window||!window.isSecureContext||location.href!==expectedUrl||location.origin!==origin)throw Error('Login destination changed'); const visible=element=>{ const style=getComputedStyle(element); return !element.disabled&&!element.readOnly&&!element.closest('[hidden],[inert],[aria-hidden="true"]')&&style.display!=='none'&&style.visibility!=='hidden'&&Number(style.opacity)!==0&&element.getClientRects().length>0; }; const passwords=[...document.querySelectorAll('input[type="password"]')].filter(element=>visible(element)&&element.autocomplete!=='new-password'); if(passwords.length!==1)throw Error('A single visible login password field is required'); const target=passwords[0],form=target.form; if(!form||form.method.toLowerCase()!=='post'||!['','_self'].includes(form.target.toLowerCase()))throw Error('A same-window POST login form is required'); const action=new URL(form.action||location.href,location.href); if(action.protocol!=='https:'||action.origin!==origin||action.username||action.password)throw Error('Login form sends to a different destination'); for(const control of [...(form.elements||form.querySelectorAll('button,input'))]){ if(control.disabled||typeof control.getAttribute!=='function')continue; const actionOverride=control.getAttribute('formaction'),methodOverride=control.getAttribute('formmethod'),targetOverride=control.getAttribute('formtarget'); if(actionOverride!==null){const destination=new URL(actionOverride||location.href,location.href);if(destination.protocol!=='https:'||destination.origin!==origin||destination.username||destination.password)throw Error('Login submit destination differs');} if(methodOverride!==null&&methodOverride.toLowerCase()!=='post')throw Error('Login submit method differs'); if(targetOverride!==null&&!['','_self'].includes(targetOverride.toLowerCase()))throw Error('Login submit window differs'); } const inputs=[...form.querySelectorAll('input')].filter(element=>visible(element)&&['text','email'].includes(element.type)); const named=inputs.filter(element=>element.autocomplete==='username'||/^(?:username|user|email|login)$/i.test(element.name||element.id)); const candidates=named.length?named:inputs; if(username&&candidates.length!==1)throw Error('A single login username field is required'); const user=username?candidates[0]:null; if(user&&user.form!==form)throw Error('Login fields belong to different forms'); const setter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; // Recheck inside the target renderer, after queued native dispatch. A hidden // or blurred document must not receive credentials after a tab switch. if(document.visibilityState!=='visible'||!document.hasFocus())throw Error('Login tab is no longer foreground'); // Set both values before dispatching events: page handlers cannot move focus between writes. if(user)setter.call(user,username);setter.call(target,password); if(user)user.dispatchEvent(new Event('input',{bubbles:true})); target.dispatchEvent(new Event('input',{bubbles:true})); return {completed:true}; } function fillScript(expectedUrl,credential,expiresAt=Date.now()+10000){ const url=new URL(expectedUrl); if(url.protocol!=='https:'||url.username||url.password||url.origin!==credential.origin)throw Error('Saved login requires its exact HTTPS origin'); if(!Number.isFinite(expiresAt)||expiresAt<=Date.now())throw Error('Password policy expired'); return `(${fillDocument.toString()})(${JSON.stringify(url.href)},${JSON.stringify(credential.origin)},${JSON.stringify(credential.username)},${JSON.stringify(credential.password)},${JSON.stringify(expiresAt)})`; } module.exports={fillDocument,fillScript};