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.
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.
| Field | Value | Why it matters | |
|---|---|---|---|
Authorization value | Authorization: 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 string | device_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. |
The background service worker stores gmgn.ai authorization data and reuses it
var 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`)
})
}
};var 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
};- gmgn.ai
Receives the extension-initiated remark-wallet POST with the Authorization value copied from a prior gmgn.ai request.