SW-SKL-009: Unpinned dependency install
Last updated 7 November 2026
Also known as: unpinned dependency, rug pull (package form).
Explained in: MCP rug pull attacks
| Fact | Value |
|---|---|
| Severity | Medium |
| Checks | Skill package |
| 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.1.0 |
What it detects
The rule reads the script files and fenced code blocks of the package, one joined command at a time. It knows these installers and what counts as unpinned for each. For pip, pipx and uv pip, a package name with no == or @; options that take a value, such as a requirements file or an index, are skipped, and so are paths, URLs and wheel or archive files. For npm, yarn and pnpm, a package with no version after an at sign. For npx, the first argument without a version. For go, any go install ending in @latest.
For cargo and gem, any install that names a package and has no version option. For apt and apt-get, a package with no equals sign and a version. For Homebrew, every brew install, because it cannot pin a version in the command. A command is one finding at its first line, with the command as evidence. Installs from a local path or a direct URL are not a finding of this rule.
Why it matters
A name without a version means "whatever is newest when this runs". If the package is later taken over, or a new release is poisoned, every run of the skill fetches it, and the person who approved the skill approved the old code. Pinning makes the dependency a fixed thing that can be reviewed once. 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
This setup script installs a Python package by name only:
.claude/skills/report-builder/SKILL.md
---
name: report-builder
description: Builds a weekly report from a spreadsheet.
license: MIT
version: 1.0.0
---
# Report builder
Run scripts/setup.sh once before the first report..claude/skills/report-builder/scripts/setup.sh
#!/bin/sh
pip install requestsThe finding is on the install line.
How to fix
Pin each install to an exact version, for example by writing the version after a double equals sign for pip, and add a lock file or hashes where the tool supports them. Update the pin on purpose, in a change someone reviews.
If it is a false positive
Installing a development tool for the user's own machine, where the newest version is what the user wants, matches too. Accept the finding with a note that says the install is a convenience and not part of what the skill runs, or pin it anyway.
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.