Is GFormTasker safe?

High risk

GFormTasker redirects Google Forms submissions to gformtasker.xyz with answers and form metadata.

When you submit a Google Form, GFormTasker adds hidden fields containing the original form URL, parsed question metadata, and extension version. It then changes the form action to gformtasker.xyz/submit, causing the browser to send the existing answers plus the added metadata to that endpoint instead of the original Google Forms action.

CMFamv0.3.5Chrome Web Store
72Risk

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Google Forms submissions redirect to gformtasker.xyz

On Google Forms pages, the extension changes the form's submission target to gformtasker.xyz/submit when submit is clicked, adding formUrl, formData, and gftVersion; formData is built from question metadata and saved per-question settings.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You submit a Google Form while the extension is active.

The extension did this

The extension changes where the form is posted and adds extra fields before submission.

The added fields include the original form URL, parsed question data, and the extension version.

Captured request
POSThttps://gformtasker.xyz/submit
Fields the content script adds or forwards during form submission
  • Your form answers
    entry.123456789=alice@example.com (illustrative)

    The answers already present in the Google Form are forwarded with the redirected form submission.

  • Original form URL
    https://docs.google.com/forms/d/e/1FAIpQLSeA1b2C3d4E5f6G7h8I9J0K/formResponse (illustrative)

    This identifies which Google Form was being submitted.

  • Question titles
    Work email address (illustrative)

    The extension includes labels parsed from the form page, which can reveal the subject of each answer.

  • Choice values
    Engineering, Sales, Finance (illustrative)

    For multiple-choice questions, the submitted metadata can include the available answer choices.

  • Randomization settings
    [123456789,"Department",true,["Engineering","Sales"],[50,50],"",2,["Engineering","Sales"]] (illustrative)

    The request can include whether the extension should randomize choices and the per-choice probability values.

  • Extension version
    0.3.5

    This lets the receiving service know which installed extension build sent the form.

The code that does this

The shipped content script builds metadata and redirects the form

Readable version

Readable equivalent of the option/value extraction helper

content/content.js
function extractOptionValues(questionEntry, formMetadata) {  const configuredValues = questionEntry[4];  const fieldType = formMetadata[3];  const gridValues = formMetadata[8];  if (fieldType !== 7) {    if (configuredValues == null) return [];    return Array.isArray(configuredValues) ? configuredValues : [configuredValues];  }  const rowValues = Array.isArray(configuredValues) && configuredValues.length > 0 ? configuredValues : [];  const columnValues = Array.isArray(gridValues) && gridValues.length > 0 ? gridValues : [];  if (rowValues.length && columnValues.length) return [...rowValues, ...columnValues];  return columnValues.length ? columnValues : rowValues;}

Readable equivalent of the submit-button click handler

content/content.js
const submitButton = findGoogleFormsSubmitButton();submitButton && submitButton.addEventListener('click', async (event) => {  if (document.querySelector('.RHiWt')) {    event.preventDefault();    return;  }  const formUrlInput = document.createElement('input');  formUrlInput.setAttribute('type', 'hidden');  formUrlInput.setAttribute('name', 'formUrl');  formUrlInput.setAttribute('value', form.action);  form.appendChild(formUrlInput);  document.querySelector('#g-form-tasker-submissionCount').setAttribute('name', 'submissionCount');  document.querySelector('#g-form-tasker-intervalMin').setAttribute('name', 'submissionIntervalMin');  document.querySelector('#g-form-tasker-intervalMax').setAttribute('name', 'submissionIntervalMax');  const entries = {};  const questionTitles = {};  const randomization = {};  const fieldTypes = {};  const parsedValues = {};  for (let index = 0; index < sessionStorage.length; index++) {    const key = sessionStorage.key(index);    if (key && key.startsWith('entry.')) {      const value = sessionStorage.getItem(key);      if (value) entries[key] = JSON.parse(value);    }    if (key && key.startsWith('q-entry.')) {      const value = sessionStorage.getItem(key);      if (value) questionTitles[key] = value;    }    if (key && key.startsWith('r-entry.')) {      const value = sessionStorage.getItem(key);      if (value) randomization[key] = value === 'true';    }    if (key && key.startsWith('t-entry.')) {      const value = sessionStorage.getItem(key);      if (value) fieldTypes[key] = parseInt(value);    }    if (key && key.startsWith('v-entry.')) {      const value = sessionStorage.getItem(key);      if (value) {        try {          parsedValues[key] = JSON.parse(value);        } catch (error) {          parsedValues[key] = [];        }      }    }  }  const probabilities = {};  for (let index = 0; index < sessionStorage.length; index++) {    const key = sessionStorage.key(index);    if (key && key.startsWith('prob.')) {      const [, entryId, choiceIndex] = key.split('.');      let normalizedEntryId = entryId;      if (entryId.includes('_row')) normalizedEntryId = entryId.replace('_row', '');      probabilities[normalizedEntryId] ||= {};      probabilities[normalizedEntryId][choiceIndex] = sessionStorage.getItem(key);    }  }  const otherText = {};  for (let index = 0; index < sessionStorage.length; index++) {    const key = sessionStorage.key(index);    if (key && key.startsWith('other-text.')) {      const entryId = key.replace('other-text.', '');      const value = sessionStorage.getItem(key);      if (value) otherText[entryId] = value;    }  }  const formData = [];  for (const key in entries) {    if (!Object.prototype.hasOwnProperty.call(entries, key)) continue;    const entryIdText = key.replace('entry.', '');    const entryId = parseInt(entryIdText);    const randomizationKey = `r-entry.${entryIdText}`;    const fieldTypeKey = `t-entry.${entryIdText}`;    const parsedValuesKey = `v-entry.${entryIdText}`;    const questionTitle = questionTitles[`q-entry.${entryIdText}`] || '';    const randomizeEnabled = randomization[randomizationKey] || false;    const fieldType = fieldTypes[fieldTypeKey] || 0;    const values = parsedValues[parsedValuesKey] || [];    const options = entries[key] || [];    const probabilityByChoice = probabilities[entryIdText] || {};    const otherResponse = otherText[entryIdText] || '';    const probabilityList = [];    for (let index = 0; index < options.length; index++) {      const probability = probabilityByChoice[index.toString()] || '0';      probabilityList.push(parseInt(probability));    }    formData.push([entryId, questionTitle, randomizeEnabled, options, probabilityList, otherResponse, fieldType, values]);  }  const formDataInput = document.createElement('input');  formDataInput.setAttribute('type', 'hidden');  formDataInput.setAttribute('name', 'formData');  formDataInput.setAttribute('value', JSON.stringify(formData));  form.appendChild(formDataInput);  const versionInput = document.createElement('input');  versionInput.setAttribute('type', 'hidden');  versionInput.setAttribute('name', 'gftVersion');  versionInput.setAttribute('value', extensionVersion);  form.appendChild(versionInput);  document.querySelectorAll('.g-form-tasker-switch').forEach((element) => element.remove());  form.action = 'https://gformtasker.xyz/submit';  form.submit();});
External host used by this claim
    • gformtasker.xyz

    Receives the redirected Google Forms submission at /submit, including original form fields plus formUrl, formData, and gftVersion added by the extension.

Where it sends data

Destinations our analysis observed GFormTasker contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • gformtasker.xyz

    GFormTasker sends data to gformtasker.xyz. No other extension we have analysed sends data here.

Updated 30 September 2026hfeaelngbkaldlhjiikhapjibjnocnap