Is Video Downloader - Download M3U8, MP4, HLS and Live Streams safe?
Qooly is high risk. Every install fetches network filtering rules from qooly.com via declarativeNetRequest, able to add/modify/remove headers (cookie, referer, origin) on any request, any site. 8 rules applied at startup; the server can change rules anytime.…
Who publishes itqooly - 1 other listing from the same operator, none carrying a finding
qooly - 1 other listing from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
1 other listing published from this account, 30k+ users between them, none of them carrying a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote Server Controls Your Browser's Network Rules
Every install fetches network filtering rules from qooly.com via declarativeNetRequest, able to add/modify/remove headers (cookie, referer, origin) on any request, any site. 8 rules applied at startup; the server can change rules anytime.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You install the extension or restart your browser.
The extension fetches a list of network rules from qooly.com and applies them to your browser.
These rules can rewrite or remove HTTP headers on any request you make to any website, without any visible indication.
JSON array of 8 declarativeNetRequest rules including modifyHeaders actions on cookie, referer, origin, sec-fetch-dest, sec-fetch-mode, and sec-fetch-site headers across matched URL patterns.
The code that fetches and applies the remote rules
The fetch action
// Runs on startup (and every 24 hours thereafter).// xt.a === 'qooly.com' (hardcoded constant)actions.UPDATE_RULES = function(store) { const lastFetch = store.state.storage.remoteConfig.rulesFetchDate; const stale = !lastFetch || lastFetch + 86_400_000 < Date.now(); // 24h if (!stale) return; fetch('https://qooly.com/domains-list.json?v=1') .then(r => r.json()) .then(rules => { if (rules && rules.length) { store.commit('SET_RULES', rules); // writes to chrome.storage.local store.commit('SET_RULES_FETCH_DATE', Date.now()); } });};The rule application — fires whenever rules are updated in storage
// Called when Vuex emits 'remoteConfig/SET_RULES'class DynamicRules { setRules() { chrome.declarativeNetRequest.getDynamicRules().then(existingRules => { chrome.declarativeNetRequest.updateDynamicRules({ addRules: store.state.storage.remoteConfig.rules, // server's list removeRuleIds: existingRules.map(r => r.id) // clears prior rules first }); }); }}- qooly.com
Developer domain for Video Downloader. Serves both the DNR rule list (/domains-list.json) and the M3U8 domain allow-list (/master-list.json). Also hosts the uninstall survey page.
Reads the current dynamic declarativeNetRequest rules applied by Video Downloader and prints them to the console. Run this in the extension's service worker DevTools to see exactly what rules are currently active.
- Chrome with Developer mode enabled
- Video Downloader extension installed
// check-dnr-rules.js// Reads the active declarativeNetRequest dynamic rules set by Video Downloader.// Run this snippet in the service worker DevTools console for this extension.chrome.declarativeNetRequest.getDynamicRules().then(rules => { if (rules.length === 0) { console.log('[DNR-CHECK] No dynamic rules are currently active.'); return; } console.log(`[DNR-CHECK] ${rules.length} dynamic rule(s) are currently active:`); rules.forEach((rule, i) => { console.log(`\nRule ${i + 1} (id=${rule.id}):`); console.log(' condition:', JSON.stringify(rule.condition, null, 2)); console.log(' action:', JSON.stringify(rule.action, null, 2)); });});// To also see what qooly.com is currently serving:fetch('https://qooly.com/domains-list.json?v=1') .then(r => r.json()) .then(rules => { console.log('\n[DNR-CHECK] Current server-side rules from qooly.com/domains-list.json:'); console.log(JSON.stringify(rules, null, 2)); });- 1Open chrome://extensions, enable Developer mode.
- 2Find the extension, click 'service worker' to open DevTools.
- 3Paste this script, press Enter.
- 4Output shows active rules and what qooly.com is serving.
Remote Server Decides Which Streaming Sites the Extension Intercepts
The extension fetches a streaming-domain list from qooly.com at startup, deciding which HLS/M3U8 streams to intercept.
The list includes 'twimg.com' and 'hls.ted.com', stored locally.
The server can change it anytime, no update needed.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You install the extension or restart your browser.
The extension fetches a list of streaming domains from qooly.com and uses it to decide which video streams to intercept.
The server controls which sites' HLS streams the extension processes, and can change that list remotely without any extension update.
JSON object with field 'master-m3u8-domains' containing an array of streaming domain hostnames. Observed response: {"master-m3u8-domains": ["twimg.com", "hls.ted.com"]}. Extension storage confirmed rawMasterDomains set to ["twimg.com", "hls.ted.com"] matching the server response exactly.
The extension saved the server's domain list locally. The timestamp (2026-04-15T07:29:00 UTC) confirms it was set at session start.
- Location
- chrome.storage.local key '@@vwe-persistence', field remoteConfig.rawMasterDomains
{ "rawMasterDomains": [ "twimg.com", "hls.ted.com" ], "rawMasterDomainsFetchDate": 1776238140419}The code that fetches and applies the remote domain list
The fetch action
// Runs on startup (and every 24 hours thereafter).// xt.a === 'qooly.com' (hardcoded constant)actions.UPDATE_RAW_MASTER_DOMAINS = function(store) { const lastFetch = store.state.storage.remoteConfig.rawMasterDomainsFetchDate; const stale = !lastFetch || lastFetch + 86_400_000 < Date.now(); // 24h if (!stale) return; fetch('https://qooly.com/master-list.json') .then(r => r.json()) .then(data => { const domains = data && data['master-m3u8-domains'] ? data['master-m3u8-domains'] : []; store.commit('SET_RAW_MASTER_DOMAINS', domains); // writes to chrome.storage.local store.commit('SET_RAW_MASTER_DOMAINS_FETCH_DATE', Date.now()); });};The intercept filter — checks each intercepted response URL against the server's list
// bg.js:7202 — inside the webRequest onHeadersReceived handlerconst masterDomains = store.state.storage.remoteConfig.rawMasterDomains;let domainMatch = false;for (let i = 0; i < masterDomains?.length; i++) { if (request.url.includes(masterDomains[i])) { domainMatch = true; // this request's URL is on the server-supplied list }}// Only process this response as an HLS stream if domainMatch is true// (or if the URL ends in .m3u8 / content-type matches)- qooly.com
Developer domain for Video Downloader. Serves the M3U8 allow-list (/master-list.json) and DNR rule list (/domains-list.json). Observed returning twimg.com and hls.ted.com.