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-toolspre-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
hooksare 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>)indenyblocks a skill outright, and deny rules overrideallowed-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
Bashcommand invoking itsbin/) 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 |
What it will install |
In a session, |
What its hooks and MCP config actually run |
Read the plugin’s own files before installing: |
A machine-readable inventory |
Clone the repository holding the plugin and run
|
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
descriptionor 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:
#!/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"
#!/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:
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.
---
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:
-
Check the marketplace’s source.
claude plugin marketplace listprints where each marketplace was added from. -
Read the details pane.
/plugin, then select the plugin: its Will install section lists commands, agents, skills, hooks, and MCP/LSP servers. -
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.jsonand everything underbin/— the files the details pane tells you exist but not what they do. -
List its full contents locally. Clone the repository and run
claude --plugin-dir <plugin-directory> plugin details <plugin-name>for aComponent inventorywithout 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 |
|
Linux and WSL |
|
Windows |
|
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.