Ad Bear
Jo
ADBear is a comprehensive Network Monitoring and DNS filtering extension for firefox
Bilder in Mozilla Add-ons
What changed between versions
This section shows what the Qwen3 model detected in the source code of the last iteration of this extension — new features, permission changes, data flow, and end-user impact.
3/100
Lowv0.0.1 → v0.1.0
What this update means for you
Layperson summary from Qwen3
Das Update verbessert die Blockierung von Werbung und Tracker mit besserer Erkennung von problematischen Seiten. Es fügt neue Filterquellen hinzu und macht die Erweiterung stabiler auf Mobilgeräten. Das Update ist sicher und sinnvoll.
Ad Bear überwacht Netzwerkverbindungen, nutzt GeoIP, Bedrohungsdaten und Blocklisten, um Domains und IPs zu blockieren.
Privacy impact
Gering: Keine neuen sensiblen Berechtigungen oder Datenabflüsse, aber Kontakt zu externen Blocklisten.
What users will notice
Behaviour or UI changes the end user sees
- Neue Rechtsklick-Optionen durch Kontextmenüs
- Bessere Blockierung von Subdomains basierend auf Eltern-Domains
- Hinweis im Log bei fehlendem nativeMessaging (z. B. auf Mobilgeräten)
Feature changes
- Unterstützung für Kontextmenüs via 'contextMenus'-Berechtigung
- Verbesserte Domain-Blockierung mit Whitelist-Überprüfung entlang der Eltern-Domain-Kette
- Erweiterte IP-Blockierung nur bei literalen IP-Adressen
- Erkennung fehlender nativeMessaging-Unterstützung (z. B. auf Mobilgeräten)
Permission & access changes
What the extension can now touch that it couldn't before
- contextMenus
Code changes
6 modified code files · raw diff available · click to unfold
Code changes
6 modified code files · raw diff available · click to unfold
Robustere Domain- und IP-Block-Logik mit vollständiger Parent-Chain-PrüfungSichere nativeMessaging-Verbindung nur, wenn verfügbarExplizite Versionsanforderung ab Firefox 115.0
- https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
- https://big.oisd.nl/
- https://api.ipify.org/
background.js+44−5
@@ -191,17 +191,31 @@// ==================== WEBREQUEST LISTENERS ====================function isDomainBlocked(domain) {if (!filteringEnabled || !domain) return false;const d = domain.toLowerCase().replace(/\.$/, "");// Whitelist check (walk parent chain)let current = d;while (current) {if (whitelistedDomains.has(current)) return false;const firstDot = current.indexOf(".");if (firstDot === -1) break;current = current.substring(firstDot + 1);if (!current.includes(".")) break;}if (whitelistedDomains.has(domain)) return false;if (blockedIps.has(domain)) return true;// Domain block check (walk parent chain)current = d;let current = domain;while (current) {if (blockedDomains.has(current)) return true;const firstDot = current.indexOf(".");if (firstDot === -1) break;current = current.substring(firstDot + 1);if (!current.includes(".")) break;if (!current.includes(".")) break; // Don't block TLDs}// IP-based block: only if domain is literally an IPif (/^(\d{1,3}\.){3}\d{1,3}$/.test(d) && blockedIps.has(d)) return true;return false;}@@ -359,6 +373,7 @@let nativeRetries = 0;function connectNative() {if (typeof browser.runtime.connectNative === "undefined") return; // Androidif (connecting || nativePort) return;connecting = true;if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }@@ -440,7 +455,11 @@if (data.filteringEnabled) filteringEnabled = data.filteringEnabled;}).catch(() => {});if (typeof browser.runtime.connectNative !== "undefined") {connectNative();} else {console.info("Ad Bear: native messaging unavailable (mobile) — GeoIP/bandwidth disabled");}connectNative();// ==================== MESSAGE HANDLER ====================browser.runtime.onMessage.addListener((message, sender, sendResponse) => {@@ -540,7 +559,27 @@resolveDomain(message.domain).then((resolved) => {sendResponse({ success: !!resolved, addresses: resolved ? resolved.result.addresses : [] });}).catch(() => sendResponse({ success: false, addresses: [] }));return true;case "get_platform":(async () => {try {const info = await browser.runtime.getPlatformInfo();sendResponse({os: info.os, arch: info.arch,isAndroid: info.os === "android",hasSidebar: typeof browser.sidebarAction !== "undefined",hasContextMenus: typeof browser.contextMenus !== "undefined",hasNativeMessaging: typeof browser.runtime.connectNative !== "undefined",});} catch { sendResponse({ os: "unknown", isAndroid: false }); }})();return true;default:console.warn("Ad Bear: unknown action:", message.action);sendResponse({ error: "unknown_action", action: message.action });return false;}});
manifest.json+4−2
@@ -1,7 +1,7 @@{"manifest_version": 3,"name": "Ad Bear","version": "0.1.0","version": "0.0.1","description": "Monitor network connections with GeoIP, threat intel, blocklists, and firewall control","browser_specific_settings": {"gecko": {@@ -10,7 +10,8 @@"required": ["websiteActivity"]},"strict_min_version": "115.0"}}},"permissions": [@@ -20,6 +21,7 @@"webNavigation","dns","tabs","contextMenus","nativeMessaging"],"host_permissions": [
popup/popup.js+50−3
@@ -58,6 +58,22 @@await browser.runtime.sendMessage({ action: "set_filtering", enabled });dnsFilterStatus.textContent = enabled ? "On" : "Off";dnsFilterStatus.className = enabled ? "toggle-status active" : "toggle-status";// If enabling filtering, check if blocklists are loaded.// If not, open the sidebar/dashboard so the user can download them.if (enabled) {const stats = await browser.runtime.sendMessage({ action: "get_stats" });if (stats && stats.domainCount === 0) {dnsFilterStatus.textContent = "On — download lists!";dnsFilterStatus.className = "toggle-status warning";// Open dashboard so user can download blocklistsif (!isAndroid && typeof browser.sidebarAction !== "undefined") {try { await browser.sidebarAction.open(); } catch {}} else {try { await browser.tabs.create({ url: browser.runtime.getURL("sidebar/sidebar.html") }); } catch {}}}}} catch (e) {dnsFilterToggle.checked = !enabled;}@@ -69,11 +85,42 @@ping();fetchDNSStats();});let isAndroid = false;async function detectPlatform() {try {const info = await browser.runtime.getPlatformInfo();isAndroid = info.os === "android";} catch { isAndroid = false; }if (isAndroid) {if (openSidebar) openSidebar.textContent = "Open Dashboard";document.body.classList.add("mobile");} else if (typeof browser.sidebarAction === "undefined") {if (openSidebar) openSidebar.textContent = "Open Dashboard";}}openSidebar.addEventListener("click", async () => {// Desktop: open sidebar. Android/fallback: open in new tab.if (!isAndroid && typeof browser.sidebarAction !== "undefined") {try {await browser.sidebarAction.open();window.close();return;} catch (e) {console.warn("sidebarAction.open failed, falling back to tab:", e);}}try {await browser.tabs.create({ url: browser.runtime.getURL("sidebar/sidebar.html") });window.close();} catch (e) {console.error("Could not open dashboard:", e);}openSidebar.addEventListener("click", () => {browser.sidebarAction.open();window.close();});detectPlatform();ping();fetchDNSStats();
popup/popup.css+11−0
@@ -371,4 +371,15 @@::-webkit-scrollbar-thumb:hover {background: var(--border-light);}/* ===== Mobile (Android) ===== */body.mobile #app { min-width: 280px; max-width: 100vw; }body.mobile .header-right { display: none; }body.mobile .stats-grid { grid-template-columns: 1fr; }body.mobile .stat-card { padding: 8px 12px; }body.mobile .dns-toggle-bar { padding: 12px; }body.mobile .section-label { padding: 8px 12px 4px; }body.mobile button, body.mobile .toggle-switch { min-height: 44px; }body.mobile .header { padding-top: max(12px, env(safe-area-inset-top)); }
sidebar/sidebar.js+93−13
@@ -1,3 +1,35 @@// ===== Mobile detection (Android compatibility) =====(async function detectMobile() {try {const info = await browser.runtime.getPlatformInfo();if (info.os === "android") {document.body.classList.add("mobile");console.debug("Ad Bear: mobile mode (Android)");}} catch {}if (window.innerWidth < 600) document.body.classList.add("mobile");window.addEventListener("resize", () => {if (window.innerWidth < 600) document.body.classList.add("mobile");else document.body.classList.remove("mobile");});})();async function checkNativeAvailability() {try {const platform = await browser.runtime.sendMessage({ action: "get_platform" });if (platform && !platform.hasNativeMessaging) {const banner = document.createElement("div");banner.style.cssText = "background:#fef3c7;color:#92400e;padding:8px 12px;font-size:12px;border-bottom:1px solid #fcd34d;";banner.textContent = "GeoIP/bandwidth unavailable on mobile. Connection tracking still works.";document.body.insertBefore(banner, document.body.firstChild);document.querySelectorAll(".requires-native").forEach(el => el.style.display = "none");}} catch {}}if (document.readyState === "loading") {document.addEventListener("DOMContentLoaded", checkNativeAvailability);} else { checkNativeAvailability(); }let connections = [];let filteredConnections = [];let previousConnections = new Set();@@ -1460,7 +1492,7 @@browser.runtime.sendMessage({action: "update_blocklist",domains: [...dnsFilteringDomains].slice(0, 100000),ips: [], // IPs no longer used (parser now extracts domains)ips: [...autoBlocklistIps],whitelist: [...whitelistedDomains],enabled: dnsFilteringEnabled}).catch(() => {});@@ -1600,31 +1632,79 @@async function fetchBlocklistFeed(url) {try {const resp = await fetch(url);if (!resp.ok) {console.warn(`Ad Bear: blocklist ${url} returned HTTP ${resp.status}`);return new Set();}if (!resp.ok) return new Set();const text = await resp.text();const domains = new Set();// Regex: valid domain (RFC 1035 simplified) — excludes pure IPsconst domainRegex = /^(?!^\d+\.\d+\.\d+\.\d+$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/i;const ips = new Set();const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/;for (const line of text.split("\n")) {const t = line.trim();// Skip comments and empty linesif (!t || t.startsWith("#") || t.startsWith("!")) continue;// AdBlock format: ||example.com^ or ||example.com^$optionsif (t.startsWith("||")) {const match = t.match(/^\|\|([a-z0-9.-]+)\^?/i);if (match) {const d = match[1].toLowerCase();// Filter out IPs and invalid domainsif (!ipRegex.test(d) && domainRegex.test(d)) {domains.add(d);}}continue;}// Hosts format: IP domain [aliases...]// e.g. "0.0.0.0 example.com" or "127.0.0.1 example.com www.example.com"if (!t || t.startsWith("#")) continue;const parts = t.split(/\s+/);if (parts.length >= 2 && ipRegex.test(parts[0])) {// Take all parts after the IP — they're domain aliasesfor (let i = 1; i < parts.length; i++) {const d = parts[i].toLowerCase();if (domainRegex.test(d) && d !== "localhost" && d !== "ip6-localhost" && d !== "broadcasthost") {domains.add(d);}}continue;}// Domain-only line (some lists are just "example.com" one per line)if (parts.length === 1) {const d = parts[0].toLowerCase();if (domainRegex.test(d) && !ipRegex.test(d)) {domains.add(d);}continue;}if (ipRegex.test(parts[0]) && parts[0] !== "0.0.0.0" && parts[0] !== "127.0.0.1") ips.add(parts[0]);else if (parts.length >= 2 && ipRegex.test(parts[1]) && parts[1] !== "0.0.0.0" && parts[1] !== "127.0.0.1") ips.add(parts[1]);}console.debug(`Ad Bear: parsed ${domains.size} domains from ${url.split("/").pop()}`);return domains;} catch (e) {console.warn(`Ad Bear: failed to fetch blocklist ${url}:`, e.message);return new Set();}return ips;} catch (e) { return new Set(); }}async function updateAutoBlocklist() {if (!blocklistAutoToggle.checked) { blocklistStatus.textContent = "Auto-download disabled"; return; }blocklistStatus.textContent = "Downloading...";for (const feed of BLOCKLIST_FEEDS) {blocklistStatus.textContent = `Downloading ${feed.name}...`;const domains = await fetchBlocklistFeed(feed.url);for (const d of domains) dnsFilteringDomains.add(d);blocklistStatus.textContent = `${feed.name}: ${dnsFilteringDomains.size} domains total`;const ips = await fetchBlocklistFeed(feed.url);for (const ip of ips) autoBlocklistIps.add(ip);}try { localStorage.setItem(AUTO_BLOCKLIST_KEY, JSON.stringify([...autoBlocklistIps])); localStorage.setItem(AUTO_BLOCKLIST_KEY + "_updated", new Date().toLocaleString()); } catch (e) {}try { await browser.runtime.sendMessage({ action: "update_blocklist", domains: [...dnsFilteringDomains].slice(0, 100000), ips: [], whitelist: [...whitelistedDomains], enabled: dnsFilteringEnabled }); } catch (e) {}try { await browser.runtime.sendMessage({ action: "update_blocklist", domains: [...dnsFilteringDomains].slice(0, 100000), ips: [...autoBlocklistIps], whitelist: [...whitelistedDomains], enabled: dnsFilteringEnabled }); } catch (e) {}updateBlocklistStatus();render();}@@ -1686,7 +1766,7 @@for (const d of manualBlockedDomains) all.add(d);const domains = [...all];try { localStorage.setItem("ipwatch_dns_filtering_domains", JSON.stringify(domains)); localStorage.setItem("ipwatch_dns_filtering_updated", new Date().toLocaleString()); } catch (e) {}try { await browser.runtime.sendMessage({ action: "update_blocklist", domains: domains.slice(0, 100000), ips: [], whitelist: [...whitelistedDomains], enabled: true }); } catch (e) {}try { await browser.runtime.sendMessage({ action: "update_blocklist", domains: domains.slice(0, 100000), ips: [...autoBlocklistIps], whitelist: [...whitelistedDomains], enabled: true }); } catch (e) {}dnsFilteringUpdateBtn.disabled = false;updateDnsFilteringStatus();}@@ -2605,7 +2685,7 @@await browser.runtime.sendMessage({action: "update_blocklist",domains: dnsFilteringDomains.slice(0, 100000),ips: [], // IPs no longer used (parser now extracts domains)ips: [...autoBlocklistIps],whitelist: [...whitelistedDomains],enabled: dnsFilteringEnabled});
sidebar/sidebar.css+108−0
@@ -3331,5 +3331,113 @@input[type="text"]:focus {border-color: var(--accent);}/* ==================== MOBILE RESPONSIVE SIDEBAR ====================Add this to the end of sidebar/sidebar.css.On Android, the sidebar opens as a full tab (since sidebarActionisn't supported). The styles need to:1. Fill the viewport (no fixed sidebar width)2. Use larger touch targets (min 44px)3. Stack panels vertically instead of multi-column4. Increase font sizes for readability*//* Detect mobile via body.mobile class (set by sidebar.js on load) */body.mobile {width: 100vw;height: 100vh;max-width: 100vw;overflow-x: hidden;}body.mobile .sidebar-container {width: 100%;height: 100vh;flex-direction: column;}body.mobile .sidebar-panels {grid-template-columns: 1fr;gap: 8px;padding: 8px;}body.mobile .stat-card,body.mobile .panel,body.mobile .panel-section {min-height: 44px; /* iOS/Android touch target */padding: 12px 16px;font-size: 14px;}body.mobile button,body.mobile .btn,body.mobile .toggle-switch {min-height: 44px;min-width: 44px;}body.mobile .toggle-slider {width: 48px;height: 28px;}body.mobile .header {padding: 12px 16px;font-size: 16px;}body.mobile .section-label {font-size: 13px;padding: 12px 16px 6px;}body.mobile .stats-grid {grid-template-columns: repeat(2, 1fr);gap: 8px;padding: 0 12px;}body.mobile .topology-canvas {width: 100%;height: 300px;}body.mobile .query-log-table {font-size: 12px;}body.mobile .query-log-row {padding: 10px 12px;}/* Hide hover-only elements on touch devices */body.mobile .hover-only {display: none;}/* Make tabs scrollable horizontally on mobile */body.mobile .tab-bar {overflow-x: auto;-webkit-overflow-scrolling: touch;white-space: nowrap;}body.mobile .tab-bar .tab {display: inline-block;min-width: 80px;}/* Safe area insets for notched devices */body.mobile .header {padding-top: max(12px, env(safe-area-inset-top));padding-left: max(16px, env(safe-area-inset-left));padding-right: max(16px, env(safe-area-inset-right));}body.mobile .footer {padding-bottom: max(12px, env(safe-area-inset-bottom));}
Analysis metadata
- Model
- qwen3-235b-a22b-2507
- Analysed
- Jul 24, 2026, 6:28 PM
- Modified files
- 6
- Store
- firefox
- Store ID
- ad-bear