Is FadBlock Origin: Friendly YouTube™ Adblock safe?
FadBlock Origin fetches its ad-detection rules from a remote GitHub Pages endpoint on install and update.
On install, update, and user-triggered refresh, the extension requests a JSON config file from 0x48piraj.github.io/fadblock/fadblock.json. The response contains CSS selector strings that determine which DOM elements on YouTube are treated as ads, video players, and skip buttons. These selectors are written to local extension storage and used by the content script to locate and interact with ad elements.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote YouTube selector config drives page behavior
FadBlock Origin fetches a selector config from GitHub Pages on install, update, and via the popup Update button.
We saw a GET to it and a storage write with values like isAd=.ad-interrupting, videoPlayer=.video-stream; content.js uses them.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You install or update the extension, or press Update in its popup.
Those actions call the same selector-refresh path.
The extension downloads selector names and uses them to decide which YouTube elements to handle.
The downloaded values are stored locally before the content script reads them on YouTube pages.
200 OK; the response was parsed as JSON, and observed local storage included isAd=.ad-interrupting and videoPlayer=.video-stream.
- Ad marker selector.ad-interrupting
This tells the extension which YouTube page element means an ad is currently present.
- Video player selector.video-stream
This tells the extension which video element it can fast-forward when it decides an ad is playing.
- Stored selector fieldsisAd, videoContainer, videoPlayer, previewText, previewTextAlt, staticAds, skipButton, skipButtonAlt, surveyButton
The remote file can set the selector names the extension later uses while processing YouTube pages.
Remote config fetch, storage write, and selector use in shipped code
Background fetch and storage write
js/background.jsfunction init() { fetch("https://0x48piraj.github.io/fadblock/fadblock.json", { method: "GET" }).then((function(t) { return t.json() })).then((function(t) { chrome.storage.local.set({ isAd: t.isAd, videoContainer: t.videoContainer, videoPlayer: t.videoPlayer, previewText: t.previewText, previewTextAlt: t.previewTextAlt, staticAds: t.staticAds, skipButton: t.skipButton, skipButtonAlt: t.skipButtonAlt, surveyButton: t.surveyButton }) })), extpay.getUser().then((t => { t.paid || extpay.openTrialPage() }))}Install and update trigger
js/background.jschrome.runtime.onInstalled.addListener((t => { switch (t.reason) { case "install": console.info("EXTENSION INSTALLED"), chrome.storage.local.set({ skipCount: 0 }), init(); break; case "update": chrome.storage.local.get(["skipCount"]).then((t => void 0 !== t.skipCount ? console.info("EXTENSION UPDATED") : chrome.storage.local.set({ skipCount: 0 }))), init(); break; default: chrome.storage.local.get(["skipCount"]).then((t => void 0 !== t.skipCount ? console.info("BROWSER UPDATED") : chrome.storage.local.set({ skipCount: 0 }))), init() }}))Content script selector use
js/content.jsconst extpay = ExtPay("fadblock"), taimuRipu = async () => { await new Promise(((e, t) => { setTimeout((() => { chrome.storage.local.get(["skipCount", "isAd", "videoContainer", "videoPlayer", "previewText", "previewTextAlt", "skipButton", "skipButtonAlt", "surveyButton", "staticAds"], (function(e) { const t = document.querySelector(e.isAd), i = document.querySelector(e.previewText), o = document.querySelector(e.previewTextAlt); t && (i || o) && chrome.runtime.sendMessage({ message: "taimu-ripu" }), e.staticAds.forEach((e => { const t = document.querySelector(e); t && (t.style.display = "none") })) })), e() }), 100) })), taimuRipu() };taimuRipu();Popup update button fetch
js/popup.jsfunction getArtefacts() { fetch("https://0x48piraj.github.io/fadblock/fadblock.json", { method: "GET" }).then((function(e) { return e.json() })).then((function(e) { chrome.storage.local.set({ isAd: e.isAd, videoContainer: e.videoContainer, videoPlayer: e.videoPlayer, previewText: e.previewText, previewTextAlt: e.previewTextAlt, staticAds: e.staticAds, skipButton: e.skipButton, skipButtonAlt: e.skipButtonAlt, surveyButton: e.surveyButton }) }))}- 0x48piraj.github.io
GitHub Pages host serving fadblock.json, the selector configuration consumed by the extension.
What it can do
Permissions this extension asks for, as declared in version 2.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 youtube.com
https://*.youtube.com/*
Read and change your data on www.youtube.com
https://www.youtube.com/*
Run its own code inside the pages you visit
scripting
Store data in your browser
storage