Static Pin Check in scan-plugins: Preventing Supply Chain Attacks in Claude Plugins

The static pin check is a deterministic, network-free validation step in the scan-plugins GitHub Action that detects floating package specifications in MCP server manifests to prevent supply chain attacks.

The anthropics/claude-plugins-community repository employs a rigorous security pipeline to validate community plugins. The static pin check serves as a critical safeguard within the scan-plugins action, ensuring that every external command declared in a plugin's manifest—whether using npx, uvx, pipx, or bunx—references an immutable, version-locked dependency rather than a mutable tag like latest.

How the Static Pin Check Works

The static pin check operates as a pure Bash and jq analysis that requires zero network access to classify dependency specifications. When the scan-plugins action runs, it invokes scripts/static-pin-check.sh, which loads the classification logic from lib/pin-check.sh to audit each mcpServers entry in .mcp.json or plugin.json manifests.

Dependency Classification Logic

In lib/pin-check.sh, the analyzer categorizes each launcher specification into one of seven distinct states:

  • Pinned – Exact version specified (e.g., pkg@1.2.3)
  • Floating – Version ranges or mutable tags (e.g., pkg@latest, pkg@^1.2)
  • Bare – Package name only without version
  • Vendored – Local binaries bundled with the plugin
  • Local – Absolute or relative paths to executables
  • VCSref – Git commit SHA or tag references
  • None – No external dependency declared

Any specification classified as floating or bare-but-not-vendored triggers an immediate policy violation warning.

CI Integration and Execution

The entry point scripts/static-pin-check.sh orchestrates the validation by parsing manifests and emitting GitHub Actions annotation commands. This script is executed automatically by the workflow defined in .github/workflows/validate-plugins.yml, ensuring every pull request undergoes automated pinning verification before merge.

Why the Static Pin Check Is Critical

Unchecked floating dependencies create a supply chain attack surface where malicious actors can publish compromised package versions that execute automatically on user machines. Because the static pin check runs deterministically without external network calls, it guarantees that the code reviewed by Claude's policy systems matches exactly what executes in production.

  • Deterministic security: Ensures runtime code matches the repository's source SHA reviewed by policy LLMs, preventing supply chain drift.
  • Zero-network guarantee: The check executes entirely within the CI environment, making it impossible for malicious plugins to evade detection via external service calls.
  • Fail-safe design: Parsing errors default to empty result sets rather than workflow crashes, maintaining CI stability when encountering malformed manifests.
  • Policy enforcement: Integrates with the broader scan-plugins security layer (defined in action.yml) to block or warn on unsafe plugins before they reach downstream users.

Running the Static Pin Check Locally

Developers can execute the same validation logic used in CI to verify their plugin manifests before submitting pull requests:


# Execute the static pin check from the repository root

ACTION_PATH=.github/actions/scan-plugins
"$ACTION_PATH/scripts/static-pin-check.sh"

When the detector identifies a floating specification, it outputs a GitHub Actions warning annotation:

::warning scan-plugins: my-plugin.json — pkg@latest; pin-state not assessed

Fixing Floating Dependency Violations

To resolve policy violations, convert floating or bare package references to pinned versions in your .mcp.json or plugin.json manifest:

{
  "mcpServers": [
    {
      "name": "my-runner",
      "command": "npx my-package@1.2.3"
    }
  ]
}

Replace mutable tags like latest or semver ranges like ^1.2.0 with explicit version strings (e.g., 1.2.3) to achieve a compliant pinned classification.

Summary

  • The static pin check is a deterministic, network-free validation step in anthropics/claude-plugins-community that audits MCP server manifests in the scan-plugins action.
  • It classifies dependencies into seven categories via lib/pin-check.sh, flagging floating and bare-but-not-vendored specifications as security risks.
  • The check runs automatically via scripts/static-pin-check.sh in the CI pipeline defined in validate-plugins.yml.
  • Version pinning prevents supply chain attacks by ensuring the exact code reviewed matches what executes at runtime.
  • Developers can run the check locally and fix violations by specifying exact package versions in their plugin manifests.

Frequently Asked Questions

What triggers a static pin check failure?

A failure occurs when the analyzer detects floating dependencies (such as pkg@latest or version ranges like pkg@^1.2) or bare package names without explicit versions in the mcpServers command declarations. These specifications resolve dynamically at runtime, creating uncertainty about which code actually executes.

Can I use npx or pipx without version pinning in my Claude plugin?

No. The scan-plugins action requires all external package runners—including npx, pipx, uvx, and bunx—to use exact version pinning (e.g., package@2.1.0). Commands without pinned versions violate the deterministic security policy designed to protect users from supply chain attacks.

How does the static pin check work without network access?

The check uses pure Bash and jq parsing implemented in lib/pin-check.sh to analyze the text of command strings in your manifest. It classifies dependencies based solely on the syntax of the package specification (looking for version tags, SHA references, or local paths) rather than querying npm, PyPI, or other registries, ensuring the validation cannot be bypassed or influenced by external services.

What should I do if my plugin uses a vendored local binary?

If your plugin bundles its own executable (vendored) or references an absolute path (local), the static pin check will classify these as vendored or local respectively. These classifications are considered safe and compliant, provided the binary is actually included in your repository or the path is correctly specified, as they do not rely on external package registries at runtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →