Security of Customizations

This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page is written against the Claude Code CLI 2.1.283, VS Code 1.139 with the GitHub Copilot Chat extension, and the GitHub Copilot CLI 1.0.88, all re-verified as current as of 2026-09-28. Every skill, hook, allowed-tools grant and managed-settings mechanism this sub-section shows extends what Claude Code or Copilot can do on your behalf — so each one is also something to review before you trust it. This page covers that review, for the customization surfaces themselves (skills, plugins, MCP servers, hooks and settings); Securing AI-Assisted Development covers the broader ground of vulnerabilities in AI-generated code, sandboxing and network allowlists, secret scanning and data-privacy settings, and is the fuller treatment of prompt injection in general — this page links into it rather than repeating it.

Trusting third-party skills, plugins and MCP servers

A skill, a plugin and an MCP server each extend what an assistant can do, but with different blast radii:

  • A skill (Skills) is Markdown instructions loaded into context, but it is not inert. Beyond instructing Claude to run any tool the session can reach, a third-party skill can act through its own frontmatter and body as soon as it is invoked (as of 2026-09-28):

    • its allowed-tools pre-approves the tools it lists for the turn that invokes it, so Claude can use them without prompting you — and workspace trust does not gate this field;

    • each !`<command>` line in its body runs as a shell command while the skill renders, before Claude sees the content, with the output pasted in its place;

    • its frontmatter hooks are registered when it is invoked and keep running for the rest of the session.

      Review all three before installing a skill. To stop inline shell commands in skills from user, project, plugin and additional-directory sources, set "disableSkillShellExecution": true, ideally in managed settings, where users cannot override it. Deny rules still win: a deny rule that matches an injected command aborts the invocation, Skill(<name>) in deny blocks a skill outright, and deny rules override allowed-tools. See Claude Code — Inject dynamic context, Claude Code — Restrict Claude’s skill access and Claude Code — Hooks in skills and agents.

  • A plugin (Plugins & Marketplaces) can bundle skills and agents, but also hooks and MCP servers, which run outside the permission system entirely. Claude Code states this plainly: "A Claude Code plugin you install can execute arbitrary code on your machine with your user privileges," and its hooks and MCP/LSP server processes run outside the sandbox, as shell commands with your full user permissions — only a plugin’s own tool calls (an MCP tool, or a Bash command invoking its bin/) go through your permission rules.

  • An MCP server (Configuring MCP Servers) is a process, local or remote, that hands the session a set of tools. Claude Code’s own MCP docs put it directly: "Verify you trust each server before connecting it. Servers that fetch external content can expose you to prompt injection risk."

Before trusting any of the three, verified against Claude Code’s plugin security and trust page:

Check What to look at

Who publishes it

A marketplace name alone proves nothing: Claude Code reserves the official and community name tiers for marketplaces sourced from github.com/anthropics/, and refuses to load one that claims those names from elsewhere. Every other marketplace — including one your own organization runs — is third-party, and needs the same review regardless of how trustworthy its name sounds. claude plugin marketplace list prints the source (a GitHub repository or a directory) each marketplace was added from.

What it will install

In a session, /plugin on the plugin’s details pane shows a Will install section: which commands, agents, skills, hooks, and MCP/LSP servers it adds. That section shows that a hook or MCP server exists, not what it runs.

What its hooks and MCP config actually run

Read the plugin’s own files before installing: hooks/hooks.json for the command each hook runs, .mcp.json for each server’s command or URL, and every file under bin/, since an enabled plugin’s bin/ directory is added to the PATH of the Bash tool’s shell.

A machine-readable inventory

Clone the repository holding the plugin and run claude --plugin-dir <plugin-directory> plugin details <plugin-name> to print a Component inventory —  skills, commands, agents, hooks (with each hook’s event), and MCP/LSP servers — without starting a session.

Whether it can silently change

When auto-update is on for the marketplace it came from, Claude Code updates an installed plugin in the background, so the files you reviewed can change on disk later without a fresh review prompt.

Claude Code shows this same warning in the details pane for every plugin, regardless of marketplace:

Make sure you trust a plugin before installing, updating, or using it. Anthropic does not control what MCP
servers, files, or other software are included in plugins and cannot verify that they will work as intended or
that they won't change. See each plugin's homepage for more information.

To remove a plugin you no longer trust, run claude plugin uninstall <plugin> with the --scope you installed it at, and remove the marketplace too if you no longer trust its owner. See Plugins & Marketplaces for the install/uninstall commands themselves.

Prompt injection via tool output and files

