From d9fca1e6493302486aacfd4530dafb2209c413b3 Mon Sep 17 00:00:00 2001 From: Perry Mitchell Date: Tue, 26 Mar 2024 20:54:13 +0200 Subject: [PATCH 1/2] Remove username field guessing --- source/LoginTarget.ts | 20 +++++++------- source/inputs.ts | 62 +++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 49 deletions(-) diff --git a/source/LoginTarget.ts b/source/LoginTarget.ts index 79b0e51..bba2fbe 100644 --- a/source/LoginTarget.ts +++ b/source/LoginTarget.ts @@ -48,7 +48,7 @@ export class LoginTarget extends EventEmitter { [LoginTargetFeature.Form]: null }; protected _forceSubmitDelay: number = FORCE_SUBMIT_DELAY; - protected _form: HTMLFormElement | null = null; + protected _form: HTMLFormElement | HTMLDivElement | null = null; protected _otpField: HTMLInputElement | null = null; protected _passwordField: HTMLInputElement | null = null; protected _submitButton: HTMLElement | null = null; @@ -100,7 +100,7 @@ export class LoginTarget extends EventEmitter { this._forceSubmitDelay = delay; } - set form(form: HTMLFormElement) { + set form(form: HTMLFormElement | HTMLDivElement) { if (form) { this._form = form; this._listenForUpdates(LoginTargetFeature.Form, form); @@ -228,13 +228,15 @@ export class LoginTarget extends EventEmitter { * @returns A promise that resolves once submission has been completed */ async submit(force: boolean = false): Promise { - if (!this.submitButton) { + if (this.form.tagName.toLowerCase() !== "form" && this.submitButton) { + // Click button + this.submitButton.click(); + } else if (!this.submitButton) { // No button, just try submitting - this.form.submit(); - return Promise.resolve(); + (this.form as HTMLFormElement).submit(); + } else { + throw new Error("Invalid form: Not form element and no valid submit button"); } - // Click button - this.submitButton.click(); if (force) { await this._waitForNoUnload(); } @@ -313,9 +315,9 @@ export class LoginTarget extends EventEmitter { }); }) ]); - if (!hasUnloaded) { + if (!hasUnloaded && this.form.tagName.toLowerCase() === "form") { // No unload events detected, so we need for force submit - this.form.submit(); + (this.form as HTMLFormElement).submit(); } } } diff --git a/source/inputs.ts b/source/inputs.ts index 65445d0..a35d16f 100644 --- a/source/inputs.ts +++ b/source/inputs.ts @@ -9,7 +9,7 @@ import { import { LocustInputEvent } from "./LocustInputEvent.js"; export interface FetchedForm { - form: HTMLFormElement; + form: HTMLFormElement | HTMLDivElement; usernameFields: Array; otpFields: Array; passwordFields: Array; @@ -77,32 +77,27 @@ function fetchForms(queryEl: Document | HTMLElement = document): Array { - let usernameFields = fetchUsernameInputs(formEl); - const passwordFields = fetchPasswordInputs(formEl); - const otpFields = fetchOTPInputs(formEl); - if (otpFields.length > 0 && passwordFields.length === 0) { - // No password fields, so filter out any OTP fields from the potential username fields - usernameFields = usernameFields.filter(field => otpFields.includes(field) === false); - } - const form: FetchedForm = { - form: formEl, - usernameFields, - passwordFields, - otpFields, - submitButtons: fetchSubmitButtons(formEl) - }; - if (form.usernameFields.length <= 0 && otpFields.length <= 0) { - const input = guessUsernameInput(formEl); - if (input) { - form.usernameFields.push(input); - } - } - return form; - }) - .filter((form) => form.otpFields.length + form.passwordFields.length + form.usernameFields.length > 0); +export function fetchFormsWithInputs(queryEl: Document | HTMLElement = document): Array { + return fetchForms(queryEl).reduce((output: Array, formEl: HTMLFormElement | HTMLDivElement) => { + let usernameFields = fetchUsernameInputs(formEl); + const passwordFields = fetchPasswordInputs(formEl); + const otpFields = fetchOTPInputs(formEl); + if (otpFields.length > 0 && passwordFields.length === 0) { + // No password fields, so filter out any OTP fields from the potential username fields + usernameFields = usernameFields.filter(field => otpFields.includes(field) === false); + } + const form: FetchedForm = { + form: formEl, + usernameFields, + passwordFields, + otpFields, + submitButtons: fetchSubmitButtons(formEl) + }; + if (form.usernameFields.length <= 0 && otpFields.length <= 0 && passwordFields.length <= 0) { + return output; + } + return [...output, form]; + }, []); } function fetchOTPInputs(queryEl: Document | HTMLElement = document): Array { @@ -129,19 +124,6 @@ function fetchUsernameInputs(queryEl: Document | HTMLElement = document): Array< return sortFormElements(inputs, "username"); } -function guessUsernameInput(formEl: HTMLFormElement): HTMLInputElement | null { - const elements = /^form$/i.test(formEl.tagName) - ? [...formEl.elements] - : [...formEl.querySelectorAll("input")]; - const possibleInputs = elements.filter((el) => { - if (el.tagName.toLowerCase() !== "input") return false; - if (["email", "text"].indexOf(el.getAttribute("type")) === -1) return false; - if (/pass(word)?/.test(el.outerHTML)) return false; - return true; - }); - return possibleInputs.length > 0 ? possibleInputs[0] as HTMLInputElement : null; -} - function isInput(el: Element): boolean { return el.tagName?.toLowerCase() === "input"; } From 72dd520113a1a85c5cd0811ae8c953671be4d3a2 Mon Sep 17 00:00:00 2001 From: Perry Mitchell Date: Tue, 26 Mar 2024 21:25:54 +0200 Subject: [PATCH 2/2] Fix submit order --- source/LoginTarget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/LoginTarget.ts b/source/LoginTarget.ts index bba2fbe..aea6f7a 100644 --- a/source/LoginTarget.ts +++ b/source/LoginTarget.ts @@ -228,10 +228,10 @@ export class LoginTarget extends EventEmitter { * @returns A promise that resolves once submission has been completed */ async submit(force: boolean = false): Promise { - if (this.form.tagName.toLowerCase() !== "form" && this.submitButton) { + if (this.submitButton) { // Click button this.submitButton.click(); - } else if (!this.submitButton) { + } else if (this.form.tagName.toLowerCase() === "form") { // No button, just try submitting (this.form as HTMLFormElement).submit(); } else {