The dsh Plugin Ecosystem Has No Permission Model

A capability survey of the DeepSeek Harness plugin ecosystem — August 2026

中文版 · Data: dsh-xray · Method: static analysis, nothing executed


Summary

The dsh-plugin topic went from roughly 200 repositories to 7,000+ in 30 days. We statically scanned the most-starred ones and found that a large majority carry what we call a powerful capability surface: the ability to rewrite the system prompt, intercept API traffic, spawn subprocesses, or patch the runtime itself.

The headline is not that plugins are dangerous. It is this:

Plugins are not exceeding their permissions. There are no permissions to exceed.

dsh has no plugin-level permission declaration and no runtime enforcement of one. The inject field resolves load order and dependencies — it is not a permission grant. Installing a plugin gives it the context it asks for, and nothing in the install path tells you what that means.

That is a normal stage for a fast-growing ecosystem to be in. npm was here once. It is also the stage where the cost of getting it wrong is lowest, which is why it is worth naming now.


What we measured

SourceGitHub topic:dsh-plugin, ranked by stars
ScannedSee live data — this report uses the run dated in the footer
MethodStatic analysis of shipped code (test/dev code classified separately and excluded from risk flags)
Not doneNo execution, no dynamic analysis, no network observation

Every finding below links to file:line evidence in the registry. Disputes are welcome as issues.


Finding 0 — Carrying the topic is not the same as being a plugin

KindCountShare
Installable plugin (declares dsh.bundle)1396786%
Declares only dsh.client — not installable6524%
No dsh integration found5453%
A skill, not an installable plugin4603%
Depends on dsh packages, no plugin manifest4323%
Plugin code with no manifest to install it by2051%

14% of repositories carrying the dsh-plugin topic are not installable plugins.

Adding a GitHub topic takes one click. Being installable takes a dsh.bundle manifest — that is what dsh plugin add acts on, and declaring only dsh.client is not enough. Sorting the topic by that test produces a result we did not expect:

StarsActually installable
1,000+20%
100–99945%
10–9977%
1–978%
081%

The correlation runs backwards. Among repositories carrying the topic with a thousand stars or more, only one in five is a plugin you can install. The most-starred include a résumé builder, a terminal coding agent, an agent platform, and a browser simulation of a spinning noisemaker toy — none of which integrate with dsh at all.

The plugin ecosystem is the long tail. The names at the top are mostly popular projects that added a trending topic, and anyone judging the ecosystem by its most-starred entries is largely not looking at plugins.

This matters for the rest of this report: every figure below is computed over repositories we could scan, and the capability findings describe what those repositories contain. Read the type on any capability card to see which kind you are looking at.


Finding 1 — Powerful capability is the norm, not the exception

LevelMeaningCountShare
C3Powerful + sensitive behavior513732%
C2Powerful (one of)991761%
C1Ordinary7104%
C0No notable surface4973%

93% of scanned plugins carry a powerful capability surface (C2+); 32% combine it with sensitive behavior (C3).

A correction. Earlier versions of this report put the C2+ share at 91%. That figure was wrong, and wrong in our favour: two scanning rules credited plugins with code they had not written. Comments mentioning child_process scored an exec flag, and — the larger error — build output in lib/ and dist/ was read as authored code, so a plugin was flagged for eval that its bundler had inlined from a dependency. Frequently that dependency was schemastery, which ships with dsh itself.

We found this by sampling our own flags and reading the source each one cited. Fixing it moved about one plugin in seven down a level. The corrected figure is what the table above shows.

The finding itself survives the correction: powerful capability is still the ordinary case, not the exception. But it is less overwhelming than we first reported, and anyone who quoted the old number should use this one.

Coverage: we hold cards for 3,972 of the 7,010 repositories in the topic. Every repository with two or more stars is covered, along with 4,092 of the 4,115 that have at least one; the remaining gap is the zero-star tail, queued for later runs. 120 repositories contain no scannable code.

Finding 2 — Patching the host runtime is routine

13844 plugins (85%) patch the dsh runtime itself via manifest.bundle.patch.

The most-starred among them:

manifest.bundle.patch points at a cordis.patch.yml that modifies dsh core behavior at load time. This is the deepest supply-chain surface in the system — deeper than any tool call, because it changes the host that adjudicates tool calls.

In most package ecosystems, patching the host runtime is an exceptional act requiring justification. Here it is a routine integration technique, used by ordinary UI and productivity plugins. Neither the install flow nor the plugin listing surfaces it.

Finding 3 — The manifest does not describe behavior

Take a 4,300-star UI plugin. Its manifest's client.inject reads:

["@deepseek-ai/dsh-client-runtime", "@deepseek-ai/dsh-client-locale", "…"]

Those are npm package dependencies. Meanwhile its code obtains 17 runtime services — among them systemPrompt, apiProxy, subprocess, and webServer — and hooks both system-prompt/assemble and api/gate.

Nothing here is deceptive: inject exists for dependency resolution and load ordering, and it does that job correctly. The point is that no manifest field describes capability at all. A user who reads every line of the manifest still cannot learn that this plugin can rewrite prompts and intercept API traffic. The information is only in the code.

Finding 4 — Credential surface is broad and mostly legitimate

1595 plugins (10%) read credential-class environment variables (names containing KEY/TOKEN/SECRET/PASSWORD), spanning AWS, Aliyun OSS, GitHub, and several model-provider APIs.

Reading AWS_ACCESS_KEY_ID in a storage plugin or DEEPSEEK_API_KEY in a model router is exactly what those plugins are for. The issue is not that they read credentials — it is that a plugin that had no business reading them would look identical at install time.


What this is not

We are not publishing a list of malicious plugins. We found no evidence of malice, and we did not look for it — static capability analysis cannot establish intent.

Capability levels (C0–C3) measure capability surface and transparency, not risk of harm. Several of the highest-scoring projects are among the ecosystem's best-engineered: a C3 rating on a desktop shell or a design tool usually means it genuinely needs subprocesses and a local server to do its job.

The distinction we care about is between a plugin that needs strong capability and a plugin that has strong capability without anyone noticing — and today, from the outside, those look the same.


What would fix this

Ordered by cost to the ecosystem, cheapest first:

#ChangeOwnerEffort
1Publish capability cards at install time — even informationalRegistries, install toolsLow
2Add a declarative capabilities field to the plugin manifest, unenforced at firstdsh coreLow
3Warn on mismatch between declared capabilities and observed codeTooling (this project)Medium
4Gate C2+ installs behind explicit consent, the way tools/pre-execute already gates tool callsdsh coreMedium
5Enforce declared capabilities at the context boundarydsh coreHigh

Step 2 is the one that matters most and costs least. dsh already has the right machinery for step 4 — tools/pre-execute returning {kind: 'ask'} is exactly this pattern, applied one layer down. Plugin installation simply never got the same treatment as tool execution.


Method, limits, and how to check our work

Corrections and disputes: github.com/unStone/dsh-xray/issues. Rules are fixed in public and rescans run daily.


Data snapshot: 2026-10-02 · 16261 of 16592 repositories scanned · Top flags: runtime_patch 13844, exec 4572, prompt_surface 1968, token_env 1595, base64_decode 1446, net_server 1251

Generated by dsh-xray; figures refresh with the daily scan.