Is Local Media Player safe?

High risk

Local Media Player sends consent-gated browsing URLs, headers, page titles, and referrers to its safe-browsing servers.

After a user agrees and grants optional permissions, the bundled safe-browsing code monitors HTTP and HTTPS page loads and network events. It sends compressed events containing URLs, request and response headers, page titles, referrers, tab and frame details, and browser/OS data, and it also contacts an identity API to create or read panel user identifiers.

Scott Moorev1.0.0Chrome Web Store
72Risk

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

Publishers can request a review.

Findings

Safe Browsing consent enables cross-site navigation telemetry

After the optional Safe Browsing prompt is accepted and broad permissions granted, Local Media Player watches HTTP/HTTPS page loads.

It assembles visited URLs, titles, referrers, and headers, then posts the event to cs.bkp.v1.fsdifhj.com.

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

You accept the optional Safe Browsing prompt and grant the requested site permissions.

The consent button stores the accepted state, resets the badge, and requests web request, web navigation, scripting, alarms, and all-site access.

The extension did this

The extension enables Safe Browsing listeners that process browsing events across websites.

The service worker calls the Safe Browsing enable path once consent and the optional permissions are present.

Captured request
POSThttps://cs.bkp.v1.fsdifhj.com/api/v2/hazard/verify

Hazard verification request observed after the consent flow enabled Safe Browsing. The recorded evidence did not include a request body, so no body is shown.

Fields assembled for the Safe Browsing verification event
  • Visited page URL
    https://example.org/watch/local-media-demo

    Shows the exact page or frame being checked by the reputation service.

  • Page title and name
    Local Media Demo - Example Video

    Adds human-readable context about the page you opened.

  • Referrer
    https://search.example/?q=local+media+player

    Links the current page load to the page that led you there.

  • Request headers
    accept-language: en-US,en;q=0.9

    Can include site-specific metadata your browser sent during the page load.

  • Response status and headers
    statusCode: 200; content-type: text/html

    Shows how the website responded to the request.

  • Tab and frame details
    tabId 12, frameId 0, main_frame

    Identifies where the navigation happened inside your browser session.

  • Panel identifiers
    panelistId 123456789; partnerId 271

    Lets the service connect multiple browsing checks to the same installation or panel account.

  • Server IP data
    93.184.216.34

    Adds network-location context for the website response.

The code that does this

Consent, permission request, and Safe Browsing activation

Readable version

Consent flow in plain language

popup/consent.js
agreeBtn.addEventListener('click', async () => {    await consentHelper.setConsent(true);    await chrome.runtime.sendMessage({ type: 'RESET_BADGE' });    const granted = await safeBrowsingHelper.requestPermissions();    await chrome.runtime.sendMessage({ type: 'CONSENT', agreed: granted });    window.close();});

Requested permission set

libs/helper.safe-browsing.js
safeBrowsingHelper.getRequiredPermissions = () => ({    permissions: ['alarms', 'webRequest', 'webNavigation', 'scripting'],    origins: ['<all_urls>']});safeBrowsingHelper.requestPermissions = async function () {    return await chrome.permissions.request(this.getRequiredPermissions());};safeBrowsingHelper.hasEnoughPermissions = async function () {    return await chrome.permissions.contains(this.getRequiredPermissions());};

Enable path

sw.js
async function enableSafeBrowsing() {    const service = await globalThis.safeBrowsing();    await service.enable();    await service.onPageVisited(async ({ status, tabId }) => {        if (status === 'UNSAFE') {            await chrome.tabs.sendMessage(tabId, { type: 'SHOW_NOTIFICATION_POPUP' });        }    });}async function init() {    initListeners();    if (!(await consentHelper.consentSettled())) {        await safeBrowsingHelper.drawBadge();        return;    }    await safeBrowsingHelper.resetBadge();    if (await consentHelper.consentState() && await safeBrowsingHelper.hasEnoughPermissions()) {        await enableSafeBrowsing();    }}
The code that does this

Listeners collect navigation and request data before posting the event

Readable version

Listener registration in plain language

libs/safe-browsing.js
const filter = { urls: ['https://*/*', 'http://*/*'], types: ['main_frame', 'sub_frame'] };if (await permissionsContainRequiredSet()) {    chrome.webRequest.onSendHeaders.addListener(details => eventRouter.onSendHeaders(details), filter, ['requestHeaders']);    chrome.webRequest.onHeadersReceived.addListener(details => eventRouter.onHeadersReceived(details), filter, ['responseHeaders']);    chrome.webRequest.onResponseStarted.addListener(details => eventRouter.onResponseStarted(details), filter, ['responseHeaders']);    chrome.webNavigation.onBeforeNavigate.addListener(details => eventRouter.onBeforeNavigate(details));    chrome.webNavigation.onCompleted.addListener(details => eventRouter.onCompleted(details));    chrome.webNavigation.onCreatedNavigationTarget.addListener(details => eventRouter.onCreatedNavigationTarget(details));    chrome.webNavigation.onHistoryStateUpdated.addListener(details => eventRouter.onHistoryStateUpdated(details));    chrome.webRequest.onErrorOccurred.addListener(details => eventRouter.onErrorOccurred(details), filter);    chrome.webRequest.onBeforeRedirect.addListener(details => eventRouter.onBeforeRedirect(details), filter);    chrome.webNavigation.onErrorOccurred.addListener(details => eventRouter.onNavigationError(details));}