A customization surface that reads content the assistant did not write — a file a skill opens, a hook’s stdout, a grep result, an MCP tool’s response — can carry text crafted to look like an instruction rather than data. Securing AI-Assisted Development’s dedicated section covers this in depth, with worked examples from repository content and MCP tool output; the angle specific to this page is that a customization itself can be the injection vector, not just the content it processes:

  • A skill’s own description or body, if pulled from an untrusted third-party source, is loaded into context the same way any other instruction is — Skills covers how Claude decides to auto-invoke a skill from its description, which is exactly what a hostile description tries to hijack.

  • A hook’s stdout/stderr, injected back into context per Hooks's exit-code contract, is attacker-controlled if the hook shells out to something that reads untrusted input (a webhook payload, a file fetched over the network).

  • An MCP tool’s response enters the conversation like any other tool result. Claude Code’s security page lists an isolated context window only for web fetch, so an MCP server that fetches external content gets no such isolation — which is why the MCP docs quoted above warn about prompt injection from those servers.

Claude Code’s documented protections against this class of attack — a permission system that requires approval for sensitive operations in Manual mode, context-aware analysis of the full request, network commands such as curl and wget not being auto-approved, and a separate context window for web fetch — are described on its Security page, along with the practical guidance that matters most for a customization you did not write yourself: review suggested commands before approval, avoid piping untrusted content directly into a session, and verify proposed changes to critical files before accepting them. The same page suggests a deny rule when Claude shouldn’t fetch arbitrary content at all:

.claude/settings.json
{
  "permissions": {
    "deny": [
      "Bash(curl *)",
      "Bash(wget *)"
    ]
  }
}

A deny rule matches the command as written, so it isn’t a network boundary. For enforcement that doesn’t depend on the command text, use sandbox network isolation, covered on Securing AI-Assisted Development. See Claude Code — Protect against prompt injection.

Secrets in hooks and settings

A hook script or an MCP server config is a natural place to reach for a credential, and just as natural a place to leak one if it is written inline instead of read from configuration:

Bad — a credential written into the hook script itself
#!/usr/bin/env bash
# .claude/hooks/notify-slack.sh -- DO NOT do this
curl -X POST -H 'Content-type: application/json' \
  --data '{"text":"Build finished"}' \
  "https://hooks.slack.com/services/T000/B000/XXXXXXXXXXXXXXXXXXXXXXXX"
Good — read from the environment, never committed
#!/usr/bin/env bash
# .claude/hooks/notify-slack.sh
if [[ -z "${SLACK_WEBHOOK_URL:-}" ]]; then
  echo "SLACK_WEBHOOK_URL not set; skipping notification" >&2
  exit 0
fi
curl -X POST -H 'Content-type: application/json' \
  --data '{"text":"Build finished"}' "${SLACK_WEBHOOK_URL}"

settings.json follows the same rule. For the credential Claude Code itself uses to authenticate, the documented key is apiKeyHelper — "Generate the API credential with your own command" — verified against the settings reference:

.claude/settings.json
{
  "apiKeyHelper": "/opt/bin/get-anthropic-api-key.sh"
}

Claude Code runs the helper through the system shell and sends what it prints as the credential, so nothing sensitive is written to the settings file. It caches the value for five minutes by default (CLAUDE_CODE_API_KEY_HELPER_TTL_MS changes this) and reruns the helper after that, or sooner when a request fails with 401 or 403. A rotated credential therefore takes effect within one cache lifetime. When the helper comes from project or local settings, an interactive session doesn’t run it until you accept the workspace trust prompt.

MCP server configuration follows the same env-var pattern (Configuring MCP Servers covers .mcp.json in full):

.mcp.json
{
  "mcpServers": {
    "internal-search": {
      "type": "http",
      "url": "https://mcp.internal.example.com",
      "headers": {
        "Authorization": "Bearer ${INTERNAL_SEARCH_TOKEN}"
      }
    }
  }
}

The ${VAR} placeholder expands from the environment at connection time, so .mcp.json itself commits safely alongside the rest of a project’s configuration — each teammate sets INTERNAL_SEARCH_TOKEN locally rather than the file carrying a literal value. Claude Code also deliberately reads a short list of credential variables — ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and similar provider/proxy secrets — as empty inside a remote MCP server’s config, specifically so a .mcp.json (which a third party’s plugin or repository could supply) can’t cause your own Claude Code credentials to be sent to a server it names; copy the value to a differently-named variable if a server genuinely needs it passed through. In every case, the rule from Securing AI-Assisted Development applies without exception: no committed example may hardcode a secret.

Least-privilege allowed-tools

