安装所有Firefox扩展程序
Installing Every* Firefox Extension

原始链接: https://jack.cab/blog/every-firefox-extension

## 2025年伟大的Firefox扩展安装 该项目详细描述了一次雄心勃勃(最终混乱不堪)的尝试,即安装*所有* Firefox扩展。作者最初使用公共API,遇到了检索全部84,000多个扩展的限制,因此需要使用不同的排序方法和URL排除技术来巧妙地解决问题。最终,完整的数据集被编译并上传到Hugging Face。 真正的挑战在于*运行*安装了所有这些扩展的Firefox。多次尝试,在不同的虚拟机和硬件配置上,都显示出显著的性能问题。仅仅启用扩展就需要大量的磁盘写入,并导致频繁崩溃。 最终,一台配备24GB内存的机器*勉强*可以运行安装了所有扩展的Firefox,表现出极度的缓慢和不稳定。许多页面无法加载,甚至访问设置等基本功能也变得无法使用。分析显示出奇怪的行为——扩展注入内容、访问遥测数据时崩溃,以及数量惊人的潜在恶意或垃圾信息扩展,其中一大批与“Innover Online Group Ltd.”有关。 虽然完成了完整安装,但该项目凸显了Firefox在这种极端条件下的脆弱性,并为进一步研究扩展行为和性能开辟了道路。

黑客新闻 新的 | 过去的 | 评论 | 提问 | 展示 | 工作 | 提交 登录 安装每一个* Firefox 扩展 (jack.cab) 18 分,RohanAdwankar 1小时前 | 隐藏 | 过去的 | 收藏 | 2 评论 帮助 xnorswap 2分钟前 | 下一个 [–] 这篇文章太棒了,简直疯狂。发现一个或多个 about: 页面中潜在的性能问题,这绝对值得跟进。回复 gathered 6分钟前 | 上一个 [–] 我看到视频笑得停不下来,想象这就像老年人使用电脑的样子。有人能在 Chrome 上做到吗?回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

*All but 8 we didn’t scrape (or got deleted between me checking the website and me scraping) and 42 missing from extensions.json.1 Technically we only installed 99.94% of the extensions.

It turns out there’s only 84 thousand Firefox extensions. That sounds feasibly small. That even sounds like it’s less than 50 gigabytes. Let’s install them all!

Scraping every Firefox extension

There’s a public API for the add-ons store. No authentication required, and seemingly no rate limits. This should be easy.

The search endpoint can take an empty query. Let’s read every page:

"https://addons.mozilla.org/api/v5/addons/search/?page_size=50&type=extension&app=firefox&appversion=150.0"

let res = await fetch(url)

let data = await res.json()

console.log(`PAGE ${page++}: ${data.results.length} EXTENSIONS`)

extensions.push(...data.results)

Bun.write("extensions-default.json", JSON.stringify(extensions))

The search API only gives me 600 pages, meaning I can only see 30 thousand extensions, less than half of them.

A solution I found is to use different sorts. The default sort is sort=recommended,users: first recommended extensions, then sorted by users, descending. Changing to just sort=created gave me some of the long tail:

"https://addons.mozilla.org/api/v5/addons/search/?page_size=50&type=extension&app=firefox&appversion=150.0"

"https://addons.mozilla.org/api/v5/addons/search/?page_size=50&type=extension&app=firefox&appversion=150.0&sort=created"

Bun.write("extensions-default.json", JSON.stringify(extensions))

Bun.write("extensions-newest.json", JSON.stringify(extensions))

import extensions_default from "../extensions-default.json"

import extensions_newest from "../extensions-newest.json"

// Yes, somehow I got the same slug twice

