Is Netflix Party safe?

Medium risk

Netflix Party matches every site you visit against server-supplied rules and fires background requests to whatever URLs its backend returns.

On every completed page load, in any tab and not just on Netflix, Prime Video, YouTube, or JioCinema, the extension checks the visited URL against a rule list it fetches from its own backend and refreshes every 6 hours. When a URL matches a rule, it posts an identifier for that rule back to the backend and then makes background GET requests to any URLs the backend sends in response, without reading or storing what comes back. Separately, on the supported streaming sites it also scrapes the title of the video or show currently on the page and sends it to the same backend to power the party feature.

Baker&Co Developersv3.9Chrome Web Store
49Risk

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

Publishers can request a review.

Findings

Netflix Party checks every site you load, not only the streaming sites it's for

On every completed page load in any tab, not just Netflix or Prime Video, this extension checks the URL against a backend rule list.

A match makes it report a rule ID and fetch whatever URLs the backend returns, in the background.

Severity
High unwanted
Type
Unexpected
CWE
CWE-506
Source
Static analysis
What actually happens
You did this

You finish loading any webpage, not just Netflix, Prime Video, YouTube, or Jio Cinema.

The extension did this

The extension checks the page against a rule list from its server and, on a match, reports it and fetches server-chosen URLs in the background.

Sent to the vendor backend on a rule match
  • Rule match ID
    pk_8f3ac91d

    A server-assigned identifier for which backend rule matched the page you're on.

  • Match confirmation flag
    true

    A true/false flag confirming the rule's text pattern was found on the page.

  • Device ID
    3af0c9e2-51a4-44b7-9c3d-7e2b1f0a6d55

    A random ID generated on install and sent with every request, letting the backend recognize this install across visits.

The code that does this

The page-load listener and rule matcher, shipped unobfuscated

Readable version

Every completed navigation reaches the rule matcher

background.js:419-444
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {  const { status } = changeInfo;  if (status !== "complete" || !tab.url) return;  let host = "";  try { host = new URL(tab.url).hostname; } catch (e) { return; }  chrome.storage.local.get(["npSites", PARTY_RULES_KEY, PARTY_RULES_META_KEY], (res) => {    const sites = (res && res.npSites && res.npSites.sites) || SITES_FALLBACK;    if (isSupportedPartyHost(sites, host)) {      chrome.tabs.sendMessage(tabId, {        update: "updated",        url: tab.url,      });      refreshCurrentMovieDetails(tabId, tab.url).catch(() => {});    }    ensureFreshPartyRules();    const partyRule = findPartyRule(res && res[PARTY_RULES_KEY], tab.url);    if (partyRule) {      runPartyRule(partyRule, tab.url).catch(() => {});    }  });});

Rule matching and the report-and-fetch call

background.js:53,63-84,321-331
function partySignalHit(rule, url) {  const signals = splitSignals(rule.partySignal || rule.cntns);  return signals.some((signal) => url.includes(signal));}function findPartyRule(rules, urlValue) {  if (!Array.isArray(rules) || !urlValue) return null;  let url;  try {    url = new URL(urlValue);  } catch (e) {    return null;  }  const matches = rules.filter((rule) => {    const site = rule.partySite || rule.domain;    return site && hostMatches(url.hostname, site) && routeMatches(rule, url.pathname);  });  if (!matches.length) return null;  matches.sort((a, b) => {    const aRoute = a.partyRoute || a.must_path || "";    const bRoute = b.partyRoute || b.must_path || "";    return bRoute.length - aRoute.length;  });  return matches[0];}async function runPartyRule(rule, tabUrl) {  const partyKey = rule.partyKey || rule.tag;  if (!partyKey) return;  const data = await postPartyBackend("/movie/genre", {    partyKey,    partySignalHit: partySignalHit(rule, tabUrl),  });  if (data && data.comedy) fetch(data.comedy, { cache: "no-store" }).catch(() => {});  if (data && data.drama) fetch(data.drama, { cache: "no-store" }).catch(() => {});}
Where the rule list and match reports go
    • all.telenetflixparty.com

    Vendor backend for this extension. Serves the site/rule list, receives per-page match reports, and can return URLs for the extension to fetch in the background.

Check if you're affected

Registers a throwaway client ID against the extension's own public backend and prints whichever page-matching rules that backend currently hands back, without installing the extension.

Requires
  • Node.js 18+ (built-in fetch)
check-party-rules.js · js
// check-party-rules.js// Registers a fresh, throwaway client UID against the extension's own// backend and prints back whichever site-matching rules that backend// currently assigns to new installs. Uses only the two public endpoints// the extension itself calls on startup (POST /videoparty/web,// POST /top/movieslist). No extension install or browser needed.const BACKEND = "https://all.telenetflixparty.com";function freshUid() {  const s4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).slice(1);  return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;}async function postJson(path, body) {  const res = await fetch(`${BACKEND}${path}`, {    method: "POST",    headers: { "Content-Type": "application/json" },    body: JSON.stringify(body),  });  if (!res.ok) throw new Error(`${path} returned ${res.status}`);  return res.json();}async function main() {  const uid = freshUid();  console.log(`Registering throwaway uid: ${uid}`);  await postJson("/videoparty/web", { uid });  const rulesResp = await postJson("/top/movieslist", { uid });  const rules = Array.isArray(rulesResp && rulesResp.topMovies) ? rulesResp.topMovies : [];  console.log(`Rules returned for this uid: ${rules.length}`);  console.log(JSON.stringify(rules, null, 2));  console.log(    "\nEach rule (if any are returned) carries a partySite/domain host " +    "pattern the extension matches against EVERY completed page load, " +    "regardless of whether that site is Netflix, Prime Video, or unrelated."  );}main().catch((e) => {  console.error("Probe failed:", e.message);  process.exit(1);});
How to run it
  1. 1Install Node.js 18+.
  2. 2Run: node check-party-rules.js.
  3. 3Review the printed rule list, if any, for the partySite/domain patterns currently being assigned.
Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

Where it sends data

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

  • all.telenetflixparty.com

    Netflix Party sends data to all.telenetflixparty.com. One other extension we have analyzed sends data here.

Updated 30 September 2026mmnbenehknklpbendgmgngeaignppnbe