Building a Browser Extension Threat Intelligence Database: Inside Malicious Extension Sentry
Hook
Over 1,500 malicious browser extensions have been removed from the Chrome Web Store, but once installed on your machine, they stay there forever unless you know exactly what to look for.
Context
Browser extensions occupy a unique security blindspot. Unlike traditional software, there's no native way to scan your installed extensions against known malware databases. When Google removes a malicious extension from the Chrome Web Store—often weeks or months after initial reports—it doesn't automatically uninstall from users' browsers. The extension simply stops receiving updates and sits dormant, potentially still harvesting data or injecting ads.
Before Malicious Extension Sentry, developers and security teams had no centralized, machine-readable database of these threats. Security researchers would publish blog posts about individual extensions, CRXcavator would analyze permissions, and various vendors maintained proprietary threat feeds. But there was no simple way to answer the question: "Are any of my installed extensions known to be malicious?" This project fills that gap with a curated database (currently tracking 1,533 extensions), a local Python scanner, and multiple consumption methods for both humans and automation.
Technical Insight
The architecture is deliberately simple: a flat-file database that prioritizes accessibility over complexity. The core data structure lives in database/malicious_extensions.csv with fields for extension ID (the 32-character identifier from the Web Store), name, reason for listing, and source URLs. This CSV serves as the single source of truth, with markdown files generated for human readability.
The Python scanner (scanner.py) demonstrates elegant cross-platform browser detection. Instead of hardcoding paths, it searches standard installation directories for Chromium-based browsers:
def find_browser_profiles():
browsers = {
'Chrome': os.path.expanduser('~/.config/google-chrome'),
'Edge': os.path.expanduser('~/.config/microsoft-edge'),
'Brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'),
'Opera': os.path.expanduser('~/.config/opera'),
}
profiles = []
for name, base_path in browsers.items():
if os.path.exists(base_path):
# Discover all profiles (Default, Profile 1, Profile 2, etc.)
for profile in os.listdir(base_path):
extensions_path = os.path.join(base_path, profile, 'Extensions')
if os.path.exists(extensions_path):
profiles.append((name, profile, extensions_path))
return profiles
This pattern works because all Chromium browsers store extensions in a predictable directory structure: {browser_config}/Extensions/{extension_id}/{version}/. The scanner walks these directories, extracts the 32-character extension IDs, and performs offline matching against the downloaded CSV database. No network calls during the actual scan—the entire threat detection happens locally.
The matching logic is intentionally simple. The scanner loads the CSV into memory (typically under 200KB) and uses set intersection for O(1) lookup:
def scan_extensions(extensions_path, malicious_set):
installed = set()
matches = []
for extension_id in os.listdir(extensions_path):
if len(extension_id) == 32: # Valid extension ID format
installed.add(extension_id)
if extension_id in malicious_set:
manifest = load_manifest(extensions_path, extension_id)
matches.append({
'id': extension_id,
'name': manifest.get('name', 'Unknown'),
'version': manifest.get('version', 'Unknown')
})
return installed, matches
The scanner reads each extension's manifest.json to extract human-readable names, but the threat identification relies solely on the immutable extension ID. This is crucial because malicious extensions often use generic, trustworthy-sounding names that change between versions.
For continuous monitoring, the project includes a companion Chrome extension that periodically checks installed extensions against the database. It uses the chrome.management API to enumerate extensions and the fetch API to pull the latest CSV from the GitHub repository:
chrome.management.getAll((extensions) => {
fetch('https://raw.githubusercontent.com/toborrm9/malicious_extension_sentry/main/database/malicious_extensions.csv')
.then(response => response.text())
.then(csv => {
const maliciousIds = parseCSV(csv).map(row => row.id);
const threats = extensions.filter(ext => maliciousIds.includes(ext.id));
if (threats.length > 0) {
chrome.notifications.create({
type: 'basic',
title: 'Malicious Extension Detected',
message: `Found ${threats.length} known malicious extension(s)`,
priority: 2
});
}
});
});
This real-time monitoring approach means users get alerts whenever a newly-discovered malicious extension is added to the database, without needing to manually run the scanner. The web dashboard at malext.io provides a searchable interface backed by the same CSV data, demonstrating how a simple data format enables multiple consumption patterns: CLI tool, browser extension, web UI, and direct API integration for enterprise tools.
Gotcha
The fundamental limitation is that this is a reactive blocklist, not a behavioral analysis system. It only detects extensions that have already been identified, reported, and removed from the Chrome Web Store. If a malicious extension is still actively listed (either because it hasn't been discovered yet or because it cleverly hides malicious behavior from automated scans), this tool won't flag it. You're always at least days or weeks behind the threat actors.
The database also relies entirely on external sources for threat intelligence. The maintainer aggregates reports from security blogs, Reddit threads, and monitoring services, but there's no independent sandboxing or code analysis. If a malicious extension isn't widely reported or flies under the radar of security researchers, it won't appear in this database. Additionally, the database doesn't track extension updates—an extension might start benign, get widely installed, then push a malicious update. The database would only capture it after the malicious version is discovered and reported, potentially missing the window when it was most dangerous. For enterprise use cases, you'll want to combine this with permission auditing tools and network monitoring to catch behavioral anomalies.
Verdict
Use if: You need a lightweight, privacy-preserving way to audit installed browser extensions against known threats, you're building security tooling that needs a machine-readable blocklist of malicious extension IDs, you want periodic hygiene checks for personal or team browsers without enterprise security software, or you're doing threat intelligence research on browser extension malware trends. The zero-dependency Python scanner and flat-file format make it trivial to integrate into CI/CD pipelines or incident response playbooks.
Skip if: You need real-time protection against zero-day extension threats, you require behavioral analysis or permission risk scoring (use CRXcavator instead), you're securing an enterprise environment where you need policy enforcement and centralized management (use Google Workspace admin controls or Microsoft Edge for Business), or you want to analyze extension source code for malicious patterns (submit to VirusTotal or hybrid-analysis services). This is one layer in a defense-in-depth strategy, not a complete security solution—treat it as a known-bad blocklist to complement proactive vetting practices.