Rules / CFG010
CFG010 MCP server uses an unpinned package or image version
Severity: warn
OWASP: LLM04:2026 – Supply Chain
OWASP Agentic: ASI04:2026 – Agentic Supply Chain Vulnerabilities: LLM04:2026 now covers the model and data supply chain and refers the agentic one, "including MCP servers and tool registries", to ASI04
OWASP MCP: MCP04:2025 – Software Supply Chain Attacks & Dependency Tampering — provisional (MCP Top 10 v0.1)
What cfgaudit checks
cfgaudit inspects each MCP server entry — both the inline mcpServers block in settings.json and the project's .mcp.json (the file enableAllProjectMcpServers / enabledMcpjsonServers auto-approve) — and flags any of:
- An arg ending in
@latestor:latest— covers both npm-style package pins (@scope/pkg@latest) and container image tags (registry/image:latest). - An npm-style runner (
npx,pnpm dlx,yarn dlx,bunx) whose first positional package argument has no@<version>suffix — e.g.npx -y @modelcontextprotocol/server-filesystem. The package resolves to whatever the registry currently callslatest. - A Python package runner (
uvx,pipx run) whose package argument has no version pin — neither a PEP 508 specifier (ruff==0.1.0,pkg>=1.2,pkg~=1.0) nor the uvx@<version>form (ruff@0.1.0).
Local-path arguments (./pkg, ../pkg, /pkg) are not flagged.
An upstream compromise of an unpinned dependency silently propagates to every developer who reloads the agent — there is no SCA tooling between the registry and the MCP server.
Trigger example
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem@latest", "/tmp"]
}
}
}
Safe alternative
Pin to an exact version:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem@1.2.3", "/tmp"]
}
}
}
For container images, pin to an immutable digest where possible:
ghcr.io/owner/server@sha256:abcd…
References
Documentation for cfgaudit 1.18.1. Source of this page · Rule implementation