The JSON Formatter extension by Callum Locke was one of those developer tools that quietly became infrastructure. Install it once, forget it exists, and every raw JSON response in your browser transforms into a syntax-highlighted, collapsible tree. Millions of developers did exactly that. Now those same users are running adware.
The extension has been closed and taken over. Whoever controls the Chrome Web Store listing pushed a malicious update that injects advertisements. The Hacker News thread has 262 points and a lot of people discovering they still had it installed.
This is not a new story. It is a story that keeps repeating because the structural conditions that enable it have never been fixed.
How Chrome Extensions Become Trojan Horses
When you install a Chrome extension, you grant it a set of permissions declared in its manifest.json. A JSON formatter has a completely legitimate reason to request broad page access:
{
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}
]
}
The <all_urls> match pattern lets the extension inject JavaScript into every page you visit. The extension needs this to detect JSON content types and replace the raw response body with a formatted view. That is the legitimate use. The malicious use of the same permission is obvious: inject ads, harvest form data, redirect searches, or exfiltrate session tokens.
The critical detail is that permissions persist across updates. Chrome checks for extension updates roughly every few hours and applies them silently. If a new version uses existing permissions in a new way, such as now injecting ad scripts in addition to formatting JSON, there is no re-consent prompt. Chrome only prompts the user when a new version requests additional permission scopes it did not have before. Using the <all_urls> permission that was already granted for something the user did not intend falls completely below that threshold.
This means an attacker who controls a Chrome Web Store listing can push malicious code to every existing user within hours, with no user interaction required.
The Ownership Transfer Gap
The Chrome Web Store has no formal mechanism for notifying users when an extension changes hands. A listing can be transferred to a new developer account, or the original developer’s account can be compromised, and users receive zero signal that the software they trusted now has a different author.
This gap has produced a small but consistent stream of incidents over the past decade.
In 2014, the developer of Add to Feedly sold the extension for a reported $100,000. The buyer pushed an update within days that injected affiliate links into pages. This was the case that defined the playbook: acquire a popular extension with a large installed base, monetize immediately through ad injection, wait for users to notice.
In 2020, the developer of Nano Adblocker and Nano Defender sold both extensions to a Turkish company. The combined install count was around 300,000. Within weeks, the new owner pushed an update that harvested credentials and manipulated engagement metrics on sites including Reddit and Hacker News. The malicious behavior was caught by other open-source ad-blocker developers who reviewed the new code. Google removed both extensions from the Web Store, but only after the community flagged them.
In 2021, The Great Suspender, a tab-management extension with roughly two million users, was sold to an unknown buyer who injected tracking code. Google removed it from the Web Store. The original codebase was forked by the community as The Marvellous Suspender, which is still maintained.
JSON Formatter joins this list. The details of whether Locke sold the extension, transferred it to a third party, or had his account compromised are not yet fully clear. What is clear is that the Chrome Web Store listing now delivers something other than what Locke built.
What Manifest V3 Does and Does Not Fix
Google’s Manifest V3 transition, which has been rolling out since 2023 and is now mandatory for new extensions, removes webRequestBlocking, the API that allowed extensions to intercept and modify HTTP responses before they reached the page. This was a meaningful attack surface, particularly for ad injectors working at the network layer.
But MV3 does not prevent content script injection. An extension with <all_urls> in its content_scripts declaration can still run arbitrary JavaScript on every page you visit. It can still read the DOM, modify page content, inject ad containers, listen to input events, and make fetch requests to external servers. The surface area that matters for adware injection remains intact.
The MV3 transition addresses some categories of abuse while leaving others untouched. The core problem for this class of attack, silent updates to extensions holding broad page permissions, is architectural and was not the target of MV3.
Auditing Your Extensions
If you had JSON Formatter installed, remove it now. Go to chrome://extensions/, find it, and click Remove.
More broadly, this is a reasonable moment to audit everything you have installed:
chrome://extensions/
For each extension, click Details to see its permissions. Any extension holding <all_urls> or access to specific sensitive domains deserves scrutiny. Ask whether the developer is still actively maintaining it, whether it has been transferred, and whether you actually still use it.
The Chrome Web Store does not surface ownership change history. The most reliable signal is the GitHub repository, if one exists. Check the commit history, open issues, and whether there are reports of unusual behavior. For the JSON Formatter specifically, the GitHub repository has been archived.
For detecting active misbehavior, the browser’s built-in DevTools are the most direct tool. Open the Network tab, filter by XHR and Fetch, and visit a few ordinary pages. Unexpected outbound requests to domains you do not recognize, especially on pages that are not themselves running analytics or ad networks, are worth investigating.
What to Use Instead
Firefox has had built-in JSON formatting since version 53. If you navigate to a URL that returns application/json, Firefox renders it as a collapsible, syntax-highlighted tree with no extension required. For development work this is the simplest solution, and it is one of several reasons to keep a Firefox profile around.
Chrome’s native JSON rendering has improved in recent versions, though it is still not as capable as the dedicated extension was at its best. For most inspection tasks, the DevTools Network tab response preview handles JSON adequately.
For API development specifically, the right tool is a dedicated client rather than a browser extension. Bruno is a good open-source option that stores collections as plain files. Postman and Insomnia remain popular. These tools handle JSON formatting as a baseline feature alongside authentication flows, environment variables, and test scripting, which is a more appropriate scope for serious API work than a browser extension.
If you need an extension specifically, look for one that is actively maintained, open source, and has a small, auditable codebase. Review the source before installing. This is more effort than clicking Install, but the alternative is occasionally running a stranger’s code with full access to every page you visit.
The Structural Problem
Google could reduce this attack surface in a few concrete ways. Publishing ownership transfer history for Web Store listings, even just a simple audit log of when developer accounts changed, would give users and security researchers an early signal. Requiring re-consent when an extension that previously only read content now also makes outbound network requests to new domains would catch many adware injections before they reach users. Shorter update propagation windows with staged rollouts, combined with anomaly detection on sudden behavioral changes in extension code, would give Google’s own review process more opportunity to catch malicious updates.
None of these are technically difficult. The Web Store is a distribution platform with known failure modes that have been publicly documented for over a decade. The incidents keep happening because the incentives for fixing the underlying model are weaker than the cost of the individual responses.
The JSON Formatter story is not unusual. It is the expected outcome when you combine silent auto-update, persistent broad permissions, no ownership transparency, and a large installed base of users who installed something once and forgot about it. The extension was doing exactly what it was designed to do. The design was the problem.