Is Luna Adblock for Youtube & Websites safe?

High risk

Luna Adblock is high risk. Luna Adblock fetches its ad-scraping config from p.qljx.co on every startup, with a persistent install ID in the URL. The response controls which sites Pathmatics scans and which ad patterns it targets, letting the operator change it.…

ST Advancedv3.0.0Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Ad blocker fetches remote ad-detection rules from p.qljx.co on startup

Luna Adblock fetches its ad-scraping config from p.qljx.co on every startup, with a persistent install ID in the URL.

The response controls which sites Pathmatics scans and which ad patterns it targets, letting the operator change it.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open Chrome with Luna Adblock installed (or the service worker restarts).

The extension checks its locally cached config; if older than 30 minutes it fetches fresh rules.

The extension did this

The extension sends a GET request to p.qljx.co with your install ID appended.

The server returns a compressed binary payload that replaces the local AdRules, SiteConfigs, and UrlBlackList controlling what the ad-finder scans.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://p.qljx.co/Ajax0001/Config?installId=7vgy12as5blj8
HTTP 200 OK, LZString-compressed binary containing AdRules (parse trees for ad DOM matching), SiteConfigs (per-domain activation flags), and UrlBlackList. Confirmed across 5 requests during dynamic analysis.
03EvidenceCODE COMPARE
The code that does this

Remote config fetch path in background.js

What it actually does
// background.js:11182–11193
async function fetchOrCacheConfig(sendMessageToBackground) {
  const cachedAt = await getOrSet('configTs', Date.now);
  const isStale = Date.now() - cachedAt > 1_800_000; // 30 min TTL

  return getOrSet('config', async () => {
    // message routed to background.js:11568 → Ff(pmExternalConfigUrl + installId)
    const raw = await sendMessageToBackground({ type: 'ads#GET_EXTERNAL_CONFIGURATION' });
    const config = JSON.parse(raw);
    if (config == null) return;
    config.TimeOffset = config.Now ? Date.now() - config.Now : 0;
    parseAdRules(config);       // decompresses AdRules[].ParseTreeString
    parseSiteConfigs(config);   // builds ParsedSiteConfigs lookup
    parseUrlBlacklist(config);  // builds ParsedUrlBlackList per hostname
    await set('configTs', Date.now());
    return config;
  }, isStale);
}

// The URL constructed at background.js:11569:
// `${pmExternalConfigUrl}?installId=${installId}`
// where pmExternalConfigUrl = 'https://p.qljx.co/Ajax0001/Config' (line 11581)
04EvidencePLAIN NOTE
What the remote config controls

The server-returned payload contains three structures that govern the ad-finder's behavior:

- **AdRules** — serialised parse trees (LZString + JSON) describing DOM patterns the ad-finder matches against - **SiteConfigs** — a domain-keyed map controlling whether the scraper activates on a given site - **UrlBlackList** — per-hostname URL regex filters that suppress crawling on specific paths

Because these rules are fetched at runtime, the server operator can expand coverage to new sites, add new DOM selectors, or alter what ad data is collected without shipping an extension update.

05EvidenceTHIRD PARTY LIST
Config server
  • p.qljx.co

    Pathmatics (acquired by SensorTower) configuration endpoint. Receives the install ID on every config fetch and returns the ad-detection ruleset in effect for this extension build.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Page URLs, Paths & Referrers Uploaded to SensorTower Panel

The same 60-minute upload also sends page-level detail: hostname, path, referrer, and UTM params, batched to adblock.st-panel-api.com.

A captured request showed the chain wikipedia.org, bbc.com, bbc.co.uk with referrers reproduced exactly.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You navigate from one page to another while the extension is installed and enabled.

The path you take between pages -- including the referring page -- becomes a local page-view record.

The extension did this

The extension's background service worker uploads the hostname, URL path, and referrer for every recorded page view to adblock.st-panel-api.com.

Page views are filtered against a local ignore list before being queued, then uploaded on the same recurring alarm as the session data.

02EvidenceTEMPORAL PATTERN
When this fires
Every 1 hour

Page views are uploaded on the same chrome.alarms timer, '@alarm/upload-web-usage', that fires roughly every 60 minutes for as long as the browser is open.

