I'm working on an Electron application with a WebView component that renders various websites. My objective is to develop an auto-fill script similar to a password manager for forms within these websites.
While the script successfully populates the form fields, I've encountered an issue with form validation upon submission. Despite my attempts to bypass validation by dispatching events, the validation still fails in some cases.
For instance, when executing the following script on console of the login page of Dropbox (https://www.dropbox.com/login), the input field gets updated, but upon clicking "continue," validation errors occur:
setInterval(() => {
const element = document.querySelector('form input');
if(!element || element.value !== "") return;
console.log('element founded')
element.value = "testemail@email.com";
element.dispatchEvent(new Event("input", { bubbles: true }));
element.dispatchEvent(new Event("change", { bubbles: true }));
}, 1000);
In my Electron application, I've attempted to execute the script within the WebView using "executeJavascript" method, but having same result of execute on console:
const webview = webviewRef.current
webview.executeJavaScript(`
setInterval(() => {
const element = document.querySelector('form input');
if(!element || element.value !== "") return;
console.log('element founded')
element.value = "testemail@email.com";
element.dispatchEvent(new Event("input", { bubbles: true }));
element.dispatchEvent(new Event("change", { bubbles: true }));
}, 1000);`)
The script successfully works in a Chrome web extension, where I added it to the "content scripts" (you can check it here).
Is there a reliable method to simulate user input effectively to bypass form validations within an Electron application's WebView? Any insights or alternative approaches would be greatly appreciated. Thank you!
Edit 1:
Also tried to emulate a user input and focus/blur events, but had same results:
const element = document.querySelector('form input');
console.log('element founded')
const textToType = "testemail@email.com";
// Simulate focus event
element.focus();
element.dispatchEvent(new Event('focus', { bubbles: true }));
let index = 0;
const typingInterval = setInterval(() => {
const char = textToType[index];
element.value += char; // Simulate typing by appending one character at a time
element.dispatchEvent(new Event('input', { bubbles: true })); // Dispatch input event
element.dispatchEvent(new Event("change", { bubbles: true }));
index++;
if (index === textToType.length) {
clearInterval(typingInterval); // Stop when all characters are typed
// Simulate blur event after typing
element.blur();
element.dispatchEvent(new Event('blur', { bubbles: true }));
}
}, 100);
I tried myself to see what was going wrong in that scenario and the goal was achieved by invoking the value
original setter (from the HTMLInputElement) instead of using the specific setter.
For the sake of completeness, the way I retrieved the original setter of the value
property was using the getOwnPropertyDescriptor Object static method, that:
returns an object describing the configuration of a specific property on a given object
such an object is a record having attributes including set
that holds the setter function.
Then I invoked such function over the specific input element object by using its call method that
calls this function with a given this value and arguments provided individually.
I must assume that the setter for that input was changed to intercept malicious intentions and became part of the validation strategy.
const textToType = "testemail@email.com";
var element = document.querySelector('form input');
//gets the value property setter from the original HTMLInputElement
var originalValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
//invokes the original setter over the specific element
originalValueSetter.call(element, textToType);
//fires the change event on the input element
element.dispatchEvent(new Event('change', { bubbles: true }));
//clicks the continue button...
document.querySelector('button.email-submit-button').click();