for (const ext of extensions_default) {

extensions[ext.slug] = ext

for (const ext of extensions_newest) {

extensions[ext.slug] = ext

console.log(`TOTAL UNIQUE EXTENSIONS: ${Object.keys(extensions).length}`)

~/Developer/every-addon> bun count

TOTAL UNIQUE EXTENSIONS: 54218

I’m still missing 30,0252 extensions, so I added rating and hotness too.

~/Developer/every-addon> bun count

TOTAL UNIQUE EXTENSIONS: 67458

That’s still 16,7852 missing. Adding updated

~/Developer/every-addon> bun count

TOTAL UNIQUE EXTENSIONS: 67945

Starting to hit diminishing returns. While I was waiting 7 minutes for that last list to get scraped because my code didn’t fetch in parallel, I had an epiphany: use exclude_addons. I can just fetch page 600 and exclude all its addons to get page 601.

"https://addons.mozilla.org/api/v5/addons/search/?page_size=50&page=600&type=extension&app=firefox&appversion=150.0&sort=updated"

const page_600 = await fetch(url).then(res => res.json())

const page_601 = await fetch(

`${url}&exclude_addons=${page_600.results.map(ext => ext.id).join(",")}`,

).then(res => res.json())

It works! There is a URL length limit, sadly, so I can only fetch an extra 20 pages.

"https://addons.mozilla.org/api/v5/addons/search/?page_size=50&page=600&type=extension&app=firefox&appversion=150.0&sort=created&exclude_addons="

let res = await fetch(url)

let data = await res.json()

console.log(`PAGE ${page++}: ${data.results.length} EXTENSIONS`)

if (data.results.at(-1).id === extensions.at(-1)?.id) break // IDK

extensions.push(...data.results)

url += data.results.map(ext => ext.id).join(",")

Bun.write("created-2.json", JSON.stringify(extensions))

TAke a look, y’all:

~/Developer/every-addon> bun count

TOTAL UNIQUE EXTENSIONS: 68035

A lot less than I expected, especially considering what happens when I add the downloads sort:

~/Developer/every-addon> bun count

TOTAL UNIQUE EXTENSIONS: 68901

Reading the docs again, I notice I can filter by category as well. I’m tired of waiting 7 minutes so I’ll just fetch every page in parallel.

function get(url: string, path: string) {

Array.from({ length: 600 }, (_, i) => fetch(`${url}&page=${i + 1}`).then(res => res.json())),

let extensions = pages.flatMap(page => page.results)

Bun.write(path, JSON.stringify(extensions))

const categories = await fetch("https://addons.mozilla.org/api/v5/addons/categories/").then(res =>

.filter(category => category.type === "extension")

`https://addons.mozilla.org/api/v5/addons/search/?page_size=50&type=extension&app=firefox&sort=created&category=${category.slug}&appversion=150.0`,

`./newest-${category.slug}.json`,

I got basically all the extensions with this, making everything I did before this look really stupid.

~/Developer/every-addon> bun analyze

Found 84235 unique extensions

That would be 49.3 GB, an average of 584.9 kB per extension

That’s 8 less extensions than what it says on the website. When I ran this in September 2025, it found 21 more extensions than what was mentioned on the website, so I think this is enough.

So that nobody has to do this again, I’ve uploaded this dataset to Hugging Face.

Alternatively, addons-server has CORS enabled, so click this funny button to get your very own all_extensions.json:

Analyzing every Firefox extension

I have a copy of Bun and all_extensions.json, so I will torment you with my unmatched script power.

Biggest extensions

The biggest Firefox extension is dmitlichess at 196.3 MB, which contains 2000+ audio files.

Here’s the rest of the top ten:

The first time I ran this analysis, in September, “Cute doggy - Dog puppies” was the 10th largest extension. I’m still mentioning it here, because I was so fucking confused:

Cute doggy - Dog puppies by cutypal dotcom: Cute AI dog wallpapers with live clock & messages. Click “Talk with Me” to chat and enjoy cozy dog vibes anytime!

The smallest extension is theTabs-saver, which is 7518 bytes and has no code.

Worst extension

Subjectively it’s Cute doggy - Dog puppies, but objectively:

import extensions from "../all_extensions.json"

.filter(ext => ext.ratings.count > 10)

.sort((a, b) => a.ratings.bayesian_average - b.ratings.bayesian_average)[0],

it’s Tab Stack for Firefox, by lolicon (?!?!?!?!?!).

First extension

Web Developer.

Most screenshots

RDS Bar has 54.

The “Middle Finger Emoji Sticker” Award

FalscheLaden, with no users, requests 3,695 permissions.

Second place is Google Dark Theme, which requests 2,675 permissions but has 1,687 users.

Most prolific developer

import extensions from "../all_extensions.json"

extensions.flatMap(e => e.authors),

).sort((a, b) => b.length - a.length)[0][0],

Dr. B is the king of slop, with 84 extensions published, all of them vibe coded.

How do I know? Most of their extensions has a README.md in them describing their process of getting these through addon review, and mention Grok 3. Also, not a single one of them have icons or screenshots.

Personally, I’m shocked this number is this low. I expected to see some developers with hundreds!

Phishing

I reviewed the source of a couple homoglyph attacks on crypto wallets discovered in the dataset and was disappointed to find out they just pop up a form asking for your seed phrase and send it off to their server. It’s an extension!!! You can steal their coinbase.com token! You can monitor the clipboard and swap out their address for yours! You can crash their browser and claim your real malware is the fix!

Why would you make a fake MetaMask extension and bot 1-star reviews?

A phishing extension has 1,672 1-star reviews, which all use the same template

Is this the doing of their cybercrime competitors, who bot 4-star reviews on extensions of their own?

A phishing extension has two 5-star reviews, 1,113 4-star reviews and a single 3-star review

Either way, these extensions are clearly phishing. I reported some to Mozilla, and the next day they were all gone, even the ones I was too lazy to report. I forgot to archive them, so I guess they live on in May’s VM!

In terms of implementation, the most interesting one is “Іron Wаllеt” (the I, a, and e are Cyrillic). Three seconds after install, it fetches the phishing page’s URL from the first record of a NocoDB spreadsheet and opens it:

chrome.runtime.onInstalled.addListener(async () => {

await new Promise(e => setTimeout(e, 3e3))

let e = await (0, r.fetchUrlFromNocoRest)()

? await chrome.tabs.create({

: console.warn("No valid URL from NocoDB.")

console.error("Install flow failed:", e)

I think the extension’s “no accounts or remote code” description is really funny, like putting “no copyright infringement intended” in your video’s description in case YouTube is watching. The API key had write access, so I wiped the spreadsheet.

SEO spam

You get a “Homepage” link in your extension’s page and your own page. It’s been nofollow for two years, but that hasn’t stopped grifters from trying anyway.

On Attempt 1, I encountered Typo Sniper and Tab Fortune Teller, AI generated extensions with casinos in their author’s Homepage links.

In the dataset, there’s many “Code Injector” extensions, which are all virtually identical and also have random websites in their author’s Homepage link.

All of these extensions are from 2025. Is there an ancient SEO guide still circulating? Is there some evil AMO frontend they’re still getting a backlink from? I have no idea what’s happening here.

PUAs

Do you notice a pattern?

Over 700 thousand users in total.

All of these extensions are their author’s only uploads and they have their own domains. Most of them are on both Chrome and Firefox, their websites look the same, and they all have a terms of service referencing “Innover Online Group Ltd”, which is a .png for some reason.

Because I scraped every Firefox extension twice, I can see what got removed in between the runs. Three of Innover Group’s extensions—Earth View 360°, View Manuals, and View Recipes, totaling 115 thousand users—have been disabled by Mozilla.

Innover Group runs Google ads for their extensions, a lot of them simply saying “Continue”.

The “Custom Web Search” is Yahoo but with their affilate code. That code being safeplexsearch, which has a website of its own which of course mentions Innover Online Group Ltd, and links to an addon with 3,892 users, which is actually a Firefox exclusive. Actually, “Custom Web Search” is a Firefox exclusive on all of these extensions. Why did they even make a Chrome version, to sell them to the NSA??

One user claimed Ezy Speed Test “disables Ublock [sic] Origin once installed”, which I did not find in its code.

There’s a million companies like this, though. I just went to Download.com with my ad-blocker off and discovered the company Atom Apps in an ad, which also uploads extensions for both Chrome and Firefox, with a new account for each extension, only includes Yahoo in the Firefox version, with names that end in either “and Search” or ”& Search”, and has their company name as a .png in their terms of service. They have 220 thousand daily users total across 12 extensions, and none of theirs have been disabled.

Some percentages

  • 34.3% of extensions have no daily users
    • 25.1% of extensions have more than 10 daily users
    • 10.6% of extensions have more than 100 daily users
    • 3.2% of extensions have more than 1000 daily users
    • 0.7% of extensions have more than 10000 daily users
  • 76.7% of extensions are open source (SPDX license that isn’t All Rights Reserved)
  • 23% of extensions were created after I started writing this article
    • 19% of extensions have no users, no reviews, no screenshots, no downloads, and no icon
  • 2.4% of extensions require payment
    • 38.1% of those are open source???

Installing every Firefox extension

Obviously I’m not going to open each of these in a new tab and go through those prompts. Not for lack of trying:

The "select add-on to install" dialog is Not Responding as I try to select all

Each extension has the current_version.file.url property which is a direct download for the extension. I download them to my profile’s extensions folder with the guid property as the base name and the .xpi file extension, because anything else will not be installed.

Then, I delete the addonStartup.json.lz4 and extensions.json files. When I reopen Firefox, each extension is disabled. Tampering with extensions.json is common enough that you can ask any chatbot to do it for you:

const fs = require("fs") // WHY IS THIS COMMONJS

const path = require("path")

// Path to extensions.json (adjust this to your Firefox profile directory)

// WHY IS THIS IN CAMELCASE

const extensionsJsonPath =

"/Users/user/Library/Application Support/Firefox/Profiles/1avegyqd.default-release/extensions.json"

// Read the extensions.json file

const data = fs.readFileSync(extensionsJsonPath, "utf-8") // WHY IS THIS NOT NODE:FS/PROMISES

const extensionsData = JSON.parse(data)

if (Array.isArray(extensionsData.addons)) {

extensionsData.addons.forEach(addon => {

addon.userDisabled = false

// WHY IS THIS NOT A GUARD

console.error("Unexpected format: addons property is missing or not an array.")

// Write the updated data back to extensions.json

fs.writeFileSync(extensionsJsonPath, JSON.stringify(extensionsData, null, 2))

console.log("All extensions enabled successfully!")

console.error("Error processing extensions.json:", error)

Attempt 0: 65,335

My first attempt was in a tiny11 core VM on my desktop.

At first, instead of downloading all of them with a script, I tried using enterprise policies, but this copies all the extensions into the folder. I quickly ran out of memory, and the pagefile took up the rest of the storage allocated to the VM. I had also expected Firefox to open immediately and the extensions to install themselves as the browser is being used, but that also did not happen: it just froze.

Firefox skeleton UI inside a VM

Attempt 1: ~1,000

After that, I tried downloading them myself.

import extensions from "./all_extensions.json"

import { exists } from "node:fs/promises"

let count = extensions.length

const PATH_TO_EXTENSIONS_FOLDER =

"C:\\Users\\user\\AppData\\Local\\Mozilla\\Firefox\\Profiles\\mkrso47f.default-release\\"

extensions.map(async ext => {

if (await exists(PATH_TO_EXTENSIONS_FOLDER + ext.guid + ".xpi")) {

console.log("Downloading", ext.current_version.file.url)

const file = await fetch(ext.current_version.file.url)

await Bun.write(PATH_TO_EXTENSIONS_FOLDER + ext.guid + ".xpi", file)

console.log("Downloaded", ext.slug, `${(++progress / count) * 100}% done`)

File explorer with the extensions folder open in the foreground, and the download script open in the background. All visible extensions were last modified on 9/27/2025 at 1:12 AM.

To make sure I was installing extensions correctly, I moved the extensions folder elsewhere and then moved about a thousand extensions back in. It worked.

Jack.cab open on Attempt 1. The color scheme is strange, all text is "Se ni važn", there's a low resolution Goku background, and there's a sticky note

There were multiple extensions that changed all text to a certain string. bruh-ifier lost to Se ni važn. Goku is in the background.

My context menu is so long that I’m showing it sideways:

Context menu bloated with a lot of extensions

I had installed lots of protection extensions. One blocks traffic to .zip and .mov domains, presumably because they are file extensions. This is .cab erasure! Then, I realized that there were likely multiple people viewing my browsing history, so I went to send them a message.

Scam warning popups from both "Anti-Phishing Alert" and Fraudulent Detector on a DuckDuckGo search for "IF YOU SEE THIS MESSAGE CONTACT ME JACK.CAB"

That “⚠️ SCAM WARNING!” popup is from Anti-Phishing Alert. As you may have inferred, it seems to only exists for its Homepage link. How does it work?

function isPhishingURL(url) {

const suspiciousPatterns = [

/[a-z0-9\-]{1,}\.com\.xyz/i,

/https?:\/\/(?!www\.)[a-z0-9\-]+\.([a-z]{2,}){2,}/i,

return suspiciousPatterns.some(pattern => pattern.test(url))

Vasavi Fraudulent Detector also has a popup for when a site is safe:

title: "Vasavi Fraudulent Detector",

content: "Safe Webpage !!",

icon: $.sweetModal.ICON_SUCCESS,

Attempt 2: 65,335

Only the addons from Attempt 1 were actually loaded, because I didn’t know I needed to delete addonStartup.json.lz4 yet. I scrolled through the addons page, then I opened DevTools to verify it was the full 65,335, at which point Firefox froze and I was unable to reopen it.

联系我们 contact @ memedata.com