Is Mendeley Web Importer safe?
Mendeley Web Importer sends analytics with your IP address and device fingerprint to New Relic, Adobe Analytics, and Mendeley.
When you use the extension to import academic articles, it collects telemetry that includes your public IP address (fetched from api.ipify.org), screen resolution, browser language, timezone, and the URL of the page you are viewing. These details are transmitted to three third-party analytics endpoints: Elsevier's Adobe Analytics (elsevier.sc.omtrdc.net), New Relic (insights-collector.newrelic.com), and Mendeley's own backend (brxt.mendeley.com). The IP is cached for 60 minutes and included in events fired on common actions such as opening the extension, scraping article metadata, and downloading PDFs.
Who publishes itMendeley - no other listings under this identity, 7 shared hostnames
Mendeley - no other listings under this identity, 7 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 7 hostnames
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
Mendeley sends device analytics to New Relic and Adobe
Mendeley Web Importer builds analytics events when you open or use it, including browser, screen, language, time zone, tab URL, account, and IP when available.
It sends batched JSON to New Relic and maps fields into Adobe XML requests.
What actually happens
You open Mendeley Web Importer or use one of its import, login, PDF, or download features.
The extension builds an analytics event with device, browser, session, account, institution, IP, and tab context when those values are available.
Fields assembled before analytics transmission
- Action nameEXTENSION_LAUNCHED
This tells the analytics service which extension action happened.
- Browser and operating systemFirefox 128 on Linux
This identifies your browser family, version, and operating-system family for the event.
- Language and time zoneen-US, UTC offset -240
This adds locale and time-zone context that can narrow where and how you use the browser.
- Screen size1920x1080
This records your display dimensions as part of the event context.
- Cookie supporttrue
This records whether your browser reports cookies as enabled.
- Extension version and ID3.2.78, webapps@mendeley.com
This ties the event to the installed extension build and browser extension identifier.
- Session ID7d4a5c68-1c5e-4ac6-9f07-4f6b8fd3e318
This lets events from the same browser session be grouped together.
- Tab URL and hostnamehttps://pubmed.ncbi.nlm.nih.gov/37200000/
This can reveal the page associated with the extension action.
- Account and institutionuserId 1234567, institution Example University
When you are signed in, this can associate the event with your Mendeley account and institution.
- IP address198.51.100.24 (illustrative)
This can identify the network used when the analytics event is sent.
Captured request
The verification evidence records the POST target and header; no request body or response body was recorded for this claim.
- Content-Type
- application/json
- X-Insert-Key
- <redacted>
The code that does this
The analytics builder and senders in background.js
New Relic queue and POST sender
deobfuscated/background.jsconst newRelicUrl = N.newrelic_insights_url;const newRelicInsertKey = N.newrelic_insights_apikey;const normalizeFields = event => Object.fromEntries( Object.entries(event) .filter(([, value]) => value !== undefined) .map(([key, value]) => typeof value === "boolean" ? [key, String(value)] : [key, value]));const queuedEvents = new Set;function Bs(actionName, fields = {}) { queuedEvents.add({ appName: "brxt-extension", eventType: "PageAction", actionName, ...normalizeFields(fields) }); if (Us === null) Us = setTimeout(js, 1000);}let Us = null;async function js() { Us = null; fetch(newRelicUrl, { method: "POST", headers: { "Content-Type": "application/json", "X-Insert-Key": newRelicInsertKey }, body: JSON.stringify([...queuedEvents]) }); queuedEvents.clear();}Event context builder
deobfuscated/background.jsconst analyticsClients = [e, n, t];const Ys = (...args) => { try { (function(actionName, eventData = {}, context = {}) { const parsedUa = Is().parse(window.navigator.userAgent); const event = { ...eventData, ...context, extensionVersion: f.client_version, extensionBuildTarget: f.target_platform, userAgent: navigator.userAgent, userAgentName: parsedUa.browser.name, userAgentVersion: parsedUa.browser.version, userAgentOsName: parsedUa.os.name, userAgentOsVersion: parsedUa.os.version, userAgentOsVersionName: parsedUa.os.versionName, language: navigator.language, cookiesEnabled: navigator.cookieEnabled, timestamp: Date.now(), timezone: (new Date).getTimezoneOffset(), extensionId: o().runtime.id, height: window.screen.height, width: window.screen.width, sessionId: F() }; analyticsClients.forEach(client => client.logEvent(actionName, event)); })(...args); } catch (error) { console.error(error); }};Tab, account, institution, IP, and session context
deobfuscated/background.jsconst $c = tab => { if (tab && tab.url) { return { tabUrl: tab.url, tabHostname: new URL(tab.url).hostname, tabIncognito: tab.incognito }; }};const Wc = state => { const timeSinceLaunched = Date.now() - state.launched; const timeSinceLoaded = state.loaded ? Date.now() - state.loaded : void 0; const oneHour = 36e5; const userInfo = rl.possiblyStale({ refreshIfOlderThan: oneHour }); const userProfileUuid = userInfo == null ? void 0 : userInfo.mendeleyId; const analyticsInfo = userInfo == null ? void 0 : userInfo.analyticsInfo; const userId = analyticsInfo == null ? void 0 : analyticsInfo.userId; const institutionId = analyticsInfo == null ? void 0 : analyticsInfo.accountId; const institutionName = analyticsInfo == null ? void 0 : analyticsInfo.accountName; const accessType = analyticsInfo == null ? void 0 : analyticsInfo.accessType; const ipAddress = Rs.possiblyStale({ refreshIfOlderThan: oneHour }); const sessionId = F(); return { ...$c(state.tab), accessType, institutionId, institutionName, ipAddress, timeSinceLaunched, timeSinceLoaded, userId, userProfileUuid, sessionId };};Popup launch trigger
deobfuscated/background.jsconst tl = async tab => { const state = Yc(tab); state.launched || (state.launched = Date.now()); Xs(tab); Ys("EXTENSION_LAUNCHED", void 0, Wc(state));};o().browserAction.onClicked.addListener(tl);Adobe Analytics XML sender
deobfuscated/background.jsfunction As(actionName, fields) { const adobePageFields = vs[actionName]; if (!adobePageFields) return; if (actionName === "ADD_REF_TO_LIBRARY") { adobePageFields.events = fields.addReferenceSuccess ? "event79" : "event26"; } const body = function(event) { const { accessType, cookiesEnabled, errorMessage, events, height, institutionId, institutionName, ipAddress, language, linkName, linkType, page, pageName, pageType, products, timestamp, timezone, userAgent, userId, width } = event; const xmlFields = { connectionType: "LAN", cookiesEnabled, evar7: institutionName, evar16: institutionId, evar29: userId, evar33: accessType, evar43: errorMessage, events, ipAddress, language, linkName, linkType, page, pageName, products, resolution: width && height ? `${width}x${height}` : "", timestamp, timezone, userAgent }; return `<?xml version="1.0" encoding="UTF-8"?>\n<request>${ "\n" + Object.entries(xmlFields) .map(([key, value]) => value ? ` <${key}>${value}</${key}>\n` : "") .join("") }</request>\n`; }({ ...fields, ...adobePageFields }); fetch("https://elsevier.sc.omtrdc.net/b/ss//6", { method: "POST", headers: { "Content-Type": "text/xml" }, body }).catch();}Network destinations used by this analytics path
- insights-collector.newrelic.com
Receives batched JSON PageAction analytics events through the New Relic Insights endpoint.
- elsevier.sc.omtrdc.net
Receives Adobe Analytics XML requests built from the same event fields.
- api.ipify.org
Supplies the public IP address that Wc can add to later analytics event context.
What it can do
Permissions this extension asks for, as declared in version 3.2.78. 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 every site you visit
<all_urls>
Read and change cookies, including the ones that keep you signed in
cookies
Start, monitor and manage your downloads
downloads
Show you desktop notifications
notifications
Store data in your browser
storage
Watch every request your browser makes
webRequest
Where it sends data
Destinations our analysis observed Mendeley Web Importer 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.
- api.ipify.orgwidely used
Mendeley Web Importer sends data to api.ipify.org. A widely used service: 58 other extensions we have analyzed send data here.
- elsevier.sc.omtrdc.net
Mendeley Web Importer sends data to elsevier.sc.omtrdc.net. One other extension we have analyzed sends data here.
- insights-collector.newrelic.com
Mendeley Web Importer sends data to insights-collector.newrelic.com. One other extension we have analyzed sends data here.
- brxt.mendeley.com
Mendeley Web Importer sends data to brxt.mendeley.com. No other extension we have analyzed sends data here.