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.
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.
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.
| Field | Value | Why it matters | |
|---|---|---|---|
Action name | EXTENSION_LAUNCHED | This tells the analytics service which extension action happened. | |
Browser and operating system | Firefox 128 on Linux | This identifies your browser family, version, and operating-system family for the event. | |
Language and time zone | en-US, UTC offset -240 | This adds locale and time-zone context that can narrow where and how you use the browser. | |
Screen size | 1920x1080 | This records your display dimensions as part of the event context. | |
Cookie support | true | This records whether your browser reports cookies as enabled. | |
Extension version and ID | 3.2.78, webapps@mendeley.com | This ties the event to the installed extension build and browser extension identifier. | |
Session ID | 7d4a5c68-1c5e-4ac6-9f07-4f6b8fd3e318 | This lets events from the same browser session be grouped together. | |
Tab URL and hostname | https://pubmed.ncbi.nlm.nih.gov/37200000/ | This can reveal the page associated with the extension action. | |
Account and institution | userId 1234567, institution Example University | When you are signed in, this can associate the event with your Mendeley account and institution. | |
IP address | 198.51.100.24 (illustrative) | This can identify the network used when the analytics event is sent. |
| Content-Type | application/json |
| X-Insert-Key | <redacted> |
The analytics builder and senders in background.js
const 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();
}const 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);
}
};const $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
};
};const 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);function 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();
}- 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.