This is the third cross-registry state report, after April's 202-server sample and June's 2,233-project Trust Middle. Before writing it I went back over how those reports were put together, because the obvious headline, Blocked is up nearly fivefold, is the kind of number that is technically true and practically misleading. Most of this report is about why, and about what I've changed in how I report so it doesn't happen again.
Everything here is reproducible. The per-project dataset, the hand review of every security block, and the analysis script are published with this report (CC BY 4.0). The June baseline is the pinned June 9 snapshot on Hugging Face.
TL;DR
- 384 of 2,767 projects are Blocked. 344 are blocked for having no license, 31 because the repository is archived, and 9 for a security reason.
- The jump since June is mostly a rule change, not an ecosystem change. Two days after the June snapshot, a missing license became a hard block. Under today's license rule, June would have been roughly 13–15% Blocked, about the same as now.
- I checked all 9 security blocks by hand. 2 hold up. 5 are false positives in my own scanner. 2 rest on advisories whose affected-version ranges disagree between sources (one of those projects stays Blocked anyway because it's archived).
- Licensing isn't improving. Of 287 projects that had no license in June, 31 (10.8%) have added one since.
- The biggest blind spot is vulnerability data. Only 28% of projects get a vulnerability lookup today, because the lookup reads npm packages. Python projects are 26% of the corpus and 3.6% of them are covered. MCP Atlassian, a popular Python server with 30 GitHub advisories, has no vulnerability signal in its score at all.
- August's Deadbugz campaign got past pre-install checks by entering as a configuration line, not a package. I explain why that matters and what does catch it.
What Blocked is made of
Blocked is a hard gate: if any of five disqualifiers fires, the project is Blocked regardless of its score. The trouble is that those five disqualifiers mean very different things for the person about to install:
If you only read a tier, you can't tell these apart. Starting with this report, I will always report Blocked broken down by these three reasons, never as a single number.
Why the number jumped
Three algorithm changes landed between the June snapshot and today. Each project picks up new rules only when it is re-scored, so their effect shows up gradually in the weekly public snapshots:
| Date | Change | Effect on Blocked |
|---|---|---|
| Jun 9 | June snapshot (The Trust Middle) | 64 blocked (2.9%) under the rules of the day |
| Jun 11 | v3.3.0: no license and archived repositories become hard blocks (before, they only lowered the tier); malware advisories fire the critical-vulnerability gate | Pushes up, week by week, as projects are re-scored |
| Jun 11 | v3.3.1: the supply-chain gate narrowed to actual token exfiltration | Narrows one rule |
| Jul 6 | v3.4.0: safety scanner rebuilt around behavior instead of keywords (details) | Clears most false safety blocks |
Here is the Blocked share (%) in each weekly public snapshot. Counts are records, before the de-duplication described below:
The July 6 spike is the false-positive epidemic that the scanner rebuild cleared the following week. The climb after that is the license rule reaching more of the corpus as projects were re-scored, plus new projects entering it. The share peaked at 14.7% on August 3 and has eased slightly since.
Two ways to check that this is a rule change and not the ecosystem getting worse:
- Apply today's license rule to June's data. 278 June projects that weren't Blocked had no license and would be subject to the rule (npm-only packages aren't). Blocking them gives 342 Blocked (15.3%). If June's safety-scanner blocks are also removed, as nearly all were by September, it's 282 (12.6%). Today's figure is 13.9%. This models only the license rule; June's snapshot doesn't record which repositories were archived.
- Follow the same projects. 2,131 records appear in both snapshots. 63 of June's 64 Blocked records are still in the corpus; 59 of them are no longer Blocked (11 are now Verified). 274 moved into Blocked, and 239 of those already had no license in June. They didn't change; the rule did.
Licensing hasn't moved
Of the 287 same-record projects with no license in June, 31 (10.8%) have added one in the 15 weeks since. The other 256 are still unlicensed. Across the whole corpus, 348 projects trip the no-license rule and all of them are Blocked: 344 for that alone, and four that are also archived. Another 25 npm-only packages declare no license but aren't blocked, because the npm-only scorer doesn't apply the rule.
This is the cheapest problem in the whole dataset to fix: one file. It's also the one that moves slowest. If you publish an MCP server or skill, adding a LICENSE is most of the distance out of Blocked. If you run a registry, requiring one at publish time would clear about nine in ten Blocked entries.
The nine security blocks, checked by hand
A security block is a strong claim about someone's project, so before counting any of them I looked at the exact file, advisory, or workflow that triggered each one. The full review is in the published review file.
| Project | What fired | Verdict |
|---|---|---|
| diegosouzapw/omniroute | Critical RCE advisory (GHSA-hf57-cqmx-p4gr). GitHub's reviewed range includes the npm latest; the maintainer's advisory and code show a fix in 3.8.49 | Range dispute |
| lobehub/lobehub | Critical SSRF advisory (GHSA-xmwj-c75x-6346). Fixed upstream, but the fix was never published to npm | Holds (npm) |
| packrift/packrift-mcp | MCP resources and start guides hand the agent curl … | bash one-liners from the publisher's domain | Holds |
| browserbase/mcp-server-browserbase | Malware advisory on the npm package; OSV's range matches every version, GitHub's covers only 2.1.1 | Range dispute |
| alibaba/nacos | CI sends GITHUB_TOKEN with curl, but to api.github.com | False positive |
| caura-ai/caura-memclaw | HTML comments in SKILL.md that are maintainer notes | False positive |
| nvidia/nemoclaw | An SPDX copyright comment in a SKILL.md; installer usage text for humans; a build script | False positive |
| decolua/9router | Install commands for other CLIs, displayed as text in the app's UI | False positive |
| koala73/worldmonitor | A defensive instruction that quotes “ignore previous instructions” as an example of what to disregard | False positive |
What holds. LobeHub's npm package: its latest version, last published in February, is inside the range of a critical SSRF advisory. LobeHub's current releases carry the fix, but it never reached npm, so anyone installing from npm gets the affected version. And packrift-mcp, a commerce server with no sign of bad intent: its MCP resources and start guides hand your agent a shell one-liner that downloads a script from the publisher's domain and pipes it to bash. The script checks a confirmation variable itself, so whatever that URL serves on the day your agent runs it is what executes. That's exactly the risk the rule exists for. The difference from NemoClaw's installer, which I count as a false positive, is the audience: NemoClaw's one-liner is usage text for a human; packrift's is delivered to an agent.
What doesn't. Five of the nine are my scanner being wrong, and they fall into four patterns: an HTML comment in an agent-facing file with nothing instruction-like in it; defensive text that quotes an attack string in order to tell the agent to ignore it; install commands shown to a human in a UI rather than executed; and a GitHub token sent to GitHub's own API. The July rebuild cleared 107 false safety blocks, but these four patterns got through it. Fixing them is next on my list, and until that ships, those five score pages still show Blocked. I'd rather tell you that than quietly drop them from the count.
The range disputes. Two blocks rest on advisories whose sources disagree about which versions are affected. OmniRoute's critical RCE advisory, as reviewed by GitHub and mirrored in OSV, covers every version up to the current npm release (3.8.50). The maintainer's own advisory for the same ID says it was fixed in 3.8.49, and the code agrees: 3.8.49 made the vulnerable endpoint loopback-only. The block follows the reviewed range until the records agree, but I don't count it as a finding that holds. Browserbase's MCP package had one malicious version, 2.1.1, in November 2025. OSV's malware record flags every version of the package; GitHub's advisory covers only 2.1.1. The current release (3.0.0, March 2026) is outside GitHub's range, and the project is archived, so it stays Blocked on that basis. Going forward, I check an advisory's range against the other sources before I call a current version affected.
Net: 2 of 2,767 projects are Blocked for a security reason that holds up. That doesn't mean everything else is safe. It means the Blocked tier is mostly a legal and maintenance signal, and the security question lives mostly in the scores. The next section is about the parts of that question the scores can't answer yet.
What the scores couldn't see this quarter
Vulnerabilities outside npm
The vulnerability signal checks the installable version against OSV.dev, CISA's Known Exploited Vulnerabilities catalog, and EPSS. But it finds the installable version by reading the project's npm package. If there isn't one, there's no lookup:
| Primary language | Projects | With a vulnerability lookup |
|---|---|---|
| TypeScript | 750 | 479 (64%) |
| Python | 728 | 26 (3.6%) |
| JavaScript | 233 | 113 (48%) |
| Go | 147 | 8 (5.4%) |
| Rust | 98 | 14 (14%) |
| All projects | 2,767 | 775 (28%) |
MCP Atlassian shows what that means. CVE-2026-73498, disclosed in July, is an arbitrary file read in its confluence_upload_attachment tool. GitHub now lists 30 advisories against the package, 2 of them critical and 25 published on September 22. The maintainers shipped every fix before the advisory went public, and the current release, 0.23.1, is affected by none of them. So for someone installing today, the score's silence costs nothing. But it is silence, not a finding: its score, Established, 5.9, has no vulnerability signal at all, so it would read exactly the same if the current release were affected. For a project with 30 advisories in a year, that history is itself worth knowing, and the score can't see it.
My methodology page used to list PyPA and Go advisories among the sources for this signal. OSV does include them, but my engine only queried npm packages, so that was an overstatement. I've corrected the page. Adding PyPI and Go lookups is the most valuable coverage fix available, and it's queued.
Deadbugz: an attack a pre-install score can't see
In August, Pillar Security documented Deadbugz: 23 pull requests to unrelated AI and developer-tool projects, most of them adding a remote MCP server called productivity-suite to a project's configuration (four pointed at a hidden local script instead). It offers text formatting and summarization. After exactly three tool calls, its tool listings and prompts change into instructions to collect SSH keys, AWS credentials, shell history, and Kubernetes config, and to hide that from the user. None had been merged when Pillar reviewed them; four were still open.
No pre-install score would have caught it, mine included, and not because the code was hidden: Pillar found the server's source public, with the trigger in it. The problem is where the attack enters. Each pull request added an endpoint URL or a file path to a project's configuration, not an installable package, so nothing in the change prompts a pre-install lookup. Anyone who connects to the server to try it gets harmless answers for the first three calls, and the hostile behavior starts only after it's trusted. The general lesson: what a remote endpoint returns tomorrow is outside anything you can check today.
What does help:
- Treat a new remote MCP endpoint in a pull request like a new dependency. Deadbugz was a code-review attack. The line to look for is a server URL you've never seen.
- Watch runtime behavior. A runtime scanner or proxy that pins tool descriptions and alerts when they change is designed for exactly this. Pick whichever you prefer; that's a different layer from the one I work on, and you want both.
- Be honest about what an archive can see. My version archive records how projects change between versions, but today it doesn't capture remote endpoint URLs or tool lists on most observations. That's a coverage gap, and I'm not going to put a number on tool-description drift until it's closed.
The rest of the picture
The June report's main finding was the “trust middle”: two-thirds of projects packed into the 5–7 band and nothing above 9. With the caveat that three algorithm changes sit between the two snapshots, here's how the distribution looks now:
- The middle has thinned. The 5–7 band fell from 66.7% to 54.2%, with projects spreading both up (7–9) and down (3–5).
- Two projects now score above 9: openai/openai-node (9.28) and axios/axios (9.22).
- Purpose-built MCP projects are catching up on Verified. Skills-mode projects reach Verified at 13.1% (June: 8.2%); general developer tools at 24.2% (June: 24.8%). Much of that gain is the July scanner rebuild removing unfair safety penalties, not the projects themselves changing. I can't separate the two cleanly without re-scoring June's inputs under today's rules, which is on the list for the next report.
- Soft flags are common. 908 projects (32.8%) have no OpenSSF Scorecard and no detectable security tooling; 616 (22.3%) have a single author with little adoption. Neither blocks, but both keep a project out of Verified.
How I report now
These changes apply to this report and every one after it:
- Blocked is always broken down by reason: legal (no license), maintenance (archived), security.
- Every security block is checked by hand before it's counted, and the review is published.
- Projects, not records. Renamed and moved repositories can leave several records pointing at one project. I de-duplicate by GitHub repository ID: 2,863 records become 2,767 projects.
- Rule changes are separated from ecosystem change. Any comparison over time names the algorithm versions in between and gives a same-rules estimate.
- Inputs are pinned. Baselines are content-addressed snapshots, and each report ships its dataset, review file, and analysis script.
- Coverage is stated. When a signal only covers part of the corpus, like vulnerability data at 28%, I say so next to the finding.
- Advisory ranges are cross-checked across OSV, GitHub's reviewed advisory, and the maintainer's own advisory before a current version is called affected.
Data and reproduction
- dataset-2026-09-24.csv: one row per scored record, with tier, score, dimensions, disqualifiers, block reason, license, GitHub repository ID, primary language, and whether a vulnerability lookup ran.
- security-blocks-review.csv: the hand review of all nine security blocks, with evidence.
- analyze.js: reproduces every number in this report from the two files above plus the pinned June snapshot (Node 18+, no dependencies). Its output is results.json.
- Daily data: /data/latest.json and the weekly Hugging Face mirror. Scoring rules: methodology.
Sources
- Pillar Security, Deadbugz campaign research (August 12, 2026). pillar.security
- CVE-2026-73498, MCP Atlassian arbitrary file read, and the package's full advisory list. osv.dev · osv.dev (all)
- GHSA-hf57-cqmx-p4gr (OmniRoute) and GHSA-xmwj-c75x-6346 (LobeHub). osv.dev · osv.dev
- GHSA-j9hg-hmwf-9qx7 / MAL-2025-191195, malware in @browserbasehq/mcp. github.com/advisories
Check a server before you install it
Paste any GitHub repo, npm package, or registry URL. The score page shows the reason behind any block.
Open Scanner