# How static-pin-check.sh Enforces Runtime Pinning for Claude Plugins

> Learn how static-pin-check.sh enforces runtime pinning for Claude Plugins. This Bash script validates external marketplace entries to prevent floating package versions and ensure stable dependency management.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: how-to-guide
- Published: 2026-09-11

---

**The [`static-pin-check.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/static-pin-check.sh) script is a deterministic Bash validator that ensures external Claude Plugins Marketplace entries use pinned dependencies rather than floating package versions at runtime.**

This script runs as part of the GitHub Actions workflow in the `anthropics/claude-plugins-community` repository to guarantee that third-party plugins cannot hide unpinned auto-execution commands behind a specific commit SHA. By validating every external entry against a strict pinning policy, the tool provides network-free enforcement that prevents supply-chain attacks through mutable dependency resolutions.

## Core Architecture and Dependencies

The script operates as a standalone entry point located at [`.github/actions/scan-plugins/scripts/static-pin-check.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/actions/scan-plugins/scripts/static-pin-check.sh). It sources several shared libraries to handle specialized concerns:

- **[`pin-check.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/pin-check.sh)** – Provides the core validation functions `pin_check_tree` and `pin_check_floating_specs` that detect unpinned package specifications
- **[`targets.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/targets.sh)** – Supplies `resolve_scan_targets` to determine which marketplace entries require scanning

These libraries separate the orchestration logic from the specific pinning rules, making the system maintainable as the marketplace grows.

## Environment Setup and Validation

Before processing any entries, the script performs strict environment validation. It checks for three required variables:

- **`MARKETPLACE_PATH`** – Filesystem path to the [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) manifest
- **`BASE_REF`** – Git reference for comparing changes (typically the PR base)
- **`ALLOWED_HOSTS`** – Space-separated list of approved source code hosts (e.g., `github.com gitlab.com`)

The script also reads optional flags:

- **`FAIL_ON_UNPINNED_AUTOEXEC`** – Boolean that determines whether floating specs trigger a workflow failure
- **`LAUNCH_SHAPE_WAIVERS`** – Path to an optional waivers file for permitted exceptions

If the waivers file path is provided but the file does not exist, the script aborts immediately with a clear error message.

## Scan Target Resolution