Event construction in plain language

libs/safe-browsing.js
async function buildBackgroundNavigationEvent(record) {    const navigationCount = await foregroundNavigationStore.count(record.tabId);    const frameId = record.onSendHeaders?.frameId;    const frameType = frameId === 0 ? 'main_frame' : 'sub_frame';    const page = await pageAttributes.read(record.tabId);    event        .setUrl(record.onSendHeaders?.url)        .setTab(record.tabId, record.openerTabId)        .setLibraryVersion(await osAndBrowser.getOs())        .setBrowser(osAndBrowser.getBrowser().name, osAndBrowser.getBrowser().version)        .setNavigationCount(navigationCount)        .setFrame(frameId, record.onSendHeaders?.parentFrameId)        .setPanelist(panelistId, panelId, partnerId, distributorId)        .setTimestamp(String(record.eventTime))        .setRequest(record.onSendHeaders?.method, normalizeHeaders(record.onSendHeaders?.requestHeaders))        .setEventId(Guid.create().toString(), frameType === 'main_frame' ? 'MAIN' : 'RESOURCE', frameType)        .setPageAttributes(page.title, page.name)        .setContextReferrer(page.referrer);    if (record.onHeadersReceived) {        response.setStatusLine(record.onHeadersReceived.statusLine).setStatusCode(record.onHeadersReceived.statusCode);        normalizeHeaders(record.onHeadersReceived.responseHeaders).forEach(header => response.addHeader(header.name, header.value));    }    if (record.onBeforeRedirect) {        response.setRedirectUrl(record.onBeforeRedirect.redirectUrl).setFromCache(record.onBeforeRedirect.fromCache);        event.setServerIp(record.onBeforeRedirect.ip);    }    if (record.onResponseStarted) {        response.setFromCache(record.onResponseStarted.fromCache);        event.setServerIp(record.onResponseStarted.ip);    }    if (record.onErrorOccurred) {        response.setError(record.onErrorOccurred.error);    }    await backgroundNavigationStorage.write(event.toObject());}

POST sender in plain language

libs/safe-browsing.js
async function fastVerify(event) {    return await postHazard('/api/v2/hazard/fast-verify', event);}async function verify(event) {    return await postHazard('/api/v2/hazard/verify', event);}async function postHazard(path, event) {    const json = JSON.stringify(event);    const body = config.compressRequests ? compressor.compress(json) : json;    const contentType = config.compressRequests ? 'application/octet-stream' : 'application/json;charset=utf-8';    const response = await fetch(config.csHost + path, {        method: 'POST',        body,        headers: { 'Content-Type': contentType }    });    return await response.json();}

Identifier and host setup in plain language

libs/safe-browsing.js
const fallbackDomain = 'bkp.v1.fsdifhj.com';const dnsProviders = [    'https://dns.google/resolve',    'https://dns.alidns.com/resolve',    'https://cloudflare-dns.com/dns-query'];const resolvedDomain = await resolveTxt(fallbackDomain, dnsProviders).catch(() => fallbackDomain);const idHost = 'https://id.' + resolvedDomain;const csHost = 'https://cs.' + resolvedDomain;const memoryUrl = idHost + '/api/personal/memory';const dataUrl = idHost + '/api/personal/data';const identifierHeaders = {    'X-PANEL-USER-KEY': user.userKey,    'X-PANEL-USER-ID': user.userId,    'Cache-Control': 'no-cache'};
Remote hosts used by the Safe Browsing bundle
    • cs.bkp.v1.fsdifhj.com

    Receives hazard verification POSTs containing the assembled browsing event.

    • id.bkp.v1.fsdifhj.com

    Reads and writes panel user identifiers through the personal memory and data endpoints.

    • dns.google
    • dns.alidns.com
    • cloudflare-dns.com

    One DNS-over-HTTPS provider used to resolve the fallback domain.

What it can do

Permissions this extension asks for, as declared in version 1.0.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Store data in your browser

    storage

Where it sends data

Destinations our analysis observed Local Media Player 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.

  • cs.bkp.v1.fsdifhj.com

    Local Media Player sends data to cs.bkp.v1.fsdifhj.com. No other extension we have analysed sends data here.

  • id.bkp.v1.fsdifhj.com

    Local Media Player sends data to id.bkp.v1.fsdifhj.com. No other extension we have analysed sends data here.

Updated 30 September 2026hfjoaohgiikdanjdmgalfmmkcbajodko