SW-CFG-009: Marketplace plugin source not pinned to a commit
Last updated 7 November 2026
Also known as: unpinned plugin source, rug pull (package form).
Explained in: MCP rug pull attacks
| Fact | Value |
|---|---|
| Severity | Medium |
| Checks | Configuration file |
| Malicious indicator | No |
| OWASP Agentic Top 10 | ASI04 |
| OWASP MCP Top 10 | MCP04 |
| NIST SP 800-53 | SR-3 (provisional mapping to SP 800-53 base controls) |
| First shipped in | Scanner 0.3.0 |
What it detects
The rule reads .claude-plugin/marketplace.json and judges each plugin whose source is an object of kind github (with a repo) or url. A source given as a local path is not judged, and nor is a source of another shape. A plugin that is hosted elsewhere is listed and locked by its declared source and never fetched by the scanner.
A source counts as pinned by a sha of exactly 40 hexadecimal characters, or, when there is no sha, by a ref of that form. A tag such as v2 or a branch name is not a pin. A sha that disagrees with a commit-shaped ref is not a pin, and an abbreviated sha is not a pin whatever the ref says. Each unpinned plugin is one finding on the line of its name, with evidence of the plugin name and its source; the address of a raw URL source is shown without any user name or password.
Why it matters
A marketplace entry that names a repository and a tag or branch, not a commit, lets the plugin installed tomorrow differ from the plugin reviewed today: a branch moves by design and a tag can be moved. A plugin can carry skills, MCP servers and hooks, so a changed plugin changes what the agent can do on every machine that installs it. The scanner maps this rule to OWASP ASI04, Agentic Supply Chain Vulnerabilities, and MCP04, Software Supply Chain Attacks & Dependency Tampering, and to NIST SP 800-53 control SR-3, Supply Chain Controls and Processes.
Example
The marketplace below lists one plugin from a GitHub repository at the tag v2. A tag is not a commit:
.claude-plugin/marketplace.json
{
"name": "demo-tools",
"plugins": [
{
"name": "release-notes",
"source": {
"source": "github",
"repo": "demo-agent/release-notes-plugin",
"ref": "v2"
}
}
]
}The evidence reads the plugin name and github:demo-agent/release-notes-plugin, followed by "no declared commit pin". A sha of the full 40-character commit would clear the finding.
How to fix
Add a sha with the full commit id that the plugin was reviewed at, and move to a newer commit through a reviewed change. An id of seven characters is not accepted, and neither is a sha that disagrees with a commit-shaped ref.
The private registry also admits a plugin or marketplace only at a full commit (see the private registry guide), so the pin serves both checks.
If it is a false positive
A plugin from a repository of your own organisation, which you move forward on purpose with each release, is reported like any other. Accept the finding with a note that says who reviews each release, or pin the commit and update it in the release process. A plugin kept in the same repository by a relative path is not judged at all.
If the finding is not a problem in your case, accept it with a note: in the dashboard a manager or owner can accept a finding and the note is kept with it; with the scanner on its own, add it to the baseline file with a reason and an expiry date.