From 41dfeac0640637eab9e1a3c9aa9685fec6b62353 Mon Sep 17 00:00:00 2001 From: 1morr Date: Wed, 16 Sep 2026 10:39:25 +0800 Subject: [PATCH 1/2] chore: add .gitattributes to normalize line endings Measured before committing: `git add --renormalize .` is a zero diff and no tracked blob contains a CR, so this is insurance against a checkout on a machine without core.autocrlf, not a correction. --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto From cbc6e0a4ef6586532a894298fb23813eb8a82308 Mon Sep 17 00:00:00 2001 From: 1morr Date: Wed, 16 Sep 2026 10:40:43 +0800 Subject: [PATCH 2/2] docs: add privacy notice for the cookies permission The README's permission table says what each permission is for; a user deciding whether to grant `cookies` and bilibili.com host access needs to know which cookie, which endpoints, and that nothing leaves the machine. Every claim points at the file that backs it, so the notice goes stale loudly rather than quietly. --- PRIVACY.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 3 ++ README.zh-Hant.md | 2 ++ 3 files changed, 93 insertions(+) create mode 100644 PRIVACY.md diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..8f4cb21 --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,88 @@ +# Privacy + +BiliStreamMonitor runs entirely inside your browser. There is no server behind +it, no account, and no build step — the source in this repository is what runs. +Everything below is a statement about code you can read here; file references +point at the exact place. + +## What it reads + +- **One cookie: `DedeUserID` on `https://www.bilibili.com`.** `shared/api.js` + calls `chrome.cookies.get({ url: 'https://www.bilibili.com', name: + 'DedeUserID' })` and reads no other cookie anywhere in the extension. That + value is your own numeric Bilibili user id. It is used as the `target_id` + query parameter of the medal-wall request and for nothing else — it is not + written to storage and not sent to any host other than Bilibili. +- **Your Bilibili session, implicitly.** The API calls in `shared/api.js` use + `fetch(url, { credentials: 'include' })`, so Chrome attaches the cookies it + already holds for `bilibili.com` — the same ones bilibili.com receives when + you browse it yourself. That is what makes "your follow list" mean *yours*. + Those cookies are sent by the browser to Bilibili and to no one else. + +## Which endpoints it calls + +All API calls go to `https://api.live.bilibili.com` (`API_BASE` in +`shared/constants.js`); `shared/api.js` contains every call site: + +| Endpoint | When | +|---|---| +| `/xlive/web-ucenter/user/MedalWall` | every refresh cycle | +| `/room/v1/Room/get_status_info_by_uids` | every cycle, for the uids currently in scope | +| `/xlive/web-ucenter/user/following` | only when **All other follows** is ticked, or when you open the "everything live" view | +| `/room/v1/Room/get_info` | hovering a live card, and adding a custom room | +| `/live_user/v1/Master/info` | resolving a custom room's streamer name | + +Two other kinds of Bilibili traffic: + +- **Images.** Avatars and stream covers come from the `hdslb.com` CDN. + Notification icons are fetched with `credentials: 'omit'` and + `referrerPolicy: 'no-referrer'` (`background/notify.js`), and the popup's + `` tags carry `referrerpolicy="no-referrer"` (`popup/cards.js`, + `popup/settings.js`) — so those image requests carry no cookies and no + referrer. +- **The hover preview.** In live-player mode the popup embeds + `https://www.bilibili.com/blackboard/live/live-activity-player.html` in an + iframe (`popup/preview.js`), only while you are hovering a live card. + +`manifest.json` declares exactly two host permissions, `https://*.bilibili.com/*` +and `https://*.hdslb.com/*`. No other host appears anywhere in the code. + +## Where the data goes + +Only to your own machine and to Bilibili. + +- **Local storage only.** Settings, marks, hidden list, custom rooms and the + last known stream states live in `chrome.storage.local` (`shared/storage.js`). + `chrome.storage.sync` is not used anywhere, so nothing is uploaded to your + Google account or copied to your other devices. +- **Export is a plain file save.** The export button builds a JSON `Blob` and + triggers a normal browser download (`popup/settings.js`); the file goes + wherever you save it. It contains settings only — custom rooms, marks, hidden + list, alert scope, view mode, refresh interval, preview options, appearance — + because `exportConfig` copies a fixed `SETTINGS_KEYS` allowlist and + deliberately leaves runtime state out (`shared/storage.js`). +- **The content script is narrow.** It runs on one URL pattern, the Bilibili + live-activity player page (`manifest.json` → `content_scripts.matches`), and + acts only on `postMessage` events whose `event.origin` is this extension's own + id, which it uses to sync mute and volume (`content_script.js`). + +## What is not collected + +- No analytics, telemetry, crash or usage reporting. There is no `sendBeacon`, + no `WebSocket`, no reporting endpoint in the codebase. +- No third-party servers. The author receives nothing — there is nowhere for it + to be sent to. +- No remote code. The icons in the popup are an inline SVG sprite rather than a + CDN stylesheet (`popup/popup.html`), and `manifest.json` declares no + `externally_connectable` and loads no remote script. + +## Revoking access + +- **Cut off site access:** `chrome://extensions` → BiliStreamMonitor → + **Details** → **Site access**. Without access to `bilibili.com` the extension + can no longer query anything. +- **Remove it:** `chrome://extensions` → **Remove**. Chrome discards the + extension together with its `chrome.storage.local` area; a JSON file you + exported yourself stays where you saved it. +- The `cookies` permission is granted at install time and Chrome offers no way + to revoke it on its own — uninstalling is how you take it back. diff --git a/README.md b/README.md index 0cd9c42..932e0e2 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,9 @@ it on is the only thing that makes the extension page your whole follow list. No ``, no analytics, no third-party requests. The content script runs on exactly one URL pattern, to keep the preview player in sync. +Which cookie is read, which endpoints are called and where the data stays is +written out endpoint by endpoint in [PRIVACY.md](PRIVACY.md). + ## Configuration Everything lives behind the gear in the popup: refresh interval (30 s minimum, diff --git a/README.zh-Hant.md b/README.zh-Hant.md index 1a52ecd..87e28ce 100644 --- a/README.zh-Hant.md +++ b/README.zh-Hant.md @@ -69,6 +69,8 @@ 沒有 ``,沒有任何分析工具,也不會對第三方發出請求。content script 只作用在唯一一個網址模式上,用來讓預覽播放器保持同步。 +讀了哪個 cookie、呼叫了哪些 endpoint、資料留在哪裡,逐條寫在 [PRIVACY.md](PRIVACY.md)(英文)。 + ## 設定 所有設定都收在 popup 裡的齒輪圖示後面:重新整理間隔(最短 30 秒、預設 60 秒)、popup 大小、頭像大小、間距、字型大小、亮/暗主題、預覽模式、隱藏清單,以及提醒範圍矩陣。