A skill’s allowed-tools (Skills's frontmatter reference covers the field itself; see also Claude Code — Pre-approve tools for a skill) grants tool use without an interactive permission prompt for the turn that invokes the skill. A bare, unscoped grant removes the prompt for everything that tool can do, not just what the skill’s steps actually run:

Bad — unscoped Bash grant
---
name: run-release-checklist
description: Run this repository's pre-release checklist end to end. Invoke as `/run-release-checklist`.
allowed-tools: Bash
---

Every step this skill’s body writes runs without a prompt — but so does every other Bash command Claude decides to run for the rest of the turn, whether or not it belongs to the checklist. There is no record, from the frontmatter alone, of what the skill was actually meant to be allowed to do.

Good — each entry names the command it needs
---
name: run-release-checklist
description: Run this repository's pre-release checklist end to end. Invoke as `/run-release-checklist`.
allowed-tools: Read Bash(git status *) Bash(git tag *) Bash(mvn test *) Skill
---

This is the same pattern Skills's iru-code excerpt demonstrates: every Bash entry names a command prefix, never a bare Bash, so the frontmatter itself documents exactly what the skill is trusted to run — and a reviewer can tell, without reading the whole body, whether that trust is appropriate. The same principle scales up to session-level permission rules and their own risks (bypassPermissions mode, a wildcard Bash allow rule, Copilot’s chat.tools.global.autoApprove); Permissions & Settings covers the full rule syntax and those broader-grant settings, verified against Claude Code’s permissions reference.

Reviewing plugin contents before install

Beyond the marketplace-level checks above, a plugin’s manifest and directory are what to actually read before installing, per the same review steps:

  1. Check the marketplace’s source. claude plugin marketplace list prints where each marketplace was added from.

  2. Read the details pane. /plugin, then select the plugin: its Will install section lists commands, agents, skills, hooks, and MCP/LSP servers.

  3. Read the plugin’s source. Open its homepage or repository from the details pane (or the marketplace repository itself), and read hooks/hooks.json, .mcp.json and everything under bin/ — the files the details pane tells you exist but not what they do.

  4. List its full contents locally. Clone the repository and run claude --plugin-dir <plugin-directory> plugin details <plugin-name> for a Component inventory without starting a session.

After installing, claude plugin details <plugin> prints the same inventory for the installed copy under ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/ — useful for confirming what actually landed on disk matches what was reviewed, since an auto-updated plugin’s files can change afterward.

Check Security takes the deterministic side of this same review discipline: it runs detect-secrets over a repository (maintaining a .secrets.baseline so only new or untriaged findings are reported on a full run) to catch a credential that slipped into a commit — the same failure mode a reviewed-but-then-updated plugin, or a hook that writes a secret to a tracked file, can produce.

Managed settings for organisations

An organization that wants a policy enforced on every developer’s machine, rather than relying on each project or user remembering to configure it, deploys a managed-settings.json file. Claude Code applies managed settings above every other level: no user, project, local or --settings value overrides them, with a small set of documented exceptions. Verified against Claude Code’s managed-settings guide, the file goes in a fixed system directory per OS:

OS Path

macOS

/Library/Application Support/ClaudeCode/managed-settings.json

Linux and WSL

/etc/claude-code/managed-settings.json

Windows

C:\Program Files\ClaudeCode\managed-settings.json

This example, taken from the docs' own file-based deployment walkthrough, denies read access to two common secret locations, turns off bypassPermissions mode, and makes Claude Code ignore permission rules from user, project and local files:

managed-settings.json
{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./secrets/**)"
    ],
    "disableBypassPermissionsMode": "disable"
  },
  "allowManagedPermissionRulesOnly": true
}

Because this is a deny rule at the managed tier, no project or user settings file can re-open access to those paths — the same protection Permissions & Settings covers for project-level allow/ask/deny rules applies here, just enforced fleet-wide instead of per repository. After deploying the file, /status inside a session shows a Setting sources line naming Enterprise managed settings (file) when it took effect — the way to confirm the policy actually reached a given machine rather than assuming it did.

Managed settings also cover plugins and marketplaces specifically: an administrator can allowlist or block marketplace sources, force-enable specific plugins, and restrict which hooks are allowed to those shipped by managed settings and force-enabled plugins, via Manage plugins for your organization — the organizational counterpart to the per-developer review checklist above.

The AI Security & Responsible AI sub-section

This page reviews the customization mechanisms themselves. The AI Security & Responsible AI sub-section of this site’s Guides & References / AI section goes further, into threat modelling for agentic systems, red-teaming and evaluation, and responsible-AI governance beyond what any single tool’s settings can enforce.