03EvidenceFIELD TABLE
Fields in the page-view upload body
FieldValueWhy it matters
Install ID
wd10ml5rqqv6jA persistent ID for your install of the extension lets the panel backend tie every upload back to the same browser over time.
Page hostname + path
www.bbc.com/newsThe site and specific page path you visited, sent together.
Referrer
https://en.wikipedia.org/wiki/DogThe page you came from just before this one, showing your browsing path across sites.
UTM campaign parameters
utm_source=newsletter&utm_medium=emailIf the link you followed had marketing-campaign tracking parameters, those are captured and forwarded too.
Time spent on page
17How many seconds you stayed on the page.
04EvidenceNETWORK CAPTURE
Captured request
POSThttps://adblock.st-panel-api.com/v1/page_views/upload
200 OK. Observed during dynamic analysis: the path and referrer chain in the upload body matched the real navigation performed in the test session -- en.wikipedia.org/wiki/Dog, then www.bbc.com/news referred from the Wikipedia page, then www.bbc.co.uk/news referred from the bbc.com page.
Body
{
  "app_id": "ehfcoplbhoohillcmlophcfghpeilfjc",
  "install_id": "wd10ml5rqqv6j",
  "time_zone": "America/New_York",
  "device_name": "Chrome",
  "device_type": "Windows",
  "birth_year": 1990,
  "websites": {
    "en.wikipedia.org": {
      "/wiki/Dog": {
        "page_views": [
          {
            "duration": 17,
            "timestamp": 1756540800000,
            "referrer": "about:blank"
          }
        ]
      }
    },
    "www.bbc.com": {
      "/news": {
        "page_views": [
          {
            "duration": 22,
            "timestamp": 1756540900000,
            "referrer": "https://en.wikipedia.org/wiki/Dog"
          }
        ]
      }
    },
    "www.bbc.co.uk": {
      "/news": {
        "page_views": [
          {
            "duration": 19,
            "timestamp": 1756541000000,
            "referrer": "https://www.bbc.com/news"
          }
        ]
      }
    }
  }
}
05EvidenceCODE COMPARE
The code that does this

Page-view uploader and shared alarm wiring, shipped vs. deobfuscated

What it actually does
Alarm registration + uploader wiring (shared with session uploads)background.js:14074-14103
function _g(e) {
  let t = new hg({
    ...e,
    ...e.monitoring,
    getAdNetworks: e.getAdNetworks,
    getAdFields: e.getAdFields
  });
  t.setupTriggers();
  let n = Hm({
    ...e,
    ...e.uploads,
    pageViews: {
      ...e.uploads.pageViews,
      storage: e.storage
    },
    sessions: {
      ...e.uploads.sessions,
      storage: e.storage
    }
  });
  return bg(vg, {
    periodInMinutes: (e.uploads.uploadIntervalInMs ?? yg) / Gh
  }), Q.default.alarms.onAlarm.addListener(({
    name: e
  }) => {
    e === vg && n.uploadAll()
  }), {
    sessionMonitor: t,
    usageUploader: n
  }
}
uploadPageViews POST to /v1/page_views/uploadbackground.js:4946-4977
async function v(e) {
  if (!e.appId.trim()) {
    let {
      websites: t,
      diffPrivateWebsites: n,
      ...i
    } = e;
    throw r?.error(`Missing required parameter 'appId'`, i), Error(`Missing required parameter 'appId'`)
  }
  if (!e.installId.trim()) {
    let {
      websites: t,
      diffPrivateWebsites: n,
      ...i
    } = e;
    throw r?.error(`Missing required parameter 'installId'`, i), Error(`Missing required parameter 'installId'`)
  }
  let i = (0, Gc.default)(navigator?.userAgent);
  await t(`/v1/page_views/upload`, {
    method: `POST`,
    retry: 0,
    body: {
      app_id: e.appId,
      install_id: e.installId,
      time_zone: ms(),
      device_name: n(i.browser?.name),
      device_type: i.os.name,
      birth_year: e.birthYear,
      websites: e.websites,
      diff_private_websites: e.diffPrivateWebsites
    }
  })
}
06EvidenceTHIRD PARTY LIST
Destination for page-view data
  • adblock.st-panel-api.com

    Receives the page-view upload. The base URL and message channel '@sensortower/ad-finder' elsewhere in the script point to a bundled SensorTower analytics SDK.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Ad blocker uploads page URLs and ad DOM to p.qljx.co on every navigation

