Is Screenshot YouTube Video safe?
Screenshot YouTube Video sends the current page URL and a persistent browser ID to api.videodlservice.com daily.
Screenshot YouTube Video runs a content script on every website and, once per day, sends an analytics event from the page. That event includes the current page URL, extension version details, service metadata, and a persistent generated browser identifier stored by the extension.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Daily Page URL Report to videodlservice Analytics
Screenshot YouTube Video sends a daily analytics POST from a normal page load to api.videodlservice.com, including the event name, a persistent browser identifier, and the current URL, linking a visit to example.com to that identifier.
You open a normal web page while the extension is installed.
The content script is configured for all HTTP and HTTPS pages and runs at the start of the document.
The extension can send a daily analytics event containing that page's URL.
In dynamic analysis, the event for https://www.example.com/ was posted to api.videodlservice.com with a persistent browser identifier.
| Content-Type | application/json; charset=utf-8 |
action=extension_daily hid=ec9db9e2-7915-4b2c-a08c-15d6ed87e08e media_source=https://www.example.com/.
| Field | Value | Why it matters | |
|---|---|---|---|
Daily event name | extension_daily | Shows that the request is the extension's once-per-day analytics event rather than a download action you initiated. | |
Browser identifier | ec9db9e2-7915-4b2c-a08c-15d6ed87e08e | Lets the service connect separate daily reports from the same browser profile over time. | |
Current page URL | https://www.example.com/ | Shows the page open in your tab when the daily event is created, including any path or query text in that URL. |
The shipped code that schedules and sends the daily URL report
const hid_defaultValue = esm_browser_v4();
const hid_name = "hid";
const hid_migrations = {
name: hid_name,
ups: [
{
v: 1,
up: (storage, currentValue) => {
if (typeof currentValue !== "string") {
return storage.set(hid_name, hid_defaultValue);
}
return null;
},
},
],
};const getSendEvent = store => (
async (data) => {
const {
defaultItem,
hid,
serviceName,
utm,
} = store.getState();
const defData = {
branch: config.branch,
hash: config.hash,
hid,
project: config.extType,
type: config.analyticsType,
version: config.version,
...utm,
format: defaultItem ? defaultItem.quality : "",
media_source: window.location.href,
service: serviceName,
// format_high: "MAX_QUALITY"
};
try {
const headers = [["Content-Type", "application/json; charset=utf-8"]];
const body = JSON.stringify({ ...defData, ...data });
network_sendPost(config.analyticsUrl, headers, body, true);
} catch (e) {}
}
);/* harmony default export */ const send_daily = (async (sendEvent) => {
const lastLoaded = await index_ext.get("extensionLoaded");
if (!lastLoaded || (Date.now() - lastLoaded > 24 * 60 * 60 * 1000)) {
index_ext.set("extensionLoaded", Date.now());
sendEvent({ action: "extension_daily" });
}
});
const checkServices = async () => {
console.log("check services");
inject_script(document, "patch-xhr.js", {});
const timeoutId = setTimeout(() => {
try {
inject_script(document, "set-global.js", { name: "__stopWatching", value: true });
inject_script(document, "set-global.js", { name: "__jsons", value: [] });
} catch {}
}, 5000);
const modules = integration_modules();
const modulesNames = Object.keys(modules);
let uninstallObserver;
/* eslint-disable no-await-in-loop */
for (let i = 0; i < modulesNames.length; i++) {
const moduleName = modulesNames[i];
const module = modules[moduleName];
const { isServiceUrl, Service } = module;
if (isServiceUrl(window.location.href)) {
if (!uninstallObserver) {
uninstallObserver = true;
const uninstallSelf = () => window.location.reload();
addListener("uninstallSelf", uninstallSelf);
}
console.log("We've got a hit!!!", moduleName);
clearTimeout(timeoutId);
const store = await content_script_store(moduleName);
const {
eventRouter,
setModule,
setServiceName,
} = store.getState();
setModule(module);
const service = new Service(eventRouter);
setServiceName(service.serviceName);
service.start();
}
}
/* eslint-enable no-await-in-loop */
// send daily even if there is no module to engage
const defaultStore = await content_script_store("defaultService");
const { sendEvent } = defaultStore.getState();
send_daily(sendEvent);
};const start = async () => {
if (!in_iframe()) {
set_info();
}
// start default service anyway
default_service.start();
const onContentLoaded = () => {
const alreadyStarted = check_if_injected();
if (alreadyStarted) return;
check_services();
};
if (document.readyState !== "loading") {
onContentLoaded();
} else {
document.addEventListener("DOMContentLoaded", () => {
onContentLoaded();
}, false);
}
};- api.videodlservice.com
Receives the daily analytics POST containing the event name, persistent browser identifier, and current page URL.
What it can do
Permissions this extension asks for, as declared in version 0.7.69.0. 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://*/*
Read and change your data on every secure site you visit
https://*/*
Store data in your browser
storage
Store an unlimited amount of data in your browser
unlimitedStorage
Where it sends data
Destinations our analysis observed Scr YT Video 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.
- api.videodlservice.com
Scr YT Video sends data to api.videodlservice.com. 2 other extensions we have analysed send data here.