How static-pin-check.sh Enforces Runtime Pinning for Claude Plugins
The 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. It sources several shared libraries to handle specialized concerns:
pin-check.sh– Provides the core validation functionspin_check_treeandpin_check_floating_specsthat detect unpinned package specificationstargets.sh– Suppliesresolve_scan_targetsto 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 themarketplace.jsonmanifestBASE_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 failureLAUNCH_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 uses resolve_scan_targets from targets.sh to generate a focused list of candidates. This function produces a temporary targets.json containing:
- Only external entries that have changed since the
BASE_REF - All external entries when
SCAN_ALL_EXTERNALis set totrue
If the resolution returns no targets, the script writes empty results to 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:
- Name – Human-readable identifier
- URL – Source repository location
- SHA – Exact commit hash for the pinned version
- 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:
pin_check_tree– Walks the target directory to emit tab-separated rows describing each MCP server launch specificationpin_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:
pin-scanned.json– Complete JSON results file containing status for every examined entry- Step summary – Markdown table displayed in the GitHub Actions UI showing all non-waived floating auto-execution launchers
- Action outputs – Values set for
pin-scanned,pin-failed, andresultfor 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:
- 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:
#!/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 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,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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →