Claude Plugin Security Review: 11 Policy Invariants and CI Validation Workflow
The Claude plugin security review enforces 11 strict static invariants (I1–I11) through the validate-plugins GitHub Action, validating structural integrity, preventing command injection, and pinning external dependencies to specific Git commits without executing untrusted code.
When submitting a plugin to the anthropics/claude-plugins-community repository, every pull request undergoes an automated Claude plugin security review that exceeds standard marketplace schema validation. The CI pipeline runs the validate-plugins action to block supply-chain attacks, enforce cryptographic provenance, and ensure metadata integrity before any code reaches users.
The 11 Security Invariants (I1–I11)
The core of the review is defined in .github/actions/validate-plugins/README.md, which mandates eleven static invariants stricter than the canonical Claude plugin schema:
-
I1: Alphabetical Ordering – The
plugins[]array must be sorted alphabetically by thenamefield to ensure deterministic builds and prevent merge conflicts. -
I2: Uniqueness – No duplicate
namevalues are permitted within the marketplace catalog. -
I3: Description Hygiene – The
descriptionfield must contain 10–2000 characters with no leading or trailing whitespace. -
I4: Source URL Safety – The
source.urlorsource.repomust match a strict safe URL pattern—either an HTTPS URL or a validowner/reposhorthand—preventing arbitrary protocol handlers. -
I5: Cryptographic Pinning – Every external source must provide a 40-character lowercase hexadecimal SHA commit hash, ensuring immutable, auditable dependencies.
-
I6: Filename Consistency – For per-file plugin repositories, the JSON filename must match the plugin’s
namefield (e.g.,plugins/my-plugin.jsonmust declare"name": "my-plugin"). -
I7: Generated File Protection – Pull requests cannot manually edit the assembled
marketplace.jsonfile, as it is auto-generated during the CI process. -
I8: Vendored Path Validation – Vendored
sourcepaths must exist and contain a valid.claude-plugin/plugin.jsonmanifest file. -
I9: Shell Metacharacter Sanitization – All string fields under
sourcemust be free of shell metacharacters to mitigate command injection risks during CI execution. -
I10: Unicode Homoglyph Prevention – The
nameanddescriptionfields must contain no hidden Unicode characters such as zero-width spaces, byte-order marks (BOM), or bidirectional (bidi) control characters that could spoof plugin identities. -
I11: Strict Naming Convention – The
namemust match the regex^[a-z0-9][a-z0-9-]{1,63}$, restricting identifiers to lowercase alphanumerics and hyphens only.
Multi-Stage Security Model Workflow
Beyond the static invariants, the validation workflow implements a defense-in-depth strategy through sequential steps defined in the same README:
-
Step 11 – Executes
scripts/11-validate-invariants.shto verify I1–I11 against the full marketplace dataset. -
Step 20 – Runs
claude plugin validateviascripts/20-validate-cli-marketplace.shto check canonical schema compliance. -
Step 30 – Clones each external plugin at its pinned SHA using
scripts/30-validate-cli-external.sh, validating manifests without executing plugin code. -
Step 40 – Uses
scripts/40-validate-cli-local.shto validate in-repository plugins modified in the current PR. -
Step 41 – Parses auxiliary configuration files (
.mcp.json,.lsp.json,hooks.json) throughscripts/41-validate-aux-files.shto ensure runtime safety.
This architecture ensures that external code is never executed during the review process—only statically analyzed at a specific, immutable Git commit.
Critical Implementation Files
The Claude plugin security review is implemented across several key files in the repository:
.github/actions/validate-plugins/README.md– Documents the I1–I11 invariants and security model..github/actions/validate-plugins/scripts/11-validate-invariants.sh– Enforces the eleven static policy checks..github/actions/validate-plugins/scripts/20-validate-cli-marketplace.sh– Validates against the official Claude CLI schema..github/actions/validate-plugins/scripts/30-validate-cli-external.sh– Safely clones and validates external plugins at pinned commits..github/actions/validate-plugins/scripts/40-validate-cli-local.sh– Handles in-repo plugin validation..github/actions/validate-plugins/scripts/41-validate-aux-files.sh– Parses auxiliary JSON files..github/policy/prompt.md– Contains the broader Anthropic Software Directory Policy referenced during security scans.
Code Examples for Plugin Submission
The following examples demonstrate how to satisfy the security invariants and integrate validation into your workflow.
Minimal Compliant plugin.json
This manifest satisfies invariants I3, I4, I5, and I11:
{
"name": "my-plugin",
"description": "A short description (>=10 chars, <2000).",
"source": {
"url": "https://github.com/your-org/my-plugin",
"sha": "0123456789abcdef0123456789abcdef01234567"
}
}
GitHub Actions Workflow Integration
Add this workflow to .github/workflows/validate-plugins.yml to run the security review on pull requests:
name: Validate Plugins
on:
pull_request:
paths:
- '.claude-plugin/**'
- 'plugins/**'
- '*/.claude-plugin/**'
- '*/agents/**'
- '*/skills/**'
- '*/commands/**'
jobs:
validate:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anthropics/claude-plugins-community/.github/actions/validate-plugins@<PINNED-SHA>
with:
marketplace-path: .claude-plugin/marketplace.json
Local Validation Debugging
Run the validator locally to identify specific invariant failures before submitting:
export ACTION_PATH=.github/actions/validate-plugins
export VALIDATE_TMP=/tmp/validate-plugins
export BASE_REF=origin/main
export MARKETPLACE_PATH=.claude-plugin/marketplace.json
export ALLOWED_HOSTS="github.com gitlab.com bitbucket.org"
mkdir -p "$VALIDATE_TMP"
bash $ACTION_PATH/scripts/00-detect-changes.sh
bash $ACTION_PATH/scripts/11-validate-invariants.sh
bash $ACTION_PATH/scripts/20-validate-cli-marketplace.sh
bash $ACTION_PATH/scripts/30-validate-cli-external.sh
bash $ACTION_PATH/scripts/40-validate-cli-local.sh
bash $ACTION_PATH/scripts/41-validate-aux-files.sh
bash $ACTION_PATH/scripts/90-report.sh
Summary
- The Claude plugin security review enforces 11 mandatory invariants (I1–I11) that restrict metadata format, Unicode content, and source provenance.
- The
validate-pluginsaction performs static analysis only, never executing external plugin code during validation. - All external dependencies must be pinned to a 40-character hexadecimal SHA to prevent supply-chain attacks.
- The review checks for shell metacharacters and hidden Unicode to block injection and homoglyph attacks.
- Validation occurs across five sequential steps, from invariant checking to auxiliary file parsing.
Frequently Asked Questions
What happens if my plugin fails the I10 Unicode check?
The I10 invariant rejects any name or description containing zero-width spaces, byte-order marks (BOM), or bidirectional text controls. Remove these invisible characters using a text editor that displays whitespace or run cat -v on your JSON file to identify hidden symbols.
Can I use a Git tag instead of a commit SHA for my plugin source?
No. The I5 invariant strictly requires a 40-character lowercase hexadecimal SHA. Tags are mutable and therefore rejected to ensure immutable, auditable dependencies according to the anthropics/claude-plugins-community security model.
Why can't I edit marketplace.json directly in my PR?
I7 prohibits manual edits to marketplace.json because it is a generated artifact. The file is assembled automatically by the CI pipeline from individual plugin definitions in the plugins/ directory or .claude-plugin/ subdirectories.
Does the security review execute my plugin code to test it?
No. As implemented in scripts/30-validate-cli-external.sh, the validator clones your repository at the pinned SHA and validates the plugin.json manifest only. No build steps, installation, or code execution occurs during the review process.
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 →