Search code examples
javascriptelectronautofill

How to create a JavaScript auto-fill script for forms in an Electron WebView that bypasses validation issues?


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 = "[email protected]";
     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 = "[email protected]";
  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 = "[email protected]";
          
  // 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);

Solution

  • 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 = "[email protected]";      
    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();