Open-source project
ghostery/ghostery-extension avatar
ghostery/ghostery-extension

The Ghostery extension ships a permission checker because ad blockers get audited

Ghostery Browser Extension for Firefox, Chrome, Opera, Edge and Safari

1,760 stars175 forksJavaScriptGPL-3.0

At a glance

What is it?
ghostery-extension is the GPL-3.0 source for the Ghostery tracker and ad blocker across Firefox, Chrome, Opera, Edge and Safari, built with Node scripts rather than a bundler CLI and gated on a permissions audit plus WebDriver end to end tests. It is interesting as an example of a browser extension that treats its own requested permissions as a reviewable artefact.
Who is it for?
Read this repository if you are building or reviewing a cross-browser blocking extension and want to see how permission surface, localisation and per-browser packaging are handled in one codebase, since `npm run lint:permissions` treats the manifest as a reviewed artefact and the Xcode project sits beside the web build.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Five browsers, two build systems

Ghostery is a tracker and ad blocker extension published for Firefox, Chrome, Opera, Edge and Safari, and the repository description is explicit about the platforms. The README describes it as blocking ads including on YouTube and Facebook, stopping trackers from collecting personal data, removing intrusive cookie pop-ups automatically, and showing detailed tracker information per site including the number of trackers, their type and the company operating them.

Two features stand out as more than blocking. There is a preview of tracker information on search engine result pages, so you can see who tracks a result before you click it. And the extension ships a filter list it describes as the largest database of trackers, updated fast and reliably for all users.

The build is not a bundler CLI plus per-browser config. It is a Node script driven through npm scripts, with `npm run build` calling `node scripts/build.js`. Everything else hangs off that:

bash
npm start [chromium|firefox]
npm start -- --browser=opera
npm start -- --browser=edge

So the browser is an argument to the same build, not a separate configuration. Opera and Edge are Chromium based and are reached by passing the browser name, while Firefox is one of the two defaults.

Safari is the exception and the README is honest about why. To run the local version in Safari you need Xcode, with project files in the `xcode` folder, and the README says Apple's ecosystem is more complex. It also concedes the practical consequence: most changes can be tested reliably in Chrome.

Version 10.6.8 is current, with v10.6.6 on 2026-10-02, v10.6.7 on 2026-10-07 and v10.6.8 on 2026-10-14,.

npm ci fetches the block lists, which is the first thing to understand

The setup has one non-obvious step. Before you can start a local version you install dependencies and download additional resources, described in the README as block lists:

bash
npm ci

Those resources are the filter data. That single fact shapes the whole project: Ghostery does not vendor its tracker database in git, it fetches it, and the filter logic is a build input rather than a file you can read in the repository.

The caching behaviour is documented because it trips people up. The build script caches fetched resources in several directories to speed up development, which means a second run does not re-download. When you need fresh resources you pass the `--clean` flag:

bash
npm start -- --clean

So the failure mode is: you change something, nothing happens, and the reason is a cache. Knowing about `--clean` before you start debugging saves an afternoon.

The platform assumption is stated just as plainly: the build script assumes you are using macOS and that browsers are installed in their default locations. That is a hard constraint for contributors on Linux or Windows, and it is the reason the README's guidance about verifying changes in Chrome matters so much.

One more environment file exists in the repository root, `.tool-versions`, which pins tool versions for a version manager, and `.npmrc` sits alongside it for npm configuration.

The permissions check is the most transferable idea in the build

Linting is split into three passes, and the third one is unusual:

bash
npm run lint:permissions

which runs `node scripts/check-permissions.js`. A privacy extension asking for broad browser permissions is the single largest objection users raise, and this project treats the requested permission set as something a script verifies rather than something a human remembers to check.

The other two passes are conventional. `lint:src` runs ESLint over the source, and `lint:build` does something better than a syntax check: it builds the firefox target silently and then runs `web-ext lint`, which validates the built extension. So a lint run catches packaging and manifest problems that a JavaScript linter would never see.

The configuration follows the same split in the repository root. There is `eslint.config.js` for the flat config, `.prettierrc` for formatting with `eslint-plugin-prettier` bridging the two, and `.editorconfig` for whitespace. In `package.json` the versions are current: `@eslint/js` at `^10.0.1`, `eslint` at `^10.12.0` and `@types/chrome` at `^0.3.4`.

The permissions script is the piece to lift. If you maintain any extension with a sensitive manifest, a check that fails the build when the permission set grows unexpectedly is worth the maintenance cost, and it gives reviewers something concrete to approve.

Testing is Node test runner for units and WebDriver for browsers

The test command chains three suites:

bash
npm run test:unit && npm run wdio && npm run wdio:update

Unit tests run on Node's own test runner rather than Jest or Vitest, which keeps the toolchain smaller:

bash
node --import ./tests/unit/setup/register-import.js --experimental-test-module-mocks --test

The `--experimental-test-module-mocks` flag is doing real work: it lets unit tests substitute module dependencies, which is how you test code that talks to the browser extension APIs without a browser. The `--import` of a setup module registers that hook before the test files load.

