Is AI Grammar and Spell Checker by Ginger safe?

High risk

Ginger is high risk. Text you type into editable fields on any site is sent to api-extension.gingersoftware.com for correction. In testing, two canary phrases on w3schools.com appeared verbatim in POSTs to /correction/v1/document, plus a user identifier.…

Ginger Softwarev2.15.357Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Text you type in any web form is sent to Ginger's servers for grammar checking

Text you type into editable fields on any site is sent to api-extension.gingersoftware.com for correction.

In testing, two canary phrases on w3schools.com appeared verbatim in POSTs to /correction/v1/document, plus a user identifier.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You type text into a textarea, comment box, or other editable field on any website.

The extension did this

The content script copies the sentence and sends it in the body of a POST request to api-extension.gingersoftware.com.

This happens on every site, not only on Ginger's own pages. The extension also includes an identifier (the userIdentifier of a logged-in account, or an anonymous UUID) in the URL of the request.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://api-extension.gingersoftware.com/correction/v1/document?lang=US&apiKey=GingerWebSite&userIdentifier=fddbb18c-11c0-4238-9ab5-200676b5b94b
200 OK with a JSON GingerTheDocumentResult object listing zero corrections.
Headers
Originhttps://www.w3schools.com
Content-Typetext/plain
X-Reverso-Originginger.ext.chrome
Body
CANARYTEXT_GINGERBIRD_99887
03EvidenceFIELD TABLE
What gets sent on each typed sentence:
FieldValueWhy it matters
The sentence you typed
CANARYTEXT_GINGERBIRD_99887The exact text you entered into the field, sent in the body of the request.
Your Ginger user identifier
fddbb18c-11c0-4238-9ab5-200676b5b94bIf you are signed in, your account identifier travels with each sentence. If not, an anonymous UUID generated by the extension is used.
Dialect
USWhich English dialect the extension is configured for.
Client version
2.15.357Which version of the Ginger extension you have installed.
API origin marker
ginger.ext.chromeA header identifying the request as coming from the Ginger Chrome extension.
04EvidenceCODE COMPARE
The code that does this

The sentence-submission code in the content script.

What it actually does
What the code does in plain terms
// For each sentence the user types in any editable field on any site:
function submitSentence(sentence) {
  // Build the request URL with dialect, API key, and (if signed in) the user's Ginger account ID.
  let url = config.urlDocument                              // api-extension.gingersoftware.com/correction/v1/document
          + `?lang=${config.dialect}&apiKey=${config.apiKey}`;
  if (!isAnonymUser) {
    url += `&userIdentifier=${session.userId}`;             // logged-in user's ID is appended to the URL
  }
  // POST the raw sentence text as the request body.
  return ajax({
    url, method: 'POST',
    contentType: 'text/plain',
    data: sentence.text,                                    // <-- the raw user-typed text
    headers: { 'X-Reverso-Origin': 'ginger.ext.chrome' }
  });
}
05EvidenceTHIRD PARTY LIST
Where typed text is sent:
  • api-extension.gingersoftware.com

    Primary grammar/spelling endpoint operated by Ginger Software (the publisher). Receives the sentence text and a Ginger user identifier for every editable field typed into.

  • orthographe.reverso.net

    Alternate spelling endpoint used for some languages. Reverso is operated by Theadwise Corp. (Ginger and Reverso are part of the same group).

06EvidenceARTIFACT
Reproduce it yourself

Drop a canary phrase into a textarea on any page and watch your browser's network panel for an outbound POST to api-extension.gingersoftware.com containing the exact phrase in the request body.

RequiresChrome with the Ginger extension installed and activeDevTools open on the test page
ginger-sentence-canary.js · js
// ginger-sentence-canary.js
// Types a uniquely identifiable canary string into the first textarea
// on the page and dispatches input/change events so the Ginger content
// script's input watcher picks it up.
(function() {
  const canary = 'CANARYTEXT_GINGERBIRD_' + Math.floor(Math.random() * 1e6);
  const ta = document.querySelector('textarea, [contenteditable]');
  if (!ta) { console.warn('No editable field found on this page.'); return; }
  if (ta.tagName === 'TEXTAREA' || ta.tagName === 'INPUT') {
    ta.focus();
    ta.value = canary + '. This is a sentence that should trigger Ginger.';
    ta.dispatchEvent(new Event('input', { bubbles: true }));
    ta.dispatchEvent(new Event('change', { bubbles: true }));
  } else {
    ta.focus();
    ta.textContent = canary + '. This is a sentence that should trigger Ginger.';
    ta.dispatchEvent(new InputEvent('input', { bubbles: true }));
  }
  console.log('[ginger-canary] Planted:', canary);
  console.log('[ginger-canary] Now watch the Network panel for a POST to api-extension.gingersoftware.com containing this string in the request body.');
})();
How to run it
  1. 1
    Install Ginger, sign in or use anonymously.
  2. 2
    Open a page with a textarea (e.g. w3schools.com).
  3. 3
    DevTools Network tab, filter 'gingersoftware.com'.
  4. 4
    Run this script.
  5. 5
    Watch for a POST to /correction/v1/document; check the body.
SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Selected text sent to Ginger's rephrase server in the cleartext URL

Selecting text and using Rephrase places it into the query string of a request to rephrasesrv.gingersoftware.com, not the body.

Riding in the URL, it can persist in access logs.

A marker sentence was confirmed verbatim in the 's' parameter.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You select a sentence on a web page and click the Rephrase control.

The extension did this

The extension sends the selected text to rephrasesrv.gingersoftware.com with the text placed in the request URL itself.