Rather than scanning the entire marketplace on every run, [`static-pin-check.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/static-pin-check.sh) uses `resolve_scan_targets` from [`targets.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/targets.sh) to generate a focused list of candidates. This function produces a temporary [`targets.json`](https://github.com/anthropics/claude-plugins-community/blob/main/targets.json) containing:

- Only external entries that have changed since the `BASE_REF`
- All external entries when `SCAN_ALL_EXTERNAL` is set to `true`

If the resolution returns no targets, the script writes empty results to [`pin-scanned.json`](https://github.com/anthropics/claude-plugins-community/blob/main/pin-scanned.json), sets the action output to passing, and exits cleanly with status code 0.

## Entry Processing and Safety Guards

For each external entry in the targets list, the script extracts four critical fields from the JSON object:

1. **Name** – Human-readable identifier
2. **URL** – Source repository location
3. **SHA** – Exact commit hash for the pinned version
4. **Sub-directory** – Optional path within the repository

Before attempting any network operations, the script runs a series of guard checks to skip entries that cannot be safely assessed. It validates URL and SHA presence, checks for unsafe characters, verifies the host against `ALLOWED_HOSTS`, confirms SHA formatting, and ensures the sub-directory path contains no traversal sequences. Any entry failing these checks is recorded as *unassessed* with a descriptive warning.

## Pin Validation Logic

When an entry passes safety validation, the script clones the repository at the exact declared SHA into a temporary directory with a 120-second timeout. Failure to clone or checkout results in an *unassessed* status.

The actual validation happens in two stages using functions from [`pin-check.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/pin-check.sh):

1. **`pin_check_tree`** – Walks the target directory to emit tab-separated rows describing each MCP server launch specification
2. **`pin_check_floating_specs`** – Filters those rows to collect only floating specs (e.g., `package@latest`, semantic version ranges, or unpinned installations)

If no floating specs are found, the entry is marked as clean and the script proceeds to cleanup. This deterministic approach ensures the validation requires no network access beyond the initial clone, making results reproducible across runs.

## Waiver Handling and Failure Reporting

When floating specs are detected, the script checks for waivers if a `LAUNCH_SHAPE_WAIVERS` file was provided. The function `pin_check_entry_waived` determines whether the specific floating specifications are covered by an explicit exception.

For unwaivered floating specs, the script builds a JSON array describing each problematic launcher, including the server name, launcher type, and exact floating specification. It also captures cases where no derivable spec exists (marked as `none`).

Depending on the `FAIL_ON_UNPINNED_AUTOEXEC` flag, the script emits either a GitHub warning or a GitHub error annotation. The message includes the offending entry, the specific floating dependency, and instructions to pin an exact version to resolve the issue.

## Cleanup and Output Generation

After processing each entry, the script removes the temporary clone directory to prevent disk space exhaustion during large scans. Once the loop completes, it generates three outputs:

1. **[`pin-scanned.json`](https://github.com/anthropics/claude-plugins-community/blob/main/pin-scanned.json)** – Complete JSON results file containing status for every examined entry
2. **Step summary** – Markdown table displayed in the GitHub Actions UI showing all non-waived floating auto-execution launchers
3. **Action outputs** – Values set for `pin-scanned`, `pin-failed`, and `result` for downstream workflow steps

If any failures exist and `FAIL_ON_UNPINNED_AUTOEXEC` is set to `true`, the script exits with a non-zero status code to fail the workflow and block the pull request.

## Practical Usage Examples

Configure the script within a GitHub Action workflow:

```yaml
- name: Static Pin Check
  uses: ./.github/actions/scan-plugins
  with:
    marketplace_path: ${{ github.workspace }}/marketplace.json
    base_ref: ${{ github.event.pull_request.base.sha }}
    allowed_hosts: |
      github.com
      gitlab.com
    fail-on-unpinned-autoexec: true
    launch_shape_waivers: ${{ github.workspace }}/waivers.txt

```

Run locally for debugging or initial marketplace validation:

```bash
#!/usr/bin/env bash
export MARKETPLACE_PATH=./marketplace.json
export BASE_REF=main
export ALLOWED_HOSTS="github.com"
export FAIL_ON_UNPINNED_AUTOEXEC=true

./.github/actions/scan-plugins/scripts/static-pin-check.sh

```

Local execution produces [`pin-scanned.json`](https://github.com/anthropics/claude-plugins-community/blob/main/pin-scanned.json) in the working directory and prints annotations to stdout for any detected floating specifications.

## Summary

- **static-pin-check.sh** validates that external Claude Plugins use immutable dependency pins rather than floating versions at runtime
- The script sources specialized libraries ([`pin-check.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/pin-check.sh), [`targets.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/targets.sh)) to separate concerns between target resolution and pinning logic
- Validation runs deterministically after cloning the exact SHA declared in the marketplace, requiring no additional network access
- Safety guards prevent processing of entries with malformed URLs, untrusted hosts, or suspicious path traversals
- Waiver support allows explicit exceptions for specific launch shapes while maintaining audit trails
- Integration with GitHub Actions provides automatic annotations and configurable workflow failure on policy violations

## Frequently Asked Questions

### What happens if the marketplace.json file is missing when static-pin-check.sh runs?

The script performs an existence check on the `MARKETPLACE_PATH` file and aborts with an error if the manifest is not found. This prevents false positives that would occur from scanning an empty or non-existent marketplace.

### How does the script detect floating versus pinned dependencies?

After cloning the repository at the declared SHA, the script calls `pin_check_tree` to enumerate all MCP server launch specifications, then pipes these through `pin_check_floating_specs`. This filter identifies specifications containing version ranges, `latest` tags, or missing version constraints that would allow the package manager to resolve different code at session start versus scan time.

### Can the script be used outside of GitHub Actions?

Yes, though it requires manual environment setup. You must export the required variables (`MARKETPLACE_PATH`, `BASE_REF`, `ALLOWED_HOSTS`) and ensure the helper libraries in `.github/actions/scan-plugins/lib/` are accessible at the expected relative paths. The script writes results to local JSON files and stdout, making it suitable for local CI testing or pre-commit hooks.