Is Boomerang for Gmail safe?
Boomerang for Gmail is medium risk. Inbox Pause reads Gmail's GMAIL_AT cookie, its anti-CSRF token, granting account authority over internal endpoints incl. the Inbox-Paused filter. Testing confirmed it stays on Google's domains, but get_at_variable is publicly exposed too.…
Who publishes itBAYDIN INC. - 3 other listings from the same operator, none carrying a finding
BAYDIN INC. - 3 other listings 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
3 other listings published from this account, 25k+ 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
Reads Gmail's CSRF cookie to call Gmail's internal API as you
Inbox Pause reads Gmail's GMAIL_AT cookie, its anti-CSRF token, granting account authority over internal endpoints incl. the Inbox-Paused filter.
Testing confirmed it stays on Google's domains, but get_at_variable is publicly exposed too.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You click Pause Inbox (or Boomerang otherwise needs to manage the Inbox-Paused filter).
Boomerang reads Gmail's anti-CSRF cookie from document.cookie and uses it to call Gmail's internal filter-management API as you, installing the Inbox-Paused label-filter.
The cookie itself stays on Google domains in this build, it is used only to talk to mail.google.com, but the extension does extract it into a JavaScript variable that any future code path could ship anywhere.
- GMAIL_AT cookie valueAF6bupPl26NI-Sp2H2U4wrG3iGIUnioygg
Gmail's anti-CSRF token. Whoever holds it can call Gmail's internal endpoints with full account authority: install filters, modify labels.
- Gmail "ik" value11abc2def3
A second internal Gmail identifier the extension also reads from page state to construct authenticated calls.
- Authenticated Gmail filter requestPOST https://mail.google.com/mail/u/0/?ik=11abc2def3&at=AF6bupPl26NI-Sp2H2U4wrG3iGIUnioygg&view=up&act=df&pcd=1&mb=0&rt=c
The extension uses your token to POST to Gmail's filter endpoint, installing the Inbox-Paused-* label-filter Boomerang depends on.
- Public exposure on api objectapi.get.b4g_get_at_variable() // returns the live GMAIL_AT
This cookie-reader is also exposed as api.get.b4g_get_at_variable. Future or third-party code calling it gets your Gmail token.
The cookie reader, the api-object exposure, and the authenticated Gmail call.
What's actually happening
// Pull Gmail's anti-CSRF token straight out of document.cookie.function getGmailAt() { const start = document.cookie.indexOf('GMAIL_AT=') + 9; const end = document.cookie.indexOf(';', document.cookie.indexOf('GMAIL_AT')); return document.cookie.substring(start, end);}// Use it (plus the in-page "ik" value) to act as you against Gmail's// undocumented filter-management endpoint, installing/checking the// Inbox-Paused-* filter that the Pause Inbox feature depends on.const url = `${gmailBaseUrl}ik=${getGmailIk()}&at=${getGmailAt()}&view=up&act=df&pcd=1&mb=0&rt=c`;fetch(url, { method: 'POST', body: 'tfi=none&' });// Also exposed publicly on the bookmarklet's api object:api.get.b4g_get_at_variable = getGmailAt;Dynamic analysis instrumented `document.cookie` reads and observed 216 reads in a single session (every Inbox-Pause-related action triggers them). A SQL grep of the captured outbound traffic — `SELECT COUNT(*) FROM requests WHERE url LIKE '%b4g.baydin%' AND body LIKE '%GMAIL_AT%'` — returned 0 rows. The token is used only to call mail.google.com itself in this version.
That is the safer outcome. The risk is structural: the value is yanked into a JS string and exposed on a public `api.get.b4g_get_at_variable` function. Any future code path (or an attacker-controlled script that finds its way onto a Gmail tab and can call into Boomerang's window-scope api) that ships the result of that function is a one-line patch away from full Gmail-account takeover-level credential exposure.
Every interaction sent to Google Analytics with a per-user UUID
Boomerang fires a GA event for nearly every action (~307 types), each carrying a stable localStorage UUID (b4g_ga_cid) that lets GA correlate everything you do.
Events POST from the background worker, so page-level blockers miss them.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You click anything Boomerang touches, Pause Inbox, Schedule Send, opening Compose, the OAuth dialog.
The background service worker POSTs a Google Analytics event with your stable per-user UUID, the action you took, and a label.
Because the request comes from the extension's service worker (not the Gmail page), browser tracker-blockers running in the page do not see or block it.
204 No Content (GA Measurement Protocol acknowledges receipt). 3 such POSTs captured during dynamic analysis, covering: opening Gmail (automatic_oauth_dialog/show), the OAuth login attempt (automatic_oauth_dialog/login_attempt), and the resulting flow (oauth_flow/login). The same client_id UUID appears on every event, that is the stable cross-session identifier.
- Origin
- chrome-extension://mdanidgdpmkimeiiojknlnekblgmpdll
- Content-Type
- text/plain;charset=UTF-8
{ "client_id": "48285cd7-e1b9-4da4-89a1-c1617887469f", "events": [ { "name": "automatic_oauth_dialog", "params": { "action": "show", "label": "first_open" } } ]}- client_id (your stable UUID)48285cd7-e1b9-4da4-89a1-c1617887469f
A persistent random ID created on first run, stored in localStorage, and attached to every analytics event until you clear site storage.
- events[].name (event category)automatic_oauth_dialog
The Boomerang feature class you interacted with. The bookmarklet emits ~307 distinct names, covering essentially every UI action.
- events[].params.actionshow
What you did within that feature.
- events[].params.labelfirst_open
Optional sub-context for the action, feature variant, source, etc.
- measurement_id (in URL)G-48B3G8LRPQ
Identifies the developer's GA4 property. Hardcoded in the extension and visible to every user on every event.
- api_secret (in URL)uIAXRk5NR3m2Mef57TbpxQ
The credential GA uses to authorize the POST, hardcoded and visible to every user; see Claim 5870 for the security implication.
From the page → content script hop → background SW → Google Analytics POST.
End-to-end flow
// 1. Page-side code in the bookmarklet does:window.postMessage({type: 'B4G_TRACK_EVENT', trackedEventData: ['oauth-flow', 'login']}, '*');// 2. The b4g.js content script forwards it to the background SW.// 3. The background SW assigns the persistent UUID and POSTs to GA.// From your perspective the request originates from the extension, not the page,// so any blocker that only filters page requests will not see it.fetch(`https://www.google-analytics.com/mp/collect` + `?measurement_id=G-48B3G8LRPQ&api_secret=<redacted>`, { method: 'POST', body: JSON.stringify({ client_id: localStorage.b4g_ga_cid, // your stable per-user UUID events: [{ name: 'oauth_flow', params: { action: 'login' } }] }) });This string is your per-user identifier in GA across every Boomerang interaction, surviving restarts, recreated only if you clear storage.
- Location
- Page localStorage on mail.google.com, key b4g_ga_cid
48285cd7-e1b9-4da4-89a1-c1617887469f- www.google-analytics.com
Receives Measurement Protocol POSTs to /mp/collect, credited to GA4 property G-48B3G8LRPQ. Google processes the data under its standard terms; Baydin Inc is the controller.
Your Gmail address is sent to b4g.baydin.com on every page open and click
Boomerang reads your Gmail address on every Gmail load and sends it to b4g.baydin.com as guser, automatically and on every feature used.
It's the vendor's user ID and, being in the URL, lands in access logs too.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You open Gmail (or click any Boomerang button).
Boomerang reads your full Gmail address out of the page header and tacks it onto a request to its own server as the "guser" parameter.
It happens automatically the moment Gmail loads, no Boomerang button click required for the first request.
Captured automatically on Gmail load with no Boomerang button clicked. The live test account address robertfinwitch@gmail.com appears URL-encoded in the query string. A second capture, GET /mailcruncher/checklogin2?guser=robertfinwitch%40gmail.com&includecsrf=True, fires the moment Pause Inbox is clicked.
- Accept
- */*
- Origin
- https://mail.google.com
- Referer
- https://mail.google.com/
- /mailcruncher/hasseentutorial?guser=robertfinwitch%40gmail.com&image=True
Fires automatically on Gmail load to check if you've seen Boomerang's tutorial. Your address goes out before you touch anything.
- /mailcruncher/checklogin2?guser=robertfinwitch%40gmail.com&includecsrf=True
Fires on Pause Inbox click. Tells the vendor you are an active Boomerang user under this Gmail address.
- checkIfHasSeenAnnouncementsAndFTUEsPOST guser=robertfinwitch%40gmail.com
Polls server for in-product announcements scoped to your specific email address.
- schedulesend/return/recurring?guser=robertfinwitch%40gmail.com
Sends your address whenever you schedule, snooze, or set up a recurring email, links every scheduled-mail action to your identity.
- sendlater, boomerangfrom* endpoints?guser=robertfinwitch%40gmail.com
Sent on every Boomerang/send-later from a conversation. Builds a record of which messages you delay and when.
- /tr/track-sender-open?guser=robertfinwitch%40gmail.com
Open-tracking endpoint. Includes your address so opens you triggered are tied back to you, not just the recipient.
- dialog/subs/insights/login/contactshttps://b4g.baydin.com/insights/insightsfromgmail?guser=robertfinwitch%40gmail.com
All UI links Boomerang opens for you carry your address in the URL, useful for the vendor's tracking, exposed in browser history.
How the address is extracted, and a representative endpoint that ships it.
What the scrape does, in plain terms
// Walk the Gmail header, look for an email-shaped string in any aria-label.// Fall back to the browser tab title if the header has no match.function getGmailAddress() { for (const a of document.querySelectorAll('header a[aria-expanded]')) { const m = /[\w.+\-]+@[\w.\-]+\.\w+/.exec(a.getAttribute('aria-label')); if (m) return m[0]; } for (const w of document.title.split(' ')) { const m = /[\w.+\-]+@[\w.\-]+\.\w+/.exec(w); if (m) return m[0]; } return '';}// Then every server call looks like this:fetch(`https://b4g.baydin.com/mailcruncher/<endpoint>?guser=${encodeURIComponent(getGmailAddress())}&...`);- b4g.baydin.com
Boomerang's primary backend (Baydin Inc). Receives your Gmail address as guser= on every feature use, plus the tutorial check on load. It's the vendor's primary user identifier.
Live-tails Chrome's net log for any request to b4g.baydin.com that contains a guser= parameter. Lets you confirm in your own browser that your Gmail address is leaving on every Boomerang interaction.
- bash
- Google Chrome with --log-net-log support
#!/usr/bin/env bash# watch-baydin-guser.sh — see your Gmail address leaving in real time.## Usage: launch Chrome with --log-net-log=/tmp/netlog.json --net-log-capture-mode=IncludeSensitive# then run this script. Open Gmail with Boomerang installed and click around.set -euo pipefailLOG="${1:-/tmp/netlog.json}"until [[ -s "$LOG" ]]; do sleep 1; donetail -F "$LOG" | grep --line-buffered -oE 'https://b4g\.baydin\.com/[^"]*guser=[^&"]*' | while read -r url; do echo "$(date -u +%FT%TZ) $url"done- 1Quit Chrome.
- 2Relaunch: google-chrome --log-net-log=netlog.json --net-log-capture-mode=IncludeSensitive.
- 3Run ./watch-baydin-guser.sh netlog.json.
- 4Open Gmail: hasseentutorial?guser=YOUR_EMAIL appears within seconds, unclicked.
What it can do
Permissions this extension asks for, as declared in version 1.9.1. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 1.9.5, which we have not unpacked yet.
See, disable and uninstall your other extensions, including your security ones
management
Act on the current tab, but only after you click the extension
activeTab