Luna Adblock embeds a Pathmatics (SensorTower) SDK crawling ads on every page, uploading to p.qljx.co: page URL, ad-element outerHTML, ad source URLs, and a persistent install ID.

Enabled by default, firing on navigation with no indicator.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You load any webpage while Luna Adblock is installed.

The content script ad-finder.js activates on all URLs (<all_urls> match pattern).

The extension did this

The background service worker POSTs a compressed ad-crawl payload to p.qljx.co.

The payload includes the page URL, ad element outerHTML, source URLs, your persistent install ID, extension ID, user agent, and browser language.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://p.qljx.co/Ajax0001/IPD
HTTP 200 OK, confirmed across 3 POSTs in run1 and 9 POSTs in run2 during dynamic analysis
Headers
Content-Typeapplication/octet-stream
Body
LZString-compressed JSON: {"crawlUrl":"https://www.amazon.com/","installId":"7vgy12as5blj8","crawlId":"a3f2c1e9-...","ExtensionId":"ehfcoplbhoohillcmlophcfghpeilfjc","ExtensionVersion":"2.5.0","UserAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36","BrowserLanguage":"en-US","PanelPartnerId":0,"zys":[{"hash":"d4a7b2","text":"Sponsored","sourceUrls":["https://aax-us-east.amazon-adsystem.com/..."],"dimensions":{"w":300,"h":250},"outerHtml":"<div data-ad-type=\"sponsored\">..."}],"dateCrawlStarted":1713398400000,"dateCrawled":1713398401234}
03EvidenceFIELD TABLE
Fields included in every upload
FieldValueWhy it matters
Page URL
https://www.amazon.com/s?k=wireless+headphonesThe full URL of the page you were visiting when the upload fired.
Install ID
7vgy12as5blj8A persistent identifier that links every upload from your browser together, across sessions and page visits.
Extension ID
ehfcoplbhoohillcmlophcfghpeilfjcThe Chrome extension's stable identifier, used server-side to associate uploads with this specific extension build.
User agent
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0Your browser and OS version string, which can narrow down device type and software versions.
Browser language
en-USYour browser's preferred language setting, which can indicate region.
Ad element HTML
<div data-ad-type="sponsored" class="s-sponsored-info-icon">Sponsored</div>The raw HTML of ad elements on the page, including ad source URLs and sponsored text, capturing what ads you were shown.
04EvidenceCODE COMPARE
The code that does this

Upload path in background.js

What it actually does
// background.js:11519–11548
async upload(crawlPayload) {
  if (!await this.config.isEnabled()) return;
  const prepared = this.prepareData(crawlPayload);
  await this.fetch(this.config.crawlUploadUrl, {
    method: 'POST',
    body: LZString.compressToUint8Array(JSON.stringify(prepared)),
    headers: { 'Content-Type': 'application/octet-stream' }
  });
}

prepareData(payload) {
  return {
    ...payload,           // crawlId, zys (ad objects), dateCrawlStarted, crawlUrl
    PartnerVersion: chrome.runtime.getManifest().version,
    ExtensionId: chrome.runtime.id,       // 'ehfcoplbhoohillcmlophcfghpeilfjc'
    ExtensionVersion: this.config.extensionVersion,  // '2.5.0'
    UserAgent: navigator.userAgent,
    BrowserLanguage: navigator.language,
    PanelPartnerId: this.config.panelPartnerId   // hardcoded 0 (Vf)
  };
}

// crawlUploadUrl set at line 11580, 13650:
const Bf = 'https://p.qljx.co/Ajax0001/IPD';
05EvidenceTHIRD PARTY LIST
Data destination
  • p.qljx.co

    Pathmatics (acquired by SensorTower) ad-intelligence platform. Receives compressed ad impression crawl data including page URLs, ad HTML, and persistent user identifiers.

Updated 17 September 2026ehfcoplbhoohillcmlophcfghpeilfjc