End to end tests run through WebdriverIO, at `^10.0.2` across the `@wdio/cli`, `@wdio/globals`, `@wdio/mocha-framework`, `@wdio/spec-reporter` and `@wdio/browser-runner` packages. There are two configurations: `tests/e2e/wdio.conf.js` for the suite and `tests/e2e/wdio.update.conf.js` for the update run, which is why `test` chains `wdio` and `wdio:update` rather than running one command. A local page server for tests is `npm run wdio:page`, running `node tests/e2e/page/server.js`.

The split is sensible for this kind of project. Unit tests cover logic that does not need a browser, and WebDriver covers what genuinely requires a real extension loaded in a real browser, which for a blocker is the only meaningful verification: filter application, popup behaviour and site breakage are all things only a browser can tell you about.

The workflow badge in the README header points at a `test.yml` GitHub Actions workflow on the `main` branch.

Localisation is generated, exported to Xcode, and pushed to the app store

Localisation runs through the `hybrids` library and is a two-step script. `locales:setup` copies `src/messages.json` into `src/_locales/en/messages.json`, and `locales.src` runs `hybrids extract -c --format=chrome.i18n ./src ./src/_locales/en/messages.json`. The `locales` script runs both.

So user-facing strings are extracted from the source rather than maintained by hand, and the Chrome `i18n` format is the target, with `src/messages.json` as the source of truth.

The Safari side has its own path. `xcode-export-locales` removes `./xcode/en.xcloc` and runs `xcodebuild -exportLocalizations -project ./xcode/Ghostery.xcodeproj -localizationPath ./xcode`. That is the bridge between the Chrome format and Apple's localisation bundles, and it is why the repository ships a `.tx/` directory, the configuration directory used by translation management tooling.

Releasing is script-driven. `npm run release` runs `./scripts/release.sh` and `npm run package` runs `./scripts/package.sh`, while `npm start:update` runs `./scripts/update.sh`. And `npm run app-store-connect` runs `node scripts/app-store-connect.js`, which is how the Safari build reaches Apple's distribution.

Note the division of labour: `src/` for the shared extension source, `scripts/` for every build and release step, `xcode/` for the macOS and Safari project, and `tests/` split into `unit/` and `e2e/`.

Licensing, and the GPL question a forker has to answer

The licence is GPL-3.0, copyright 2017-present Ghostery GmbH, with all rights reserved, and a LICENSE file at the repository root. That is a different answer from the Apache-2.0 or MIT that most browser extensions use, and it is the single most important fact for anyone considering reuse.

GPL-3.0 is a copyleft licence. If you modify this extension and distribute the modified version, you must release your source under the same terms. For a personal fork installed only on your own machines that obligation does not bite. For a modified extension you publish, on an extension store or a website, it does.

It also means the fetch model and the licence interact. The filter lists are downloaded at build time, and the tracker database is a Ghostery service asset rather than part of this repository. Whether you can point a fork at a different list source, and what that does to your distribution obligations, is a question the repository does not answer and that is worth thinking through before investing in a fork.

The package itself is marked private in `package.json`, with name `ghostery-extension` and `"type": "module"`, so it is not published to npm as a library. There is no module to consume; the distribution channel is the extension stores, with the Safari build going through App Store Connect.

What the project does offer to outsiders is contribution. The README credits contributions from lots of talented people and points at the contributors graph, which is the normal path for changing an extension under this licence.

Editorial conclusion

Read this repository if you are building or reviewing a cross-browser blocking extension and want to see how permission surface, localisation and per-browser packaging are handled in one codebase, since `npm run lint:permissions` treats the manifest as a reviewed artefact and the Xcode project sits beside the web build. Do not fork it expecting a configurable rules engine you can point at your own blocklists, because the tracker database and filter lists are fetched as external build resources rather than vendored here. Before you contribute, note the constraint the README states plainly: `npm start` assumes macOS with browsers in their default install locations, and Safari work requires Xcode, so most changes must be verified in Chromium.

Frequently asked questions

How do I build the Ghostery extension locally?

Run `npm ci` first, which installs dependencies and downloads additional resources such as block lists. Then run `npm start chromium` or `npm start firefox`, or pass a different Chromium-based browser with `npm start -- --browser=opera` or `--browser=edge`. The build script assumes macOS with browsers in their default locations.

How do I force the Ghostery build to re-download its resources?

The build script caches fetched resources in several directories, so pass the `--clean` flag, for example `npm start -- --clean`, to download fresh resources instead of using the cache.

Which browsers does the Ghostery extension support?

Firefox, Chrome, Opera, Edge and Safari. Chrome and Firefox are the default build targets, Opera and Edge are passed with the `--browser` flag, and Safari requires Xcode with project files in the `xcode` folder.

How is the Ghostery extension tested?

`npm test` chains unit tests on Node's own test runner, using `--experimental-test-module-mocks` so modules can be substituted, then two WebdriverIO suites from `tests/e2e/wdio.conf.js` and `tests/e2e/wdio.update.conf.js`. A local test page server runs with `npm run wdio:page`.

What licence does the Ghostery extension use?

GPL-3.0, copyright 2017-present Ghostery GmbH. The npm package is marked private, so the distribution channels are the browser extension stores rather than npm.

Official sources

  1. ghostery/ghostery-extension on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ghostery-ghostery-extension.svg)](https://hysenlabs.com/projects/ghostery-ghostery-extension)

Was this useful?