Is Unblocker for YouTube safe?
Unblocker for YouTube is medium risk. During popup login, the shipped code logs analytics events including the email entered or stored for the account, plus a persistent device ID, timestamp, and app version, sent to Amplitude's API, which was seen carrying this identifier.
Who publishes itRouteme LLC. - no other listings under this identity, 1 shared hostname
Routeme LLC. - no other listings under this identity, 1 shared hostname
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 1 hostname
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Popup login analytics sends email and device ID to Amplitude
During popup login, the shipped code logs analytics events including the email entered or stored for the account, plus a persistent device ID, timestamp, and app version, sent to Amplitude's API, which was seen carrying this identifier.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You use the popup's premium login controls or choose to log out.
The relevant paths are the send-code, verify-code, login-failed, and logout handlers.
The extension builds an authentication analytics event that can include your email address.
The analytics helper adds a persistent device identifier and sends the event to Amplitude.
- Your email addressjordan.lee@example.com (illustrative)
This ties the popup login event to the account address you entered or had stored in the extension.
- Authentication steplogin_successful
This says which login action happened, such as requesting a code, completing login, failing login, or logging out.
- Persistent device identifierweb_pybrl6xmmmrhlg9vb
This lets analytics events from the same browser profile be linked over time.
- Session timestamp1720791446123 (illustrative)
This groups the event with other popup activity from the same browser session.
- Extension platform and versionChrome Extension / 4.0.0
This describes the software environment that produced the event.
Observed Amplitude POST traffic from the popup reached api2.amplitude.com; the recorded non-auth popup events carried device_id/user_id web_pybrl6xmmmrhlg9vb. The recorded traffic did not preserve an auth-email request body.
- Accept
- application/json
- Content-Type
- application/json
Popup authentication events feed the Amplitude HTTP API
No separate deobfuscated tree was present; the shipped popup source is already readable
popup/popup.jsdocument.addEventListener('DOMContentLoaded', () => { const emailForm = document.getElementById('email-form'); const codeForm = document.getElementById('code-form'); const emailInput = document.getElementById('email-input'); const sendCodeBtn = document.getElementById('send-code-btn'); const verificationCode = document.getElementById('verification-code'); const verifyCodeBtn = document.getElementById('verify-code-btn'); const haveCodeBtn = document.getElementById('have-code-btn'); // Email validation emailInput.addEventListener('input', () => { const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput.value); sendCodeBtn.disabled = !isValidEmail; }); // Handle email submission sendCodeBtn.addEventListener('click', async () => { amplitudeTracker.trackButtonClick("Send Code Button"); try { // Add your API call here to send verification code sendCode(emailInput.value, (data) => { if(data.statusCode == 200){ amplitudeTracker.trackAuthEvent("code_sent", { email: emailInput.value, status: "success" }); showToast("Code sent to email", "success"); emailForm.classList.add('hidden'); codeForm.classList.remove('hidden'); chrome.storage.local.set({"YTU_EMAIL": emailInput.value}); chrome.storage.local.set({"YTU_CODE": "SEND"}); } else if(data.statusCode == 201){ amplitudeTracker.trackAuthEvent("code_sent", { email: emailInput.value, status: "not_premium", error_code: data.statusCode }); showToast("Email address is not premium", "error"); } else{ amplitudeTracker.trackAuthEvent("code_sent", { email: emailInput.value, status: "failed", error_code: data.statusCode }); showToast("(" + data.statusCode + ") Failed to send code", "error"); } }); } catch (error) { amplitudeTracker.trackError("code_send_error", error.message, { email: emailInput.value }); document.getElementById('email-error').textContent = error.message; document.getElementById('email-error').style.display = 'block'; } }); haveCodeBtn.onclick = () => { amplitudeTracker.trackButtonClick("Have Code Button"); emailForm.classList.add('hidden'); codeForm.classList.remove('hidden'); } // Handle verification code submission verifyCodeBtn.addEventListener('click', async () => { amplitudeTracker.trackButtonClick("Verify Code Button"); try { const storageResult = await chrome.storage.local.get("YTU_EMAIL"); const email = storageResult["YTU_EMAIL"]; verifyCode(email, verificationCode.value, (data) => { if(data.statusCode == 200){ userEmail = email; amplitudeTracker.trackAuthEvent("login_successful", { email: email }); amplitudeTracker.setUserId(email); showToast("Login successful", "success"); chrome.storage.local.set({"YTU_CODE": "VERIFY"}); showPage(false,"login-page") checkPremium(); sendConnectionStatusToServiceWorker("disconnect"); } else{ amplitudeTracker.trackAuthEvent("login_failed", { email: email, error_code: data.statusCode }); showToast("Failed to login", "error"); } }); } catch (error) { amplitudeTracker.trackError("verification_error", error.message, { email: email }); showToast("Something went wrong", "error"); } });});document.getElementById('logout-btn').onclick = () => { amplitudeTracker.trackButtonClick("Logout Button"); showAreYouSure(true,"Are you sure you want to logout?<br>Your Subscription remain active but this extension will back to free plan.<br>You can login again anytime.",(isSure) => { if(isSure){ amplitudeTracker.trackAuthEvent("logout", { email: userEmail }); chrome.storage.local.set({"YTU_CODE": ""}); chrome.storage.local.set({"YTU_EMAIL": ""}); showToast("Logged out successfully", "success"); checkPremium(); sendConnectionStatusToServiceWorker("disconnect"); } else{ showAreYouSure(false,"",() => {}); } });}No separate deobfuscated tree was present; the shipped analytics helper is already readable
popup/amplitude.jsconst AMPLITUDE_API_KEY = '680812b42b998d072fd8d2ddf1f2aa70';const AMPLITUDE_SECRET_KEY = '36c8e4734f7608aacca72929bd21dd9a';const AMPLITUDE_PROJECT_ID = '727399';class AmplitudeTracker { constructor() { this.deviceId = this.generateDeviceId(); this.sessionId = this.generateSessionId(); this.userId = null; this.userProperties = {}; } // Generate a unique device ID generateDeviceId() { let deviceId = localStorage.getItem('amplitude_device_id'); if (!deviceId) { deviceId = 'web_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36); localStorage.setItem('amplitude_device_id', deviceId); } return deviceId; } // Generate a unique session ID generateSessionId() { return Date.now(); } // Set user ID when user logs in setUserId(userId) { this.userId = userId; } // Set user properties setUserProperties(properties) { this.userProperties = { ...this.userProperties, ...properties }; } // Track an event async trackEvent(eventType, eventProperties = {}) { const event = { event_type: eventType, user_id: this.userId || this.deviceId, device_id: this.deviceId, session_id: this.sessionId, time: Date.now(), event_properties: eventProperties, user_properties: this.userProperties, platform: 'Chrome Extension', app_version: '4.0.0' }; try { const response = await fetch('https://api2.amplitude.com/2/httpapi', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ api_key: AMPLITUDE_API_KEY, events: [event] }) }); if (!response.ok) { console.warn('Amplitude tracking failed:', response.status); } } catch (error) { console.warn('Amplitude tracking error:', error); } } // Track authentication events trackAuthEvent(eventType, additionalProperties = {}) { this.trackEvent('Authentication Event', { event_type: eventType, timestamp: new Date().toISOString(), ...additionalProperties }); }}const amplitudeTracker = new AmplitudeTracker();window.amplitudeTracker = amplitudeTracker;- api2.amplitude.com
Amplitude HTTP API endpoint that receives the popup authentication analytics event.
What it can do
Permissions this extension asks for, as declared in version 4.1.4. 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 api.ytu.routeme.me
https://api.ytu.routeme.me/*
Read and change your data on routeme.me
https://routeme.me/*
Route all of your browsing through a server of its choosing
proxy
Store data in your browser
storage
Schedule its own background tasks
alarms
Act on the current tab, but only after you click the extension
activeTab