Is AI Grammar and Spell Checker by Ginger safe?
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.…
Who publishes itGinger Software - no other listings under this identity, 6 shared hostnames
Ginger Software - no other listings under this identity, 6 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 6 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
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.
You type text into a textarea, comment box, or other editable field on any website.
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.
| Origin | https://www.w3schools.com |
| Content-Type | text/plain |
| X-Reverso-Origin | ginger.ext.chrome |
CANARYTEXT_GINGERBIRD_99887
| Field | Value | Why it matters | |
|---|---|---|---|
The sentence you typed | CANARYTEXT_GINGERBIRD_99887 | The exact text you entered into the field, sent in the body of the request. | |
Your Ginger user identifier | fddbb18c-11c0-4238-9ab5-200676b5b94b | If you are signed in, your account identifier travels with each sentence. If not, an anonymous UUID generated by the extension is used. | |
Dialect | US | Which English dialect the extension is configured for. | |
Client version | 2.15.357 | Which version of the Ginger extension you have installed. | |
API origin marker | ginger.ext.chrome | A header identifying the request as coming from the Ginger Chrome extension. |
The sentence-submission code in the content script.
// 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' }
});
}- 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).
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.
// 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.');
})();- 1Install Ginger, sign in or use anonymously.
- 2Open a page with a textarea (e.g. w3schools.com).
- 3DevTools Network tab, filter 'gingersoftware.com'.
- 4Run this script.
- 5Watch for a POST to /correction/v1/document; check the body.
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.
You select a sentence on a web page and click the Rephrase control.
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.
| Field | Value | Why 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-7e5519a0d3f1 | A persistent userIdentifier added when you are signed in, tying the request to your account. | |
Client version | 2.15.357 | Which version of the extension issued the request. | |
Platform and API key | platform=chrome, apiKey=ChromeExtension | Static values identifying the client app to the server; the same for all users. |
The code that builds the request, from the extension's shipping content script.
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());
}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.
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 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.
| Origin | chrome-extension://kdfieneakcjfaiglcfcgkidlkmlijjnh |
| Content-Type | text/plain;charset=UTF-8 |
{
"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"
}
}
]
}| Field | Value | Why it matters | |
|---|---|---|---|
Persistent client ID | 3b4e6544-69cc-4264-bb86-ba8f3983947e | A UUID identifying this install, reused across every event and session, so your interactions can be linked over time. | |
Account status | free | Whether you are anonymous (free), signed in (registered), or paying (premium). | |
Event name | Login_display | Which interaction occurred, for example, displaying the login banner, accepting a correction, or opening a definition. | |
Event category | Login | A higher-level grouping for the event. | |
Trigger | banner | How the action was initiated (button, banner, hotkey, etc.). | |
Interface | mini-writer | Which extension UI the action happened in (mini-writer, popup, full writer, etc.). | |
Extension version | 2.15.357 | Which version of the Ginger extension you have installed. | |
Operating system | Windows | Whether the extension is running on a Mac or a Windows machine. | |
Browser platform | chrome | A label identifying the browser family (chrome, edge, etc.), used by Ginger's analytics dashboards. |
The analytics submission code in the service worker.
// 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] }),
});
}- www.google-analytics.com
Receives the GA4 Measurement Protocol POSTs. The events are routed to Ginger Software's GA4 property (measurement_id G-PYJPH0C6FG).
What it can do
Permissions this extension asks for, as declared in version 2.15.357. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every site you visit
https://*/ and 1 more
See the address and title of every tab you have open
tabs
Keep running in the background while your browser is open
background
Read and change cookies, including the ones that keep you signed in
cookies
Store data in your browser
storage