Is FetchV - Video Downloader for m3u8 & hls safe?
FetchV is medium risk. The service worker captures headers of every media/XHR/object/other request site-wide, drops six standard headers, storing the rest. Testing confirmed non-standard and Authorization headers get stored. Reused only via download/preview.…
Who publishes itidxF - no other listings under this identity, 1 shared hostname
idxF - no other listings under this identity, 1 shared hostname
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 1 hostname
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Video downloader stores your page's request headers, including auth tokens
The service worker captures headers of every media/XHR/object/other request site-wide, drops six standard headers, storing the rest.
Testing confirmed non-standard and Authorization headers get stored.
Reused only via download/preview.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You visit any site and it loads a video, makes an XHR call, or requests an object/embed resource.
The listener is registered for all URLs, not just video-hosting sites.
The extension records that request's headers, strips out six standard ones, and saves the rest to local storage.
This happens for every qualifying request, whether or not you ever use the extension's download feature.
- Origin / Referer of the page you were onOrigin: https://hls-js.netlify.app (illustrative)
Records which page triggered the captured request.
- Browser/device fingerprint headerssec-ch-ua: "Chromium";v="128" (illustrative)
Identifies the specific browser and device configuration that made the request.
- Authorization/session header (if sent)Authorization: Bearer <redacted> (capture path confirmed; full value not observed end-to-end)
If a site attaches a bearer token or session header to a matching request, the same storage code keeps that value too.
Header capture and storage-write logic
Capturing every outgoing header
service-worker.jschrome.webRequest.onBeforeSendHeaders.addListener((function(e) { if (!e.initiator) return; if (0 !== e.initiator.indexOf("http")) return; const t = e.requestHeaders; s[e.requestId] = t || {}}), { urls: ["<all_urls>"], types: ["media", "xmlhttprequest", "object", "other"]}, ["requestHeaders", "extraHeaders"])Filtering and persisting non-standard headers
service-worker.jsconst l = ["range", "content-length", "content-type", "accept-encoding", "accept", "accept-language"];// ...const S = {};for (const e of O) l.includes(e.name.toLowerCase()) || (S[e.name] = e.value);P[m] = { storageKey: _, requestId: m, url: f, method: "POST" === g ? "POST" : "GET", format: y, contentType: r(y, E), name: I, size: w, headers: S, type: C}, chrome.storage.local.set({ [_]: P})Mirrors our test: three records stored under one per-tab key, with non-standard headers. Values above are representative, not verbatim.
- Location
- chrome.storage.local key storage<tabId> (observed as storage1585209826)
{ "storage1585209826": { "<requestId>": { "url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8", "name": "x36xhzz.m3u8", "type": "hls", "format": "m3u8", "method": "GET", "headers": { "Origin": "https://hls-js.netlify.app", "Referer": "https://hls-js.netlify.app/demo/", "sec-ch-ua": "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\"", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "cross-site" }, "storageKey": "storage1585209826" } }}We did not observe headers being sent to a remote server automatically just from browsing. In our test, stored headers were reused only after we triggered the extension's own download or preview flow, which reinstalls them as request-modifying rules applied to the extension's background fetches for the same media host — not to a separate third-party server. That doesn't remove the underlying exposure: any non-standard header a site sends on a matching request is written to durable local storage as soon as it's seen, whether or not you ever use the download feature.
FetchV Retains Login Cookies and Auth Headers From Any Site
FetchV's service worker records full headers of every media/XHR/object/other request site-wide, minus six routine ones, storing the rest, incl.
Cookie and Authorization, per tab.
A planted marker confirmed this; no exfiltration observed.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-200
- Source
- Dynamic sandbox
You browse to any website that loads a video, audio, or other matched-format file, while the page also makes other requests around the same time.
The header-capture listener fires on every media, xmlhttprequest, object, and "other" request on every site -- not only the site FetchV is trying to download from.
FetchV's service worker records every header sent with the request, including Cookie and Authorization, writing what it doesn't exclude to storage.local.
The capture runs before the extension even knows whether the matching response will qualify as a downloadable file.
- Session cookieCookie: w3s.theme=null; session_id=<value from the site>
Your logged-in session cookie for the site making the request is kept in full and written to local storage.
- Authorization headerAuthorization: Bearer <token from the site>
A bearer token or API key sent by the page is retained the same way, whenever one is present on a matched request.
- Headers dropped before storagerange, content-length, content-type, accept-encoding, accept, accept-language
These six header names are the only ones excluded -- everything else the site sends, including auth material, is kept.
We planted a marker in a test cookie, loaded an embedded video, and found the Cookie header, marker intact, stored per-tab in storage.local.
- Location
- chrome.storage.local key storage<tabId>, read from the extension's background service worker
{ "url": "https://www.w3schools.com/html/mov_bbb.mp4", "format": "mp4", "method": "GET", "headers": { "Cookie": "w3s.theme=null; canary_secret=<planted-marker>" }, "storageKey": "storage1585209825"}The allow-list only drops six routine headers -- Cookie and Authorization pass through
Header allow-list and the capture listener
extension/deobfuscated/service-worker.js:387-396l = ["range", "content-length", "content-type", "accept-encoding", "accept", "accept-language"];chrome.webRequest.onBeforeSendHeaders.addListener((function(e) { if (!e.initiator) return; if (0 !== e.initiator.indexOf("http")) return; const t = e.requestHeaders; s[e.requestId] = t || {}}), { urls: ["<all_urls>"], types: ["media", "xmlhttprequest", "object", "other"]}, ["requestHeaders", "extraHeaders"])Filtering the captured headers before they're stored
extension/deobfuscated/service-worker.js:583-598const S = {};for (const e of O) l.includes(e.name.toLowerCase()) || (S[e.name] = e.value);P[m] = { storageKey: _, requestId: m, url: f, method: "POST" === g ? "POST" : "GET", format: y, contentType: r(y, E), name: I, size: w, headers: S, type: C}, chrome.storage.local.set({ [_]: P})FetchV's popup later reads these stored headers to rebuild a `declarativeNetRequest` rule that reattaches the same headers when you play or download the matched video or HLS stream -- useful for videos that require a login to view. That legitimate purpose only needs the headers for the specific request being downloaded. The capture listener itself is broader: it fires on every media, XHR, object, and "other" request on every site, and the response-side filter decides only afterward whether a given exchange looks like a downloadable file, by which point the request's headers have already been read and, if a file match is found, stored.
Checks FetchV's own local storage for any request entry that still holds a Cookie or Authorization header, so you can see what the extension currently has retained.
- Chrome or Edge with FetchV installed and enabled
- Developer mode enabled in the extensions page
// Run inside FetchV's own service-worker DevTools console// (chrome://extensions -> Developer mode -> "service worker" link under FetchV).chrome.storage.local.get(null, (all) => { const AUTH_HEADER_NAMES = ["cookie", "authorization"]; let hits = 0; for (const [key, value] of Object.entries(all)) { if (!key.startsWith("storage") || typeof value !== "object" || !value) continue; for (const entry of Object.values(value)) { const headers = entry && entry.headers; if (!headers) continue; const found = Object.keys(headers).filter((name) => AUTH_HEADER_NAMES.includes(name.toLowerCase()) ); if (found.length) { hits++; console.log(`[${key}] ${entry.url} retained: ${found.join(", ")}`); } } } console.log( hits ? `${hits} stored request(s) currently contain a Cookie or Authorization header.` : "No stored requests currently contain a Cookie or Authorization header." );});- 1Open chrome://extensions (or edge://extensions), enable Developer mode, find FetchV, open its service worker console.
- 2Paste this script, Enter.
- 3Each printed line is a stored request whose headers include Cookie or Authorization.
What it can do
Permissions this extension asks for, as declared in version 3.2. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every secure site you visit
https://*/*
Read and change your data on every site you visit
http://*/*
See the address and title of every tab you have open
tabs
Watch every request your browser makes
webRequest
Store data in your browser
storage
Block and redirect the requests your browser makes
declarativeNetRequest
Run hidden pages in the background
offscreen
Run its own code inside the pages you visit
scripting