Originally published on ExtensionBooster.
You can view Chrome extension source code for any listing on the Chrome Web Store, without installing it. That matters: in December 2025, a Koi Security report (covered by The Register) described four extensions from a single publisher, with more than 8 million users combined, that captured people's AI chatbot conversations. Below are four ways to read what an extension really ships, what to grep for, and how to make your own extension easy for others to verify.
1. Download the CRX straight from Google
Every listing URL ends in a 32-character extension ID. Plug it and your Chrome version (from chrome://version) into Google's update endpoint, and it redirects you to the package:
https://clients2.google.com/service/update2/crx?response=redirect&prodversion=<CHROME_VERSION>&acceptformat=crx2,crx3&x=id%3D<EXT_ID>%26uc
A .crx is just a ZIP archive with a signed header in front. Plain unzip will open it, but it complains about "extra bytes at beginning" and exits with code 1, which breaks scripts. The header layout is simple: the 4-byte magic Cr24, a format version (3 today), a header length, then the ZIP. So a few lines of Python unpack it cleanly:
# unpack_crx.py usage: python3 unpack_crx.py ext.crx out/
import io, struct, sys, zipfile
data = open(sys.argv[1], "rb").read()
magic, version, header_len = struct.unpack("<4sII", data[:12])
if magic != b"Cr24" or version != 3:
sys.exit("not a CRX3 file")
zipfile.ZipFile(io.BytesIO(data[12 + header_len:])).extractall(sys.argv[2])
Open manifest.json first. It tells you the permissions, host access, background service worker and content scripts before you read a single line of logic.
2. Open the folder of an installed extension
If it's already installed, the code is on your disk. Find your profile path at chrome://version, then look under <Profile Path>/Extensions/<extension-id>/<version>_0/.
| OS | Default user data directory |
|---|---|
| Windows | %LOCALAPPDATA%\Google\Chrome\User Data |
| macOS | ~/Library/Application Support/Google/Chrome |
| Linux | ~/.config/google-chrome |
Copy the folder before you poke at it. Chrome checks installed files against signed hashes, so do your digging in the copy.
3. Watch it run in DevTools
Turn on Developer mode at chrome://extensions and click the extension's inspect views:
- Sources shows the service worker and its files.
- Popup: right-click inside it and choose Inspect.
- Content scripts: pick the extension in the Console's context dropdown on any page.
- Network is the most revealing panel for privacy questions. Use the extension normally for a few minutes and see which hosts it talks to.
4. Use CRX Viewer when you don't want to install anything
CRX Viewer, an open source project by Rob Wu (MPL 2.0), is available as an extension and as a web page. It opens a store listing's package, searches files by text or regex, beautifies minified code and shows file hashes.
What to look for once you're in
-
Permissions vs. purpose. Does a color picker really need
<all_urls>orcookies? -
Network calls. Search for
fetch,XMLHttpRequest,WebSocketandsendBeacon. -
Overridden browser functions, such as a replaced
window.fetch, which can expose data on other sites. -
Dynamic code:
eval,new Function, or scripts loaded from outside. -
Content script reach, via the
matchespatterns in the manifest. - Heavy obfuscation: long hex strings or encoded blobs.
Context helps here. Chrome Web Store policy bans obfuscation but allows minification, and Manifest V3 requires an extension's full functionality to be discernible from the submitted code. Remote data is allowed, remote logic is not. Still, a fetched config (say, a list of sites to act on) can change behavior, so check how fetched data gets used. If something looks wrong, report it through Google's flagging page.
Shipping your own extension? Make it verifiable
If users can read your code, a "this extension reads all your data" review becomes something you can answer with a link instead of a promise.
- Keep shipped code readable: no obfuscators or string encoders, and consider skipping minification if size isn't critical.
- Publish the source and tag every release to match the
manifest.jsonversion. - Make builds reproducible: commit the lockfile, use
npm ciorpnpm install --frozen-lockfile, pin Node.js, build in CI, and attach the ZIP plus aSHA256SUMSfile. Compare file contents, not ZIP hashes, because timestamps change archive hashes. - Diff what the store actually serves against your build, ideally as a scheduled CI job:
python3 unpack_crx.py extension.crx store/
diff -r --exclude=_metadata store/ dist/
Expect exactly two differences: the store adds _metadata/verified_contents.json and an update_url field in manifest.json. Anything else deserves a closer look.
- Lock down publishing with Verified CRX Uploads and 2-Step Verification on every account.
- Add a short security page covering data flows, permissions, where the source lives and how to report a vulnerability.
Key takeaways
- Any store extension can be downloaded by ID and unpacked, since a CRX is a ZIP behind a
Cr24header. - Installed extensions live in your profile's
Extensionsfolder; copy before editing. - DevTools' Network panel answers most "where does my data go" questions.
- Minification is allowed, obfuscation and remotely hosted code are not.
- Tagged source, reproducible builds and a scheduled store diff turn trust questions into checkable facts.
The full walkthrough, including an FAQ, is in the guide to viewing Chrome extension source code and verifying CRX builds. For more on earning ratings and user trust, see ExtensionBooster's resources for extension and app developers.
Have you ever unpacked an extension's CRX and found something you didn't expect in it, or diffed your own store build against your repo?
Top comments (0)