Is Video Downloader PLUS safe?
Video Downloader PLUS is high risk. Our dynamic analysis observed the worker POST to videodetect.fdown.net on every tab activation or page load on Wikipedia, YouTube, Reddit and Amazon, carrying the page URL, referrer, and a tracking ID; fires on any page, not just video.…
Who publishes itFDOWN - no other listings under this identity
FDOWN - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Video Downloader PLUS reports every page you visit to fdown.net
Our dynamic analysis observed the worker POST to videodetect.fdown.net on every tab activation or page load on Wikipedia, YouTube, Reddit and Amazon, carrying the page URL, referrer, and a tracking ID; fires on any page, not just video.
You switch to a tab or a page finishes loading, on any website.
This includes ordinary browsing, not just video pages or use of the download feature.
The extension's background service worker sends the exact page URL, its referrer, and a permanent tracking ID to fdown.net's server.
The request fires immediately, before you interact with the extension in any way.
| Field | Value | Why it matters | |
|---|---|---|---|
Page you're viewing | https://en.wikipedia.org/wiki/Web_browser | The exact address of the page you just opened or switched to, on any website. | |
Referring page | https://en.wikipedia.org/wiki/Internet | The page that linked you to the page you're viewing. | |
Permanent tracking ID | 8554ac37-fb10-4e47-b814-d520f0dfccac | A random ID generated once and stored permanently, letting fdown.net link together everything you browse across every session. |
| Content-Type | application/json |
{
"targetUrl": "https://en.wikipedia.org/wiki/Web_browser",
"referrerUrl": "https://en.wikipedia.org/wiki/Internet",
"userId": "8554ac37-fb10-4e47-b814-d520f0dfccac"
}The VideoPlatformDetect class registers navigation listeners and posts every visited URL
class VideoPlatformDetect {
config = {
url: "https://videodetect.fdown.net/api/videoplatform/"
};
videoPlatformDetectInfo = {};
uuid = null;
async run() {
chrome.runtime.onMessage.addListener(async t => {
"requestVideoPlatformDetect" === t.subject && this.sendVideoPlatformDetectMessage(t.tabId)
}), chrome.tabs.query({
active: !0,
currentWindow: !0
}, t => {
this.fetchTabs(null, t)
}), chrome.tabs.onActivated.addListener(e => {
chrome.tabs.query({
active: !0,
currentWindow: !0
}, t => {
this.fetchTabs(e.tabId, t)
})
}), chrome.tabs.onUpdated.addListener(e => {
chrome.tabs.query({
active: !0,
currentWindow: !0
}, t => {
this.fetchTabs(e, t)
})
}), this.uuid = await this.getUuid()
}
fetchTabs(t, e) {
for (var o of e) null !== t && o.id !== t || this.fetchInfo(o)
}
async fetchInfo(t) {
var e = t.url,
o = t.id;
if (!this.videoPlatformDetectInfo[o] || this.videoPlatformDetectInfo[o].url !== e)
if (this.videoPlatformDetectInfo[o] = {
score: null,
url: e
}, this.isValidUrl(e)) try {
var n = await this.getReferrer(t),
{
score: r,
cause: i
} = await (await fetch(this.config.url, {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
targetUrl: e,
referrerUrl: n,
userId: this.uuid
})
})).json();
this.videoPlatformDetectInfo[o] = {
score: r,
cause: i,
url: e
}, this.sendVideoPlatformDetectMessage(o)
} catch (t) {
this.videoPlatformDetectInfo[o] = void 0
} else this.sendVideoPlatformDetectMessage(o)
}
async getUuid() {
const e = await new Promise(e => {
chrome.storage.sync.get(["uuid"], t => {
e(t.uuid)
})
});
if (e && this.validateUUID4(e)) return e;
{
const e = this.makeUUID();
return new Promise(t => {
chrome.storage.sync.set({
uuid: e
}, async () => {
t(e)
})
})
}
}
makeUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (t, e) => ("x" === t ? 16 * Math.random() | 0 : 3 & e | 8).toString(16))
}
validateUUID4(t) {
return new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i).test(t)
}
isValidUrl(t) {
if ("about:blank" === t || t.startsWith("chrome://")) return !1;
try {
return new URL(t), !0
} catch (t) {
return !1
}
}
sendVideoPlatformDetectMessage(t) {
chrome.runtime.sendMessage({
subject: "videoPlatformDetect",
data: this.videoPlatformDetectInfo[t]
})
}
getReferrer(t) {
return new Promise(e => {
chrome.scripting.executeScript({
target: {
tabId: t.id
},
func: function() {
return document.referrer
}
}).then(t => {
e(t[0].result)
})
})
}
}const videoPlatformDetect = new VideoPlatformDetect;
window.videoPlatformDetect = window.videoPlatformDetect || videoPlatformDetect, (async () => {
fbdExtensionApp.Media.init(), fbdExtensionApp.MainButton.refreshMainButtonStatus(), chrome.runtime.setUninstallURL("https://fdown.net/ext/uninstall.php"), chrome.tabs.query({
active: !0,
currentWindow: !0
}, function(t) {
0 < t.length && set_popup(t[0].id)
}), chrome.storage.local.get(["fbdown-uuid"], function(t) {
Object.keys(t).length || (t = uuid.v4(), chrome.storage.sync.set({
"fbdown-uuid": t
}, async function() {}))
}), await videoPlatformDetect.run(), fbdExtensionApp.Utils.setUUID(videoPlatformDetect.uuid)
})(), chrome.tabs.onUpdated.addListener(function(t, e, o) {
"complete" == o.status && set_popup(t)
}), chrome.tabs.onActivated.addListener(function(t) {
set_popup(t.tabId)
});The extension does have a stored consent flag (`share_stats_consentment`) and a helper that checks it, but that check only gates whether the popup rebuilds its list of detected downloads when you open it. The navigation listeners that send your page URL and referrer to videodetect.fdown.net are started unconditionally at browser startup, before any consent check runs and regardless of that flag's value.
- videodetect.fdown.net
Receives the current tab URL, referrer, and persistent tracking ID on every navigation; operated by fdown.net, the extension's own developer.
Consent Choices Sent With Persistent UUID
When you choose a consent option in Video Downloader PLUS, the popup sends it to the worker, which reads its UUID and posts the value to consentmangement.fdown.net, the same UUID sent as userId in the video-detection request.
You make a consent selection in the extension popup.
The popup has separate buttons for agreeing, accepting all, and confirming the custom setting.
The extension sends your selected consent value with a stored UUID.
That UUID is the same stored identifier used in another background request.
| Field | Value | Why it matters | |
|---|---|---|---|
Your consent answer | true | This records whether you accepted sharing in the extension's consent prompt. | |
Your extension UUID | 8b8b0d10-4c7c-4fd6-a62f-6f328a04b68d (illustrative) | This lets the consent record be tied to the same browser profile over time. |
| Content-Type | application/json |
The popup sends the selected consent value to the background worker
function sendConsentChoice(value) {
chrome.runtime.sendMessage({ subject: "SaveConsentChoice", value: value }, function(response) {
response.status;
});
}
function showConsentDialog() {
var dialog = document.getElementById("disclaimer_message");
if (dialog) dialog.classList.remove("d-none");
var agree = document.getElementById("dismiss-disclaimer-agree");
if (agree) {
agree.addEventListener("click", function(event) {
event.preventDefault();
fbdExtensionApp.Utils.setShareStatsConsentmentValue(true);
sendConsentChoice(true);
var dialog = document.getElementById("disclaimer_message");
if (dialog) dialog.classList.add("d-none");
}, false);
}
var customize = document.getElementById("dismiss-disclaimer-customize");
if (customize) {
customize.addEventListener("click", function(event) {
var dialog = document.getElementById("disclaimer_message");
if (dialog) dialog.classList.add("d-none");
var customizeScreen = document.getElementById("customize_screen");
if (customizeScreen) customizeScreen.classList.remove("d-none");
var accept = document.getElementById("dismiss-disclaimer-accept");
if (accept) {
accept.addEventListener("click", function(event) {
event.preventDefault();
fbdExtensionApp.Utils.setShareStatsConsentmentValue(true);
sendConsentChoice(true);
if (customizeScreen) customizeScreen.classList.add("d-none");
}, false);
}
var confirm = document.getElementById("dismiss-disclaimer-confirm");
if (confirm) {
confirm.addEventListener("click", function(event) {
event.preventDefault();
if (document.getElementById("URLs").checked) {
fbdExtensionApp.Utils.setShareStatsConsentmentValue(true);
sendConsentChoice(true);
} else {
fbdExtensionApp.Utils.setShareStatsConsentmentValue(false);
sendConsentChoice(false);
}
if (customizeScreen) customizeScreen.classList.add("d-none");
}, false);
}
var collapsibles = document.getElementsByClassName("collapsible");
for (var index = 0; index < collapsibles.length; index++) {
collapsibles[index].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
content.style.maxHeight = content.style.maxHeight ? null : content.scrollHeight + "px";
});
}
}, false);
}
}The background worker adds the UUID and posts the consent record
class SaveConsentChoice {
constructor() {
this._addListener();
}
_addListener() {
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if ("SaveConsentChoice" === message.subject) {
return this.saveRequest(message.value)
.then(success => {
sendResponse({ success: success });
})
.catch(error => {
sendResponse({ success: false, error: error });
}), true;
}
});
}
async saveRequest(consent) {
var stored = await chrome.storage.sync.get(["uuid"]);
await fetch("https://consentmangement.fdown.net/api/consent", {
method: "POST",
cache: "no-cache",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ uuid: stored.uuid, consent: consent })
});
return true;
}
}async fetchInfo(tab) {
var url = tab.url;
var tabId = tab.id;
if (!this.videoPlatformDetectInfo[tabId] || this.videoPlatformDetectInfo[tabId].url !== url) {
if (this.videoPlatformDetectInfo[tabId] = { score: null, url: url }, this.isValidUrl(url)) {
try {
var referrer = await this.getReferrer(tab);
var response = await fetch(this.config.url, {
method: "POST",
cache: "no-cache",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ targetUrl: url, referrerUrl: referrer, userId: this.uuid })
});
var parsed = await response.json();
this.videoPlatformDetectInfo[tabId] = { score: parsed.score, cause: parsed.cause, url: url };
this.sendVideoPlatformDetectMessage(tabId);
} catch (error) {
this.videoPlatformDetectInfo[tabId] = void 0;
}
} else {
this.sendVideoPlatformDetectMessage(tabId);
}
}
}
async getUuid() {
const existing = await new Promise(resolve => {
chrome.storage.sync.get(["uuid"], value => {
resolve(value.uuid);
});
});
if (existing && this.validateUUID4(existing)) return existing;
const generated = this.makeUUID();
return new Promise(resolve => {
chrome.storage.sync.set({ uuid: generated }, async () => {
resolve(generated);
});
});
}- consentmangement.fdown.net
Receives the consent choice together with the stored extension UUID.
- videodetect.fdown.net
Receives the same stored UUID as userId in video-platform detection requests.
What it can do
Permissions this extension asks for, as declared in version 6.5.5. 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 site you visit
http://*/* and 1 more
Read and change your data on every secure site you visit
https://*/*
See the address and title of every tab you have open
tabs
Run its own code inside the pages you visit
scripting
Watch every request your browser makes
webRequest
Store an unlimited amount of data in your browser
unlimitedStorage
See, disable and uninstall your other extensions, including your security ones
management
Start, monitor and manage your downloads
downloads
Store data in your browser
storage