Is AdLibNote: Ad Library Downloader Facebook™ safe?

Medium risk

AdLibNote intercepts Facebook session tokens and GraphQL request data and transmits them to denote.net.

The extension injects a script into Facebook Ads Library pages that monkey-patches XMLHttpRequest to capture session tokens (X-FB-LSD, fb_dtsg, lsd, __user) and ad GraphQL responses, then forwards them via postMessage to a background listener that POSTs the data to denote.net. It also registers webRequest listeners that capture raw request headers and form data from facebook.com GraphQL calls, driven by remotely-fetched interception rules that can be updated at any time. Ad creative metadata including URLs, author info, and engagement data is uploaded to denote.net when a user saves an ad; separately, a persistent device fingerprint is sent to an Aliyun logging endpoint on every telemetry event.

Dunkum Sherwinv1.0.5.11Chrome Web Store
49Risk
Who publishes it

Dunkum Sherwin - 4 other listings from the same operator, 1 of them carrying a finding

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

Publisher
Dunkum Sherwin

Shared hosts - 2 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.

cdn.denote.net
Also called by 3 other listings, including Denote, TTNote: Video Downloader and Saver
ext-denote-save.canny.io
Also called by 4 other listings, including Denote, TTNote: Video Downloader and Saver

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

Publishers can request a review.

Findings

Server-controlled interception rules fetched from denote.net on every startup

On install and every 12h, the extension fetches a config from denote.net, stored as EXT_RULES, governing which Facebook GraphQL requests are intercepted and which API key/doc ID values substitute in, changeable without a Store update.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You install the extension or leave it running for 12+ hours.

Pe() is called on install and when the browser checks for a stale config; it fires Ue() if configLastUpdate is absent or older than 43,200 seconds.

The extension did this

The service worker downloads updated interception rules from denote.net and stores them, changing which Facebook requests the extension modifies without a browser update.

Ue() POSTs {extension_id: 102} to denote.net/user/api-chrome-extension/get-remote-config, parses t.data.config[0].params, and overwrites EXT_RULES in chrome.storage.local.

The code that does this

Remote config fetch and rule storage (background/index.js lines 1507-1534)

Readable version
async function checkConfigFreshness() {  let stored = await chrome.storage.local.get(['configLastUpdate']);  let isStale = stored.configLastUpdate === undefined ||    Date.now() - stored.configLastUpdate >= 12 * 60 * 60 * 1000;  if (isStale) {    fetchRemoteConfig();    // also calls wt() — another initialisation routine  }}function fetchRemoteConfig() {  let headers = new Headers();  headers.append('Content-Type', 'application/json');  let body = JSON.stringify({ extension_id: 102 });  fetch('https://denote.net/user/api-chrome-extension/get-remote-config', {    method: 'POST',    headers,    body  })    .then(r => r.json())    .then(data => {      chrome.storage.local.get(['latest_user', 'multiple_version']).then(local => {        let update = {          EXT_RULES: data.data.config[0].params,          configLastUpdate: Date.now()        };        // A/B test assignment for new users        if (local.latest_user === 1 && !local.multiple_version) {          try {            let abRule = data.data.config[0].params.filter(r => r.type === '2')[0];            update.multiple_version = Math.random() > Number(abRule.ab_rate) ? 'B' : 'A';          } catch {}        }        chrome.storage.local.set(update);      });    })    .catch(err => console.log('error', err));}
When this fires

Every 12 hours

Config is refreshed every 12 hours while the browser is running, and immediately on extension install.

Remote config endpoint
    • denote.net

    Extension operator's backend. Serves EXT_RULES config controlling which Facebook GraphQL requests are intercepted and how headers are modified. Also hosts save/board/folder APIs.

Persistent device fingerprint sent to Alibaba Cloud telemetry on user actions

The extension MD5-hashes its name, version, user-agent, and a timestamp into a device ID stored locally, sent with your language, page URL, plan, and version as a protobuf POST to Alibaba Cloud on install, uninstall, and key actions.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You save a Facebook ad, download media, or perform any tracked action.

The content script sends a SEND_LOG message to the background service worker carrying the event type and action details.

The extension did this

The service worker batches your device ID, browser details, and plan tier, then transmits them to Alibaba Cloud Log Service.

The payload is protobuf-encoded and posted to nc-dn-us.us-west-1.log.aliyuncs.com/logstores/extensions/track via sendBeacon or fetch.

Fields transmitted on every SEND_LOG event
  • Device ID
    a3f8c2d1e4b5f6a7

    A stable identifier created from your browser fingerprint. Reused across every session to tie all actions back to your device.

  • Extension version
    1.0.5.6

    The installed version of the extension sent with every event.

  • Plan tier
    0

    Your subscription level (free or paid plan index).

  • A/B variant
    A

    A randomly assigned experiment group (A or B) stored in your profile.

  • User-agent string
    Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/128.0.0.0

    Your browser name, version, and operating system, sent as part of the request headers bundle.

  • Referrer URL
    https://www.facebook.com/ads/library/

    The URL of the current tab at the time of the action, included in the request headers payload.

The code that does this

Device ID generation and telemetry dispatch (background/index.js)

Readable version

ID generation (lines 687-698)

background/index.js
async function generateUserId() {  let manifest = chrome.runtime.getManifest();  let id = md5(`${manifest.name}_${manifest.version}_${navigator.userAgent}_${Date.now()}`).toString();  await chrome.storage.local.set({ userId: id });  return id;}async function getUserId() {  let stored = await chrome.storage.local.get();  return stored.userId || (await generateUserId());}

Payload assembly (lines 856-875)

background/index.js
async function sendLog(eventData) {  let basePayload = await buildBasePayload();  slsClient().send(Object.assign(basePayload, eventData));}async function buildBasePayload() {  let userId = await getUserId();  let stored = await chrome.storage.local.get(['userInfo', 'planId', 'multiple_version']);  let signedUpAt = stored.userInfo?.created_at ? String(new Date(stored.userInfo.created_at).getTime()).slice(0, 10) : 0;  return {    extension_name: 'dn_fbadnote',    ext_version: chrome.runtime.getManifest().version,    anonymous_user_id: 0,    user_id: userId,    sign_up_at: signedUpAt,    created_at: currentTimestamp(),    plan_index: stored.planId || 0,    multiple_version: stored.multiple_version || 'A'  };}
Telemetry destination
    • nc-dn-us.us-west-1.log.aliyuncs.com

    Alibaba Cloud Log Service (Aliyun SLS). Receives batched protobuf-encoded usage events. Operator-controlled project 'nc-dn-us', logstore 'extensions'.

Where it sends data

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

  • nc-dn-us.us-west-1.log.aliyuncs.com

    AdLibNote sends data to nc-dn-us.us-west-1.log.aliyuncs.com. 7 other extensions we have analysed send data here.

  • denote.net

    AdLibNote sends data to denote.net. 2 other extensions we have analysed send data here.

Updated 30 September 2026niepmhdjjdggogblnljbdflekfohknmc