Is Linguix: Grammar Checker and AI Writing App safe?
Linguix sends text fragments surrounding grammar corrections to Amplitude analytics on any website.
When a user accepts a grammar or writing suggestion, Linguix captures up to 100 characters of surrounding text (the 'context' field) along with the rule triggered and the replacement made. This data is transmitted to Amplitude via its HTTP API using a hardcoded project key. The user's Linguix account ID or a generated UUID is attached to each event as the Amplitude userId.
Who publishes itTextly Inc. - 1 other listing from the same operator, none carrying a finding
Textly Inc. - 1 other listing from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
1 other listing published from this account, 2k+ users between them, none of them carrying a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Typed writing context is sent to Amplitude after accepted suggestions
When you accept a Linguix suggestion, the extension logs a ReplacementClicked event to Amplitude.
Fields come from the rule, nearby context, replacement text, and source; the user ID comes from the Linguix account ID or a stored UUID.
What actually happens
You accept a suggested writing replacement on a webpage.
This can happen inside forms or editable page content where the extension is active.
The extension logs an analytics event that includes the nearby writing context.
The event is named ReplacementClicked and is routed through the Amplitude SDK.
Fields passed into the ReplacementClicked analytics event
- Rule that firedMORFOLOGIK_RULE_EN_US (illustrative)
Shows which writing rule matched the text you were editing.
- Nearby typed textPlease reviev the onboarding notes before Friday. (illustrative)
Can reveal part of what you were writing on the page when you accepted the suggestion.
- Replacement textreview (illustrative)
Shows the corrected word or phrase you chose from the suggestion popover.
- Analytics user identifier8f2c6b7e-41a6-4e28-9e5f-1d43d6a9c2b4 (illustrative)
Lets the analytics provider link the event to the same extension account or stored browser profile over time.
Captured request
The code that does this
The accepted replacement is converted into an Amplitude analytics event
Content script emits the replacement details
ext-content/main.jsreplace(t, n) { this.prevent(n), !this.authUser.actionRequired() || this.authUser.actionRequired() !== z.TRIAL_ENDED || this.authUser.isPremium() ? (this.authUser.actionRequired() && this.authUser.actionRequired() === z.TRIAL_ENDS_SOON && this.alert.isPremium && chrome.storage.local.set({ [z0]: this.premiumReplacesDoneToday + 1 }), this.checkable.replace(this.alert.id, t), this.auth.isGuest() && chrome.storage.local.get("guestReplacementCount").then(a => { chrome.storage.local.set({ guestReplacementCount: (a.guestReplacementCount || 0) + 1 }) }), chrome.storage.local.get("totalReplacementCount").then(a => { chrome.storage.local.set({ totalReplacementCount: (a.totalReplacementCount || 0) + 1 }) }), this.onMessage.emit({ type: s1, message: { sessionId: this.checkable.id, rule: this.alert.rule, context: this.alert.context, replacement: t, from: "alert", sentence: this.alert.wholeSentence } })) : this.goPremiumTrialExpired()}Background worker forwards the action to analytics
ext-background/main.jsthis.onLogReplacement$ = ge(() => this.withOnlineCheckerAction(Gu).pipe($e(({ action: M, checker: Y}) => { Y.ensureIsConnected(), Y.logReplacement(M.rule, M.context, M.replacement, M.sentence), Cr.replacementClickedEvent(M.rule, M.context, M.replacement, M.from)})), { dispatch: !1})Analytics wrapper maps fields into ReplacementClicked
ext-background/main.jsclass Cr { static init(n) { S0("050aec1607a54971d0794e2397b6979b", n) } static rephraseRequestedEvent(n, r, a, u) { Cr.logEvent("RephraseRequested", { type: n, limitReached: r, domain: a, isSimplifier: u }) } static replacementClickedEvent(n, r, a, u) { Cr.logEvent("ReplacementClicked", { rule: n, context: r, replacement: a, from: u }) } static logEvent(n, r) { Yi(n, r) }}Report helper bounds context around the flagged text
ext-background/main.jsconstructReport(n, r, a = 100) { if (!this.currentData) return; const p = this.currentData.text.split("\n")[n.lineNumber]; if (!p) return; const C = p.substring(Math.max(0, n.offset - a), n.offset + n.length + a); return { id: ts.generateUuid(), type: r, context: C }}External analytics destination
- api2.amplitude.com
Amplitude HTTP API endpoint that receives the ReplacementClicked analytics event.
Where it sends data
Destinations our analysis observed Linguix: Grammar Checker and AI Writing App 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.
- api2.amplitude.comwidely used
Linguix: Grammar Checker and AI Writing App sends data to api2.amplitude.com. A widely used service: 42 other extensions we have analyzed send data here.
