Is Local Media Player safe?
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.
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
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 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.
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.
- Visited page URLhttps://example.org/watch/local-media-demo
Shows the exact page or frame being checked by the reputation service.
- Page title and nameLocal Media Demo - Example Video
Adds human-readable context about the page you opened.
- Referrerhttps://search.example/?q=local+media+player
Links the current page load to the page that led you there.
- Request headersaccept-language: en-US,en;q=0.9
Can include site-specific metadata your browser sent during the page load.
- Response status and headersstatusCode: 200; content-type: text/html
Shows how the website responded to the request.
- Tab and frame detailstabId 12, frameId 0, main_frame
Identifies where the navigation happened inside your browser session.
- Panel identifierspanelistId 123456789; partnerId 271
Lets the service connect multiple browsing checks to the same installation or panel account.
- Server IP data93.184.216.34
Adds network-location context for the website response.
Consent, permission request, and Safe Browsing activation
Consent flow in plain language
popup/consent.jsagreeBtn.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.jssafeBrowsingHelper.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.jsasync 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(); }}Listeners collect navigation and request data before posting the event
Listener registration in plain language
libs/safe-browsing.jsconst 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.jsasync 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.jsasync 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.jsconst 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'};- 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.