The text is URL-encoded into the 's' query parameter rather than carried in a request body.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://rephrasesrv.gingersoftware.com/rephrase/rephrase?platform=chrome&clientVersion=2.15.357&apiKey=ChromeExtension&s=Please%20rephrase%20this%20sentence%20for%20me%20now.&size=
03EvidenceFIELD TABLE
What the rephrase request carries in its URL:
FieldValueWhy it matters
Your selected text
Please rephrase this sentence for me now.The exact sentence or passage you highlighted and asked to rephrase.
Account identifier
userIdentifier=8f21c4a7-0b3e-4d9a-bc12-7e5519a0d3f1A persistent userIdentifier added when you are signed in, tying the request to your account.
Client version
2.15.357Which version of the extension issued the request.
Platform and API key
platform=chrome, apiKey=ChromeExtensionStatic values identifying the client app to the server; the same for all users.
04EvidenceCODE COMPARE
The code that does this

The code that builds the request, from the extension's shipping content script.

What it actually does
Deobfuscated: the selection is encoded into the query string, not a body
function getRephrase(text) {
  const url = config.get('urlRephrase'); // rephrasesrv.gingersoftware.com/rephrase/rephrase
  const params = {
    platform: config.get('platform'),
    clientVersion: config.get('clientVersion'),
    apiKey: config.get('apiKey'),
    s: (text || selectionInfo.text).trim(),   // the selected text
    size: 8
  };
  if (!session.get('isAnonym')) {
    params.userIdentifier = session.get('userId'); // persistent account id
  }
  let fullUrl = url + '?';
  for (const k in params) {
    fullUrl += k + '=' + encodeURI(params[k]) + '&'; // text goes into the URL
  }
  fullUrl = fullUrl.slice(0, -1);
  return window.fetch(fullUrl, { cache: 'default' }).then(r => r.json());
}
SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Detailed analytics events sent to Google Analytics with a persistent client ID

The background worker sends GA4 events to google-analytics.com/mp/collect for most interactions.

Each includes a persistent client_id UUID, account status, event name; client_id, measurement ID, api_secret are hardcoded, reused per install.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You interact with the extension, for example, the login banner appears in the mini writer, you accept a correction, or you rephrase a sentence.

The extension did this

The worker POSTs a JSON event to google-analytics.com/mp/collect, including a persistent client_id, account status, event name, triggering surface, and version.

The Google Analytics measurement_id and api_secret are passed in the URL of every request, both hardcoded in the extension source.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://www.google-analytics.com/mp/collect?measurement_id=G-PYJPH0C6FG&api_secret=<redacted>
204 No Content, the Google Analytics Measurement Protocol acknowledges the event.
Headers
Originchrome-extension://kdfieneakcjfaiglcfcgkidlkmlijjnh
Content-Typetext/plain;charset=UTF-8
Body
{
  "client_id": "3b4e6544-69cc-4264-bb86-ba8f3983947e",
  "events": [
    {
      "name": "Login_display",
      "params": {
        "category": "Login",
        "trigger": "banner",
        "interface": "mini-writer",
        "engagement_time_msec": 1,
        "user_status": "free",
        "version": "2.15.357",
        "platform": "Windows",
        "browser": "chrome"
      }
    }
  ]
}
03EvidenceFIELD TABLE
Fields included in every analytics event:
FieldValueWhy it matters
Persistent client ID
3b4e6544-69cc-4264-bb86-ba8f3983947eA UUID identifying this install, reused across every event and session, so your interactions can be linked over time.
Account status
freeWhether you are anonymous (free), signed in (registered), or paying (premium).
Event name
Login_displayWhich interaction occurred, for example, displaying the login banner, accepting a correction, or opening a definition.
Event category
LoginA higher-level grouping for the event.
Trigger
bannerHow the action was initiated (button, banner, hotkey, etc.).
Interface
mini-writerWhich extension UI the action happened in (mini-writer, popup, full writer, etc.).
Extension version
2.15.357Which version of the Ginger extension you have installed.
Operating system
WindowsWhether the extension is running on a Mac or a Windows machine.
Browser platform
chromeA label identifying the browser family (chrome, edge, etc.), used by Ginger's analytics dashboards.
04EvidenceCODE COMPARE
The code that does this

The analytics submission code in the service worker.

What it actually does
What the function does in plain terms
// Sends one Google Analytics 4 event for an extension interaction.
function sendGA4Event(props) {
  // Hardcoded GA4 destination + credentials, both baked into the extension.
  const url =
    'https://www.google-analytics.com/mp/collect'
    + '?measurement_id=G-PYJPH0C6FG'
    + '&api_secret=<redacted>';

  // Hardcoded client_id (widgetUid) — same UUID across sessions.
  const client_id = 'f882d471-53f2-431f-8fe7-ad08beb3370c';

  // Map session.license to GA user_status.
  const user_status = !session            ? 'free'
                    :  session.isAnonym   ? 'free'
                    :  session.license !== 2 ? 'registered'
                                              : 'premium';

  const event = {
    name: props.event,                    // e.g. 'Login_display'
    params: {
      ...props,                           // category, trigger, interface, eventValue
      engagement_time_msec: 1,
      user_status,
      version: '2.15.357',
      platform: navigator.platform.includes('MAC') ? 'Mac' : 'Windows',
      browser:  'chrome',
    },
  };

  fetch(url, {
    method: 'POST',
    body: JSON.stringify({ client_id, events: [event] }),
  });
}
05EvidenceTHIRD PARTY LIST
Where the analytics events are sent:
  • www.google-analytics.com

    Receives the GA4 Measurement Protocol POSTs. The events are routed to Ginger Software's GA4 property (measurement_id G-PYJPH0C6FG).

Updated 17 September 2026kdfieneakcjfaiglcfcgkidlkmlijjnh