Is Frontrun safe?
Frontrun is medium risk. When gmgn.ai pages make wallet-remark requests, Frontrun's background reads the Authorization header and device query. A later getGmgnAuth message reuses those values to POST to gmgn.ai's remark-wallet API with that Authorization.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
gmgn.ai authorization header reused for wallet remarks
When gmgn.ai pages make wallet-remark requests, Frontrun's background reads the Authorization header and device query.
A later getGmgnAuth message reuses those values to POST to gmgn.ai's remark-wallet API with that Authorization.
What actually happens
You browse gmgn.ai pages that make wallet-remark requests.
The watched URL prefix is gmgn.ai's Solana wallet remark endpoint.
Frontrun reads the request's Authorization value and device query string, then reuses them in a later remark-wallet request.
The follow-up request is initiated by the background message handler named getGmgnAuth.
Values the code carries from the watched request into the follow-up POST
- Authorization valueAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 (illustrative)
This represents your signed-in gmgn.ai session. Reusing it lets the follow-up request act with the same session context.
- Device query stringdevice_id=4f7c2b8a-0e41-4c0f-9b42-7a6d8a21c014&client_id=gmgn_web (illustrative)
This keeps the same device and request context from the gmgn.ai page request attached to the later POST.
- Wallet remark pair["8r3Xv1k7JmG9qQp3nV6sT2yA4bC5dE6fH7iJ8kL9mN0", "Primary wallet"] (illustrative)
The later request sends the wallet address and label selected by the extension code.
Captured request
The code that does this
The background service worker stores gmgn.ai authorization data and reuses it
Readable code: listener and POST handler
background.jsvar eQt = md(`GetGmgnAuthMessage`), tQt = `https://gmgn.ai/defi/quotation/v1/wallet/sol/remark_wallets`, nQt = `https://gmgn.ai/api/v1/remark_wallet`, rQt = null;function iQt(e) { let t = typeof e?.address == `string` ? e.address.trim() : void 0, n = typeof e?.primaryLabel == `string` ? e.primaryLabel : void 0; return !t || n === void 0 || t.length > 128 || n.length > 256 ? null : { address: t, primaryLabel: n }}chrome.webRequest.onBeforeSendHeaders.addListener(e => { let t = e.requestHeaders?.find(e => e.name.toLowerCase() === `authorization`), n = new URL(e.url).searchParams.toString(); return t?.value && n && (rQt = { authToken: t.value, deviceQuery: n }), { requestHeaders: e.requestHeaders }}, { urls: [`${tQt}*`]}, [`requestHeaders`]);var aQt = async (e, t) => { if (!EYt(e.sender)) { eQt.error(`Blocked GMGN sync from unauthorized sender`, { origin: V5(e.sender) }), t.send({ success: !1, error: `Unauthorized sender` }); return } let n = iQt(e.body); if (!n) { t.send({ success: !1, error: `Invalid GMGN sync payload` }); return } let r = rQt; if (!r) { t.send({ success: !1, error: `GMGN auth unavailable` }); return } try { let e = await fetch(`${nQt}?${r.deviceQuery}`, { method: `POST`, headers: { authorization: r.authToken, "content-type": `application/json` }, body: JSON.stringify({ chain: `sol`, remark_addresses: [ [n.address, n.primaryLabel] ] }) }); if (!e.ok) { eQt.warn(`GMGN remark request failed`, { status: e.status }), t.send({ success: !1, error: `GMGN sync failed (${e.status})` }); return } t.send({ success: !0 }) } catch (e) { eQt.error(`GMGN remark request errored`, e), t.send({ success: !1, error: L5(e, `gmgnSync`) }) }};Readable code: sender restriction and dispatch table
background.jsvar wYt = `https://gmgn.ai`, EYt = e => e?.origin === wYt ? !0 : !!e?.url?.startsWith(`${wYt}/`), V5 = e => e?.origin ?? `unknown`;var F5t = { "auth-with-google": YYt, auth: SYt, "auth-email-otp-send": UYt, "auth-email-otp-verify": KYt, "auth-email-otp-finalize": WYt, "base-trade": cZt, "base-transfer": vZt, "bnb-trading": CZt, "bridge-evm-origin": PZt, captureSentryEvent: IZt, checkJitoBundle: RZt, claimTurnkeyReauthPrompt: WZt, event: YZt, fetchTransactionDetails: ZZt, forwardLog: $Zt, getGmgnAuth: aQt, getInstallAttribution: sQt, getOnchainTokenBalance: uQt, getTokenDecimals: dQt, getTurnkeySecret: pQt, icon: gQt, "link-telegram": xQt, "link-twitter": EQt, mergeBscNonces: DQt, openSidepanel: Fit, openTabs: AQt, "perf-metrics-config": qQt, "perf-metrics-report": JQt, ping: YQt};Host receiving the follow-up request
- gmgn.ai
Receives the extension-initiated remark-wallet POST with the Authorization value copied from a prior gmgn.ai request.