Is Luna Adblock for Youtube & Websites safe?
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.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
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.
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 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.
Remote config fetch path in background.js
// 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)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.
- 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.
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.
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'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.
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.
| Field | Value | Why it matters | |
|---|---|---|---|
Install ID | wd10ml5rqqv6j | A 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/news | The site and specific page path you visited, sent together. | |
Referrer | https://en.wikipedia.org/wiki/Dog | The page you came from just before this one, showing your browsing path across sites. | |
UTM campaign parameters | utm_source=newsletter&utm_medium=email | If the link you followed had marketing-campaign tracking parameters, those are captured and forwarded too. | |
Time spent on page | 17 | How many seconds you stayed on the page. |
{
"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"
}
]
}
}
}
}Page-view uploader and shared alarm wiring, shipped vs. deobfuscated
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
}
}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
}
})
}- 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.
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.
You load any webpage while Luna Adblock is installed.
The content script ad-finder.js activates on all URLs (<all_urls> match pattern).
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.
| Content-Type | application/octet-stream |
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}| Field | Value | Why it matters | |
|---|---|---|---|
Page URL | https://www.amazon.com/s?k=wireless+headphones | The full URL of the page you were visiting when the upload fired. | |
Install ID | 7vgy12as5blj8 | A persistent identifier that links every upload from your browser together, across sessions and page visits. | |
Extension ID | ehfcoplbhoohillcmlophcfghpeilfjc | The 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.0 | Your browser and OS version string, which can narrow down device type and software versions. | |
Browser language | en-US | Your 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. |
Upload path in background.js
// 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';- 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.
+3 more findings not shown