Is Bookmarks to Notion safe?
Bookmarks to Notion sends Notion OAuth tokens and bookmark details to Amplitude analytics.
During connection setup, the extension sends Notion token data, including the access token and owner email, to Amplitude analytics. It also sends bookmark creation, change, move, and removal event details, including bookmark titles and URLs, to the same Amplitude endpoint.
Who publishes itBTN - 1 other listing from the same operator, none carrying a finding
BTN - 1 other listing 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
1 other listing published from this account, 458 users between them, none of them carrying a finding.
Shared hosts - 1 hostname
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
Notion OAuth token sent to Amplitude during setup
Connecting Notion, the extension accepts the connection result from bookmarkstonotion.com, log the message to Amplitude, store the token locally, and log that change.
Code puts access_token, email, + database details in Amplitude events.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You connect a Notion account to the extension.
The extension puts the Notion token message and saved connection record into Amplitude analytics events.
- Notion OAuth tokensecret_2d8f6c1e9a704cf5b7d3b4a9 (illustrative)
This can let another system act through the connected Notion integration until the grant is revoked.
- Notion owner emailalice@example.org (illustrative)
This ties the connection event to your Notion account identity.
- Saved connection recordconnections[0] with folders, token_info, and database_info
This shows which folders, token record, and database record are associated with your extension connection.
- Notion database ID8f1d9b0c4a8f4d2aa0b1c6e2f9012345 (illustrative)
This identifies the Notion database selected for bookmark syncing.
The connection handler logs the full request and saved connection value
Readable equivalent of the tracker setup
background.jsconst amplitudeEndpoint = "https://api2.amplitude.com/2/httpapi";const amplitude = createAmplitudeClient();const track = amplitude.track;Readable equivalent of the external message handler
background.jsasync function handleRuntimeOnMessageExternal(req, sender, sendResponse) { logger.debug("in handleRuntimeOnMessageExternal, req:", req); logger.debug("in handleRuntimeOnMessageExternal, sender:", sender); track("Background Runtime MessageExternal", { status: "begin", cmd: req.cmd, req }); let response = null; if (req.cmd === "from_web_token_save") { const connectionStage = await storageGet(null, "connection_stage"); if (!connectionStage?.folders) { logger.error("not found connection_stage"); response = { errcode: -1, errmsg: "not found connection_stage" }; } else if (!req.token_info?.access_token || !req.database_info?.id) { logger.error("req.token_info or req.token_info invalid"); response = { errcode: -1, errmsg: "token_info or database_info invalid" }; } else { const connections = (await storageGet(null, "connections")) || []; connections.push({ folders: connectionStage.folders, token_info: req.token_info, database_info: req.database_info }); await storageSet("connections", connections); await storageSet("connection_stage", null); response = { errcode: 0, errmsg: "save connection info succ" }; logger.debug("amplitude.setUserId:", req.token_info?.owner?.user?.person?.email); if (req.token_info?.owner?.user?.person?.email) { amplitudeSetUserId(req.token_info.owner.user.person.email); } } sendResponse(response); } else { logger.error("wrong req.msg"); response = { errcode: -1, errmsg: "wrong req.msg" }; sendResponse(response); } logger.debug("rsp:", response); track("Background Runtime MessageExternal", { status: "end", cmd: req.cmd, rsp: response });}Readable equivalent of the storage listener
background.jschrome.storage.onChanged.addListener(function(changes, namespace) { for (const [key, change] of Object.entries(changes)) { const oldValue = change.oldValue; const newValue = change.newValue; logger.debug(`"${key}" in "${namespace}" changed from "${oldValue}" to "${newValue}".`); track("Background Storage Change", { namespace, key, oldValue, newValue }); }});- api2.amplitude.com
Amplitude HTTP API receives the extension's analytics events, including the connection message and storage-change event properties shown in the code.
Bookmark titles and URLs sent to Amplitude
Creating, editing, moving, or removing a bookmark triggers the extension's listeners before Notion sync, passing event details to Amplitude: the bookmark object, changed title/URL, move metadata, or removal metadata as event properties.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You change a browser bookmark.
The extension sends the bookmark event details to Amplitude as analytics properties.
- Bookmark URLhttps://intranet.example.org/projects/q3-roadmap (illustrative)
This reveals the address of a page you saved in the browser.
- Bookmark titleQ3 Roadmap - Internal Wiki (illustrative)
This can reveal the subject of the saved page even when the URL is not descriptive.
- Bookmark ID742
This lets events about the same saved bookmark be linked over time.
- Folder and move detailsparentId 1, oldParentId 2, index 4 (illustrative)
This describes where a bookmark sits in your browser's saved-bookmark structure.
Bookmark event listeners forward bookmark metadata to Amplitude
Readable equivalent of the bookmark listeners
background.jschrome.bookmarks.onCreated.addListener(async function(id, bookmark) { track("Background Bookmarks Created", { status: "begin", id, bookmark }); await handleBookmarkCreated(id, bookmark);});chrome.bookmarks.onChanged.addListener(async function(id, changeInfo) { track("Background Bookmarks Changed", { status: "begin", id, changeInfo }); await handleBookmarkChanged(id, changeInfo);});chrome.bookmarks.onMoved.addListener(async function(id, moveInfo) { track("Background Bookmarks Moved", { status: "begin", id, moveInfo }); await handleBookmarkMoved(id, moveInfo);});chrome.bookmarks.onRemoved.addListener(async function(id, removeInfo) { track("Background Bookmarks Removed", { status: "begin", id, removeInfo }); await handleBookmarkRemoved(id, removeInfo);});- api2.amplitude.com
Amplitude HTTP API receives analytics events from the bookmark listeners shown in the code.
What it can do
Permissions this extension asks for, as declared in version 2.3.1. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on bookmarkstonotion.com
https://*.bookmarkstonotion.com/
Read and change your data on api.notion.com
https://api.notion.com/
Store data in your browser
storage
Read and change your bookmarks
bookmarks
Where it sends data
Destinations our analysis observed Bookmarks to Notion 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.
- api2.amplitude.comwidely used
Bookmarks to Notion sends data to api2.amplitude.com. A widely used service: 41 other extensions we have analysed send data here.