The Permission Model
Every skill in the Claude Code Plugins catalog declares, upfront, the complete list of tools it is allowed to use.
Not a suggestion. Not a hint. An explicit, enumerated surface area that Claude enforces at runtime before executing
a single action. If a skill's frontmatter says allowed-tools: Read, Glob, that skill cannot write a
file, run a command, or fetch a URL — even if the skill's instructions tell Claude to try.
This is the principle of least privilege expressed as a data structure. And it's worth understanding exactly what that means in practice, because the 13 tools in the specification are not created equal.
The 13 Tools and Their Risk Profiles
The complete list of valid tools as of the 2026 spec is: Read, Write, Edit,
Bash, Glob, Grep, WebFetch, WebSearch,
Task, TodoWrite, NotebookEdit, AskUserQuestion, and
Skill. Any tool name outside this list is flagged as invalid during validation and will fail CI.
Each tool has a different blast radius. Some of them can only observe — they read data from the filesystem or
search for patterns. Others can modify state. And one of them, Bash, can in principle do anything
the operating system allows.
| Tool | What It Does | Risk Level | Reversible? |
|---|---|---|---|
Read |
Reads file contents from the filesystem | Low | N/A (read-only) |
Glob |
Matches file paths by pattern | Low | N/A (read-only) |
Grep |
Searches file contents by regex | Low | N/A (read-only) |
WebSearch |
Queries a search engine | Low | N/A (read-only) |
AskUserQuestion |
Prompts the user for input before proceeding | Low | N/A (interactive) |
TodoWrite |
Manages the structured task list | Low–Medium | Yes |
Write |
Creates or overwrites files | Medium | With backups |
Edit |
Makes targeted in-place changes to existing files | Medium | With version control |
WebFetch |
Fetches content from a URL | Medium | N/A (network I/O) |
NotebookEdit |
Modifies Jupyter notebook cells | Medium | With version control |
Skill |
Invokes another skill (chaining) | Medium–High | Depends on child skill |
Task |
Spawns a subagent with its own execution context | High | Depends on subagent |
Bash |
Executes shell commands (unrestricted or namespaced) | High | Depends on command |
The risk levels here aren't arbitrary. They map to two things: the ability to modify persistent state, and the
ability to interact with systems outside the current project. A skill that only uses Read,
Glob, and Grep cannot change anything. A skill that uses Write and
Edit can change files, but only files. A skill that uses Bash without constraints
can delete files, run network requests, install packages, or fork processes.
Why Bash Gets Special Treatment
There's a reason Bash is the one tool in the spec that supports namespacing. Every other tool in
the list has a well-defined scope — Read reads, Write writes, Grep
searches. Their names are their contracts. But Bash is different. Bash is a meta-tool. It grants
access to everything the shell can reach, which is effectively everything your user account can touch on the
operating system.
When a skill declares allowed-tools: Bash with no namespace, it's asking for unrestricted shell
access. That skill could, in principle, run rm -rf, curl | bash, or
git push --force. Whether it actually does any of those things depends entirely on the skill's
instructions — but the permission has been granted, and there's no technical guardrail below it beyond Claude's
own judgment.
Unrestricted Bash access is not inherently malicious — many legitimate skills need it. But it
requires more scrutiny than any other tool in the spec. When you see it, your first question should be: does
this skill actually need that much access?
This is where namespacing comes in. Instead of granting full shell access, a skill author can constrain
Bash to a specific command prefix. Bash(npm:*) means the skill can only run commands
that start with npm. Bash(terraform:*) means only Terraform commands. The wildcard
after the colon means "any arguments to that command," but the command itself is fixed.
This is a meaningful security boundary. A skill that declares Bash(npm:*) cannot run
git push, cannot delete files, cannot hit an arbitrary URL with curl. It can
run npm install, npm test, npm publish — all the npm surface area —
but nothing outside it. The constraint is narrow enough to be meaningful and specific enough to be auditable
by a human reviewer in about three seconds.
Namespaced Bash in Practice
Across the skills catalog, several Bash namespace patterns appear repeatedly. Each maps to a different category of work — package management, databases, infrastructure, cloud APIs. Here's what each one grants and, equally importantly, what it prevents.
| Namespace | What It Allows | What It Prevents | Common Use |
|---|---|---|---|
Bash(npm:*) |
All npm subcommands and flags |
Git, file deletion, curl, any non-npm command | Node.js dependency management, package publishing |
Bash(python:*) |
All python invocations |
System commands, pip (separate binary), file I/O via shell | Running scripts, REPL evaluation, test runners |
Bash(psql:*) |
PostgreSQL CLI commands | MySQL, file writes, network calls outside psql | Database migrations, query execution, schema inspection |
Bash(mysql:*) |
MySQL CLI commands | PostgreSQL, other databases, system shell | MySQL query execution, dump/restore operations |
Bash(ansible:*) |
Ansible CLI toolchain | Direct SSH, file system writes outside ansible | Playbook execution, inventory management |
Bash(terraform:*) |
All Terraform subcommands | AWS CLI directly, kubectl, file system manipulation | Infrastructure plan/apply, state management |
Bash(cmd:*) |
Windows Command Prompt commands | PowerShell, bash/sh, Unix utilities | Windows-specific automation workflows |
Bash(api:*) |
API-related CLI commands (curl, httpie, etc.) | File system, package managers, database CLIs | REST API testing, endpoint verification |
Bash(aws:s3:*) |
AWS S3 CLI operations only | EC2, IAM, Lambda, all non-S3 AWS services | Object storage reads/writes, bucket management |
Bash(az:storage:*) |
Azure Storage CLI operations only | Azure Compute, Azure AD, all non-storage Azure services | Blob storage operations, file share management |
Notice the two-level namespaces at the bottom: Bash(aws:s3:*) and Bash(az:storage:*).
This pattern is particularly powerful because it constrains not just which CLI tool is available, but which
subcommand group within that tool. A skill with Bash(aws:s3:*) cannot touch IAM policies, cannot
modify Lambda functions, cannot list EC2 instances. It is locked to the S3 service surface only.
This layered namespacing is how you express "this skill needs cloud access, but only to one specific service" — a common and legitimate requirement that, without namespacing, would require granting full AWS CLI access or nothing at all.
The Risk Spectrum
Security models that treat all tools as equally risky aren't useful. Real threat modeling requires calibrating attention to the actual blast radius of each tool. Here's a more granular breakdown of how to think about the risk spectrum.
Low Risk: Observation Tools
Read, Glob, Grep, and WebSearch share a defining property:
they cannot change anything. They observe and return. A skill that uses only these four tools is, at most,
an information leak risk — and even that depends heavily on what the skill does with what it reads. In most
contexts, a purely observational skill is safe to run without careful review.
AskUserQuestion also falls here. Its function is to pause execution and wait for human input
before proceeding — which is actually a risk-reducing pattern, not a risk-adding one. Skills that ask before
acting are generally safer than skills that don't.
Medium Risk: State Modification
Write, Edit, and NotebookEdit can all change files on disk. The risk
depends on what files are in scope and whether the project uses version control. In a git repository, most
file changes are recoverable — which is why these tools sit in the medium range rather than the high range.
Outside version control, file writes are permanent, and the risk profile shifts upward accordingly.
WebFetch introduces a different kind of medium risk: network I/O. It can retrieve data from
external URLs, which means it can be used to exfiltrate data via URL parameters, or to pull in content from
untrusted sources. This risk is theoretical in most skill contexts — legitimate skills use WebFetch to read
documentation or fetch API specs — but it's worth noting that the tool has an outbound vector that the
pure filesystem tools don't.
High Risk: Execution and Delegation
Task and Bash are the two tools where reviewer attention should concentrate.
Task spawns a subagent — an entirely separate execution context that can have its own tool
permissions, its own instructions, and its own chain of actions. The parent skill's constraints don't
automatically propagate to subagents. A skill that uses Task needs to be reviewed not just
for its own permissions, but for what it might spawn.
Bash, especially unrestricted, sits at the top of the risk spectrum for the reasons described
earlier. But even namespaced Bash deserves scrutiny. Bash(terraform:*) with instructions to
run terraform apply -auto-approve on production infrastructure is high risk regardless of
the namespace constraint. The namespace limits the command family; it doesn't limit what that command family
can do to your infrastructure.
Bash (unrestricted) or Task, read the skill instructions carefully before the description. The permissions are the lead signal.
What the CI Pipeline Catches
The validation pipeline that runs on every pull request and commit to the plugins repository is a multi-layered automated review. Understanding what it catches — and what it doesn't — is useful context for anyone thinking about the actual security posture of the catalog.
Secret Scanning
Every plugin file is scanned for patterns that match common secret formats: API keys, tokens, private key
headers, connection strings with embedded credentials. This scan runs at multiple points — on plugin source
files during the validate CI job, and again on the generated zip archives during the
validate-cowork-security.mjs post-build step.
The post-build scan on zip files is particularly important because it catches cases where secrets might be embedded in generated artifacts even when the source files looked clean. A build step that inlines configuration values could theoretically produce a zip containing credentials that weren't in any source file the pre-build scan examined.
Dangerous Pattern Detection
Beyond credential patterns, the pipeline scans for shell patterns that are frequently used in supply chain
attacks: piping curl output to bash, downloading and executing remote scripts, suspicious use of
eval, and similar constructs. These patterns don't automatically fail a plugin, but they
flag it for closer human review.
Tool Name Validation
The validate-skills-schema.py script enforces that every tool name in every
allowed-tools declaration matches the known valid set. An unrecognized tool name — whether
from a typo or an attempt to declare a tool that doesn't exist in the spec — fails validation outright.
This prevents a class of confusion attacks where a skill might try to claim permissions that look legitimate
but aren't actually valid.
Structural Completeness Checks
Required fields — name, description, version, author,
and allowed-tools — must all be present. Missing frontmatter fields fail validation before
the skill can be published. This isn't just housekeeping: an incomplete permissions declaration is
semantically ambiguous, and the pipeline refuses to let ambiguous skill definitions reach the catalog.
The 8-Field Constraint as Security
One of the less obvious security properties of the catalog is the strict constraint on plugin.json
structure. The spec allows exactly eight fields: name, version,
description, author, repository, homepage,
license, and keywords. Any additional field causes CI to fail.
This might look like arbitrary strictness, but it's actually preventing a specific class of attack.
Configuration Injection
Package manifests that accept arbitrary fields are a vector for configuration injection. An attacker who can add arbitrary fields to a package manifest can potentially influence how that manifest is interpreted by downstream tooling — install hooks, resolution strategies, registry behavior. The npm ecosystem has seen this attack surface exploited in practice.
By enforcing a strict eight-field allowlist, the catalog eliminates this entire class of risk. There is no
way to embed a scripts.postinstall field, a dependencies object, or any other
mechanism that downstream tooling might act on. The manifest is inert beyond the declared fields.
Dependency Confusion Prevention
Dependency confusion attacks work by publishing a package to a public registry with the same name as a
private internal package, relying on the package manager preferring the public version. The eight-field
constraint doesn't prevent this directly, but it removes the dependencies and
devDependencies fields from the manifest entirely — plugins cannot declare npm dependencies
in their plugin.json at all. MCP plugins (TypeScript) have their own package.json that's
evaluated separately, but AI instruction plugins have no dependency surface to exploit.
scripts blocks.
Practical Guidance for Reviewers
If you're reviewing a plugin before adding it to your own setup, or contributing to the catalog's review process, here's what to look for — and what distinguishes a reasonable permission request from a red flag.
Normal Patterns
A skill that reads code and writes tests probably needs Read, Glob, Grep,
and Write — that's a coherent, minimal permission set for that task. A skill that manages npm
dependencies needs Bash(npm:*). A skill that inspects and repairs database schemas needs
Bash(psql:*) or Bash(mysql:*) plus Read. The permissions should
tell a story that matches the skill's stated purpose.
It's also normal for complex, multi-purpose skills to request broader permissions. A "full-stack development workflow" skill that orchestrates an entire feature branch — writing code, running tests, managing git — legitimately needs a broader tool surface than a focused single-purpose skill. The question isn't just "are these permissions wide?" but "do these permissions match what the skill actually does?"
Red Flags
-
Unrestricted Bash with vague instructions. If a skill declares
Bashwithout namespacing and the description is generic or the instructions are opaque, that's worth extra scrutiny. Legitimate skills that need full shell access typically have obvious reasons — deployment scripts, system configuration, complex build orchestration. -
Task with no explanation of what's being spawned. Skills that use
Taskto spawn subagents should describe what those subagents do. A skill that says "uses Task to coordinate work" without any specifics about what that coordination entails is giving you incomplete information about its actual blast radius. - WebFetch with Bash. The combination of "can fetch arbitrary URLs" and "can run shell commands" is the core pattern in "curl | bash" style attacks. Skills that combine these two tools should have a clear, benign explanation — fetching an API spec to generate code, for instance — and instructions that obviously don't connect the fetch output to the shell execution.
- Permissions that don't match the description. A "code formatting" skill that requests database access. A "documentation generator" that requests Terraform permissions. When the permissions and the stated purpose are misaligned, either the description is incomplete or something else is going on.
-
Skill chaining with
Skillacross trust boundaries. A skill in a low-trust plugin that usesSkillto invoke a skill from a different, more permissive plugin is attempting to access elevated permissions through delegation. This pattern needs explicit review.
Practical Review Checklist
| Check | What to Look For | Pass Condition |
|---|---|---|
| Permission coherence | Do the declared tools match what the skill claims to do? | Every permission has an obvious use in the instructions |
| Bash namespace | Is Bash namespaced? If not, why not? | Namespaced, or has an explicit reason for full access |
| Task delegation | Does the skill use Task? What do subagents do? | Subagent purpose is described and benign |
| WebFetch + Bash | Does the skill combine network fetch with shell execution? | The two are clearly not connected in the instructions |
| Secret patterns | Are any credentials or tokens embedded in skill instructions? | No static credentials anywhere in the file |
| Minimal access | Could the skill work with fewer permissions? | No obviously unnecessary tools are declared |
What Remains Open
The permission model described here is a real security improvement over "Claude can use any tool it decides to use." But intellectual honesty requires being clear about what it doesn't protect against. No permission model is a complete security solution, and this one has known boundaries.
Prompt Injection via Fetched Content
A skill that uses WebFetch to retrieve a URL, then passes that content to Claude as context,
is exposed to prompt injection in the fetched content. The fetched page could contain instructions designed
to manipulate Claude's behavior — to expand its effective permissions, to take actions outside the skill's
stated scope, or to exfiltrate data through legitimate channels. The tool permission model cannot fully
protect against this because the attack vector is in the content, not the tool invocation.
The same risk applies, to a lesser degree, to Read — a file that a skill reads might contain
adversarial content if the file was created by an untrusted source. Skills that read then act on content
from untrusted sources carry an inherent injection risk that permission declarations alone cannot neutralize.
Social Engineering Through Skill Instructions
The validation pipeline checks that skill instructions don't contain embedded credentials or obviously dangerous patterns. But it can't evaluate whether a skill's instructions, taken as a whole, constitute social engineering. A skill that's technically well-formed but designed to manipulate Claude into behavior that serves the skill author's interests over the user's would pass most automated checks. This is a fundamentally human review problem, and automated tooling can only help at the margins.
Namespace Escape Edge Cases
The Bash(npm:*) namespace is enforced by checking the command prefix. But npm
itself has features that can invoke other programs — npm run executes scripts defined in
package.json, and those scripts can be arbitrary shell commands. A Bash(npm:*)
constraint allows npm run deploy, which might invoke terraform apply under the
hood. The namespace constrains what Claude can directly invoke; it doesn't constrain what those invocations
do transitively.
This is a known limitation and not unique to this permission model — it's a fundamental property of how shell namespacing works. Mitigating it fully would require sandboxing at the OS level, which is beyond what the catalog can enforce for every runtime environment.
The Underlying Trust Model
The permission system works within a trust model: Claude is assumed to follow its declared permissions honestly, and skill authors are assumed to be trying to solve legitimate user problems. Both assumptions are reasonable in most cases, and the catalog's review process and CI pipeline are designed to catch deviations from the second assumption before they reach users.
But the model is not adversarial-proof. A determined, technically sophisticated actor who wanted to sneak a malicious skill through review would face real obstacles — secret scanning, dangerous pattern detection, structural validation, human review for featured skills. Those obstacles are meaningful. They're just not a guarantee.
The honest summary: the permission model makes malicious skills harder to write, easier to detect, and limited in blast radius when they slip through. It does not make them impossible. The appropriate response to that is vigilance and process, not paralysis.
The current architecture — explicit permission declarations, namespaced Bash constraints, eight-field plugin manifests, automated secret scanning, and post-build security validation across 315 plugins — represents a deliberate and documented approach to shifting the security baseline. Understanding where the boundaries are is part of using the system responsibly.
Cite This Research
Tool Permission Threat Modeling. Claude Code Plugins Research, 2026. https://tonsofskills.com/research/tool-permission-threat-modeling/