AI coding agent plugins, hooks and skills are safe to install only when you treat them as code you are about to run, because that is what they are. A plugin can run shell commands with your user privileges through hooks, background monitors and local MCP servers, and none of that passes through the agent's permission prompts. A skill steers what the agent does, and in some tools it can run commands while it loads.
So the honest answer is: some are safe, none are safe by default, and "I checked it once" stops being true the moment the files change. Install from a source you control, pin to a full commit, route updates through review, read the hooks, server commands and scripts before you enable anything, and run one allowlist for the whole team. This post is the cross tool answer, built from the makers' own documentation for Claude Code and OpenAI Codex.
Key takeaways
- Plugins are executable. Anthropic's plugin security page says a plugin "can execute arbitrary code on your machine with your user privileges." Hooks, monitors, MCP servers and LSP servers run outside the agent sandbox and permission rules.
- Skills are instructions with teeth. A skill's description is in context all the time, its body loads on use, and in Claude Code it can grant itself tools and run shell commands while it renders.
- What you reviewed is not what runs next week. Auto-update, command sources, synced plugins and watched skill folders all change behaviour without a new install.
- A commit pin fixes the plugin's files, not everything it touches. It does not cover the catalog that holds the pin, code a hook downloads at runtime, or a server launched with an unpinned package.
- Govern them like dependencies, plus a prompt review. Pins, an internal registry, code review and fleet policy transfer directly. No vulnerability feed covers a skill's text, so a person still reads it.
Why the question got loud in October 2026
Agent plugin and skill catalogs are now real distribution channels. Claude Code has official, community and third party marketplaces, and OpenAI's Codex documentation describes plugins as the distribution unit for sharing skills, apps and MCP server connections. Anything with an install button and an update channel is a supply chain, and September research that circulated widely in early October went straight at it:
| Research | What it reported | What it means for you |
|---|---|---|
| "A Blind Trust, the Bloody Thrust" (arXiv 2609.03884) | A framework called HookPry turned a benign, versioned plugin into a trojan through an update that bound attacker chosen commands to ordinary hook events. Across 25 harness and backend combinations and 1,000 runs it compromised all seven harnesses tested, with per harness success up to 92.5%. The union of three static defenses still missed 47.5% of malicious artifacts | The dangerous moment is not the install you reviewed. It is the update you did not |
| SkillSecurer (arXiv 2609.14079) | A red and blue agent pipeline for finding and patching prompt injection weaknesses in skills. In popular skills from a public skills directory, the authors found latent vulnerabilities in more than 17% | Popular is not the same as reviewed |
| The "Plugin4Shell" report (17 September 2026) | A way for whoever controls a plugin's repository to make a "pinned" install check out different code, on git hosts that allow branch names shaped like commit hashes. The researchers report coordinated disclosure to four agent vendors in June 2026 and fixes in some, not all | A pin is only as good as the checkout that honours it and the catalog that holds it |
The same week also brought compromised AI developer tooling on public package registries. The tools around your agent are packages, and packages get compromised. None of this means "never install a plugin." It means the install button deserves the same thought as npm install of an unknown package, and a team needs a policy.
What counts as a plugin, a hook and a skill
The words differ by tool, so separate the parts by what they do.
| Part | What it is | Runs code on your machine? | When | Covered by agent permission prompts? |
|---|---|---|---|---|
| Hook | A shell command, HTTP call or prompt bound to a lifecycle event | Yes, command hooks are shell commands | Session start, before and after tool calls, prompt submit and more | No |
| Monitor (Claude Code) | A background shell command | Yes | Session start, plugin reload, or first run of a named skill | No |
| Local MCP server | A process that exposes tools to the agent | Yes | When the agent starts or reloads | The process: no. Each tool call: yes |
| LSP server | A language server | Yes | When the agent starts | No |
bin/ executables (Claude Code) | Files placed on the Bash tool's PATH | When the agent runs them | On a Bash call | Yes |
| Skill | SKILL.md instructions plus optional scripts and references | Not by itself; it tells the agent what to run, and Claude Code skills can inject shell output | Description always; body on use | Tool calls: yes. Claude Code ! injection: checked against rules, never prompts |
| Plugin | A package bundling any of the above with update rules | Whatever its parts run | Install, every session, every update | Partly |
In Claude Code a plugin is a directory with an optional .claude-plugin/plugin.json and default locations for skills/, commands/, agents/, hooks/hooks.json, .mcp.json, .lsp.json, monitors/monitors.json and bin/. In Codex a skill is a directory with a required SKILL.md and optional scripts/, references/, assets/ and agents/openai.yaml, and plugins package skills, apps and MCP connections.
What can a plugin, hook or skill do on install and on every session?
On install
In current Claude Code builds the install step is deliberately narrow:
- Fetching. Sources are a path inside a marketplace, a GitHub repository, any git URL, a repository subdirectory, an npm package, an HTTPS zip, or a
commandsource. For npm sources, the marketplace reference says install scripts "never run" during the fetch. - Dependencies. Claude Code installs a plugin's Node.js dependencies only from registry packages pinned to exact versions in a lockfile, over HTTPS, in a separate folder that ignores the plugin's
.npmrcand.env, with--ignore-scripts, frozen resolution and a 60 second timeout. - Command sources are the exception: Claude Code shows the command for review, runs it, and uses the directory it prints.
- Enabling. Installing also enables the plugin unless it sets
defaultEnabled: false. Install means "start running it next session."
At session start
When a session starts or you run /reload-plugins, SessionStart hooks fire, monitors set to "when": "always" start, stdio MCP servers and LSP servers start as processes, and every enabled plugin's skill descriptions join the context. Nothing asks first. Plugins carried in a repository get a workspace trust check, their MCP servers need per server approval, and their monitors do not load; plugins you installed at user scope skip those checks because you already trusted them.
On every tool call
Claude Code's hooks reference lists events across sessions, turns, the tool loop (PreToolUse, PermissionRequest, PostToolUse and others), subagents, compaction and configuration changes. A command hook receives JSON on standard input with the session id, transcript path, working directory and, for tool events, the tool name and full input, which for a Bash call is the command itself. The process inherits the parent environment.
So a hook can see every command the agent runs, every path it edits and whatever environment variables your terminal holds, and it can make network calls of its own. That is what makes hooks useful for formatters and policy checks, and why a stranger's hook deserves the scrutiny of a postinstall script.
When a skill loads
Both tools use progressive disclosure. Claude Code keeps each description in context and loads the body on use. Codex starts with each skill's name, description and path, loads SKILL.md only when it decides to use it, and by default caps the initial list at 2% of the context window. Two consequences follow:
- The description is always live. A skill nobody calls still places text in front of the model every session, so a description that triggers on almost everything changes behaviour quietly.
- Loading can execute. In Claude Code, a
!`command`line runs before the content reaches the model and pastes in the output. The skills page says these commands "never prompt for permission while the skill renders"; they are checked against your permission rules, and a disallowed one aborts the skill. A skill can also declareallowed-tools, and the docs warn that a project skill's grant applies even in a non interactive run in a folder you never trusted.
Scripts in a skill folder do not run on their own. The instructions ask the agent to run them, which become ordinary tool calls under your permission mode.
What the permission system does not see
Permission rules and the sandbox cover the model's tool calls. Per Anthropic's plugin security page, hooks, monitors, MCP servers, LSP servers and processes a mod starts all run outside the sandbox with your full user permissions. "The agent asks before it runs anything" is true for the agent and false for its plugins.
How do auto-updates change the risk of something you vetted once?
A review approves specific bytes. Updates replace them, and a modern agent has several ways to do that without you choosing it.
| Channel | When it changes | Default | What stops it |
|---|---|---|---|
| Marketplace auto-update (Claude Code) | After your first message, a random delay of up to ten minutes, then auto-update marketplaces refresh and their plugins update on disk | On for Anthropic's official marketplaces and those added from claude.ai, off for all others | autoUpdate: false per marketplace, or DISABLE_AUTOUPDATER=1 |
command sources | Re-run once per session, plus every install and update | Always, when enabled | disableCommandPluginSources; DISABLE_AUTOUPDATER does not stop it |
| Plugins synced from claude.ai | Background sync each start | On when signed in with a claude.ai account | syncClaudeAiPlugins: false, or "<name>@synced": false |
| Skill folders (Claude Code) | Edits to SKILL.md apply within the current session | Always | Code review; strictPluginOnlyCustomization |
| Skill folders (Codex) | New skills are detected automatically | Always | Code review; [[skills.config]] with enabled = false |
| Runtime downloads in a plugin | Whenever a hook or server fetches code | Depends on the plugin | Only review of its commands |
The publisher decides when an update happens. Claude Code computes a version from the plugin manifest's version, then the marketplace entry's version, and only if both are absent, the commit SHA or archive digest. A pinned "version": "1.0.0" keeps users on the cached copy however many commits land, until the publisher changes the string; with no version, every commit is an update.
Running sessions keep the old copy. An update writes a new version directory and prompts you to reload. The previous directory is marked orphaned and removed 14 days later, which is handy for forensics.
The HookPry paper's threat model is exactly this: the attacker controls only metadata and hook configuration, the installed version was benign, and a later update binds new commands to events the user already accepted. For any marketplace you do not run, set autoUpdate to false and update after reading the diff. For your own internal marketplace, auto-update can stay on, because every change already passed review.
Does pinning to a commit protect you, and how has it been bypassed?
In a Claude Code marketplace entry, github, url and git-subdir sources accept a ref (branch or tag) and a sha (a full 40 character commit); with both, Claude Code checks out the sha. Anthropic's community catalog pins nearly every entry and Claude Code refuses a different commit for those. An archive source can carry a sha256, and a mismatched download is refused. Against a repository pushing new code to a branch or moving a tag, a full SHA pin works. It leaves real gaps:
| Gap | Why the pin does not help | What does help |
|---|---|---|
| The catalog holds the pin | The sha lives in someone's marketplace.json; if that marketplace auto-updates, a new catalog commit can carry a new sha | Run an internal marketplace and change pins only by pull request |
ref without sha | Branches and tags move | Always set sha |
| Runtime downloads | A hook that fetches a script, or a server started with npx -y some-server, pulls new code every run | Require exact versions in server commands; review fetches |
| Remote servers and bundle URLs | A URL server runs someone else's code elsewhere; a .mcpb bundle can be an HTTPS URL | Treat remote servers as vendors; vendor and pin bundles |
command sources | The output changes while the entry does not | disableCommandPluginSources |
| Version string pins | "version" holds only until the publisher changes it | Pin by sha in a catalog you control |
How the Plugin4Shell report bypassed a pin
The report targets the checkout itself. As the researchers describe it, agents asked to install a pinned commit ran a git checkout without confirming the result was that commit. Because git can resolve a 40 character hex string as a branch name, a repository owner on a host that permits hash shaped branch names could create a branch named exactly like the pinned hash, make it the default, and have the "pinned" install land on it. The report says GitHub rejects such names; some other and self hosted servers may not.
The researchers call it zero click when combined with background auto-update after a marketplace re-pins a plugin. One point is disputed: the report describes background auto-update as the Claude Code default, while Anthropic's documentation says it is on by default only for Anthropic's official marketplaces and those added from claude.ai. Either way, three defenses follow: keep the agent current and check its release notes, verify every checkout in your own mirrors, and reject hash shaped refs on your git host.
# Flag branches or tags whose names look like full commit hashes
git ls-remote --heads --tags https://git.example.com/acme/agent-formatter.git \
| awk '{print $2}' | grep -E 'refs/(heads|tags)/[0-9a-f]{40}$' \
&& echo "Suspicious hash shaped ref found" || echo "No hash shaped refs"
# After checking out a pinned commit in a mirror, confirm it is exactly that commit
test "$(git -C ./agent-formatter rev-parse HEAD)" = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0" \
&& echo "Pinned commit verified" || echo "HEAD does not match the pin"
A pinned entry in a marketplace you control looks like this. defaultEnabled: false keeps a new plugin from switching on everywhere at once:
{
"name": "acme-agent-plugins",
"owner": { "name": "Platform Security" },
"plugins": [
{
"name": "formatter",
"description": "Runs the house formatter after edits",
"source": {
"source": "github",
"repo": "acme/agent-formatter",
"ref": "v2.0.0",
"sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
},
"defaultEnabled": false
}
]
}
What should you review in a skill or plugin before installing it?
Anthropic's review steps are a good base: check the marketplace's source, read the details pane, read the plugin's source, and list its components. The pane shows that a hook exists, not what it runs, so the real review is in the files.
claude plugin marketplace list # where each marketplace comes from
git clone https://github.com/example-org/example-plugin /tmp/review/example-plugin
cd /tmp/review/example-plugin && git log -1 --format='%H %an %ad' # record the reviewed commit
claude --plugin-dir /tmp/review/example-plugin plugin details example-plugin
claude plugin validate /tmp/review/example-plugin --strict
cat hooks/hooks.json .mcp.json .lsp.json monitors/monitors.json 2>/dev/null
ls -la bin/ scripts/ 2>/dev/null
# Patterns that need a reason
grep -rnE 'curl |wget |Invoke-WebRequest|https?://' .
grep -rnE 'npx (-y|--yes)|uvx |pipx run|bunx |@latest' .
grep -rnE 'base64 (-d|--decode)|eval |child_process|subprocess' .
grep -rnP '^\s*!\x60|^\x60{3}!' --include='SKILL.md' .
grep -rn 'allowed-tools' --include='SKILL.md' .
grep -rnP '[\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2066}-\x{2069}\x{E0000}-\x{E007F}]' .
A hit is not a verdict. Formatters legitimately run npx prettier. Every hit just needs a reason.
| File | What to check | Red flag |
|---|---|---|
hooks/hooks.json | Events, matcher, exact command | Session start or catch all tool hooks that call the network or read ~/.ssh, cloud credentials or the transcript |
.mcp.json | Command, arguments, environment, URLs | Unpinned package runner; a remote URL with no owner you can name |
monitors/monitors.json | Command and when | An always on background command with network access |
bin/ | Every file | Binaries without source; names that shadow git or npm |
SKILL.md frontmatter | description, allowed-tools, disable-model-invocation | A description that triggers on everything; unrestricted Bash grants |
SKILL.md body and scripts/ | What the agent is told to run | "Skip confirmation", "read the .env file", "send results to", fetch and follow remote text |
agents/openai.yaml (Codex) | dependencies.tools, policy.allow_implicit_invocation | MCP dependencies on servers you do not run |
Local MCP servers: what the protocol asks for
The Model Context Protocol's security best practices treat a local server as a program downloaded and executed beside the client. They list malicious startup commands and payloads inside the server as attack paths, say a client offering one click setup must show the exact command without truncation and require explicit approval, and recommend sandboxing with minimal default privileges. A plugin's .mcp.json is that startup command, delivered in a bundle. Read every argument and environment variable, and if the server needs neither network nor your home directory, run it where it cannot reach them.
The prompt injection review
No advisory feed covers "this paragraph tells the model to ignore the user." Read the skill as the model would. Does it ask the agent to hide actions, skip confirmations or rank its instructions above the user's? Does it fetch a URL and follow what it finds, handing control to whoever owns that URL later? Does it read untrusted input such as issues or web pages and then act with broad tools? Would it still work with fewer tools? For skills that should run only on request, set disable-model-invocation: true in Claude Code, or policy.allow_implicit_invocation: false in a Codex skill's agents/openai.yaml; explicit $skill invocation still works.
How do you run an allowlist for plugins and skills across a team?
A team needs one approved source, a policy every machine enforces, and review on every change to that source.
Step 1: create an internal marketplace
A repository with .claude-plugin/marketplace.json listing approved plugins, each pinned by sha or stored in the repository:
claude plugin validate ./acme-agent-plugins --strict
claude plugin marketplace add ./acme-agent-plugins
claude plugin install formatter@acme-agent-plugins
Step 2: enforce it with managed settings
Managed settings come from the claude.ai admin console, MDM, or a managed-settings.json file, and users cannot override them. This policy allows only Anthropic's official marketplace and your organization's repositories, blocks plugin folders under skill directories, locks auto-update for the official marketplace, force enables one plugin, and closes the side doors:
{
"strictKnownMarketplaces": [
{ "source": "github", "repo": "anthropics/claude-plugins-official" },
{ "source": "github", "repo": "acme/*" }
],
"blockedMarketplaces": [
{ "source": "skills-dir" }
],
"extraKnownMarketplaces": {
"claude-plugins-official": {
"source": { "source": "github", "repo": "anthropics/claude-plugins-official" },
"autoUpdate": false
},
"acme-agent-plugins": {
"source": { "source": "github", "repo": "acme/acme-agent-plugins" },
"autoUpdate": true
}
},
"enabledPlugins": {
"formatter@acme-agent-plugins": true
},
"disableSideloadFlags": true,
"disableCommandPluginSources": true,
"allowManagedHooksOnly": true,
"disableSkillShellExecution": true,
"syncClaudeAiPlugins": false,
"pluginTrustMessage": "Only install plugins from acme-agent-plugins. Ask #agent-tooling for reviews."
}
The details that trip people up, from Anthropic's organization page:
strictKnownMarketplacesis checked before download and again at session start, but it does not restrict entries inside an allowed marketplace and does not block--plugin-dir.disableSideloadFlagsdoes.- The allowlist registers nothing;
extraKnownMarketplacesdoes, and a managedautoUpdatevalue cannot be toggled by users. allowManagedHooksOnlyblocks user, project, local and plugin hooks, except hooks from plugins force enabled in managedenabledPlugins. Force enable approved plugins that rely on hooks.blockedMarketplaceswithskills-dirstops plugin folders in skill directories, but plainSKILL.mdfolders still load.strictPluginOnlyCustomizationgoes further.syncClaudeAiPlugins: falsestops plugins synced from claude.ai accounts; the organization's claude.ai plugin settings are a separate control.
Confirm with /status (look for Enterprise managed settings), then try adding an unlisted marketplace; it should be blocked by enterprise policy.
Step 3: review changes like code
Every new plugin, sha bump and edit goes through a pull request, with CODEOWNERS on plugins/**/hooks/, plugins/**/.mcp.json and plugins/**/skills/. A CI job catches the mechanical problems:
# .github/workflows/marketplace-check.yml
name: marketplace-check
on:
pull_request:
paths: [".claude-plugin/**", "plugins/**"]
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm install -g @anthropic-ai/claude-code
- name: Validate marketplace and plugins strictly
run: claude plugin validate . --strict
- name: Require a full commit pin on every git source
run: |
bad=$(jq -r '.plugins[]
| select(.source|type=="object")
| select(.source.source=="github" or .source.source=="url" or .source.source=="git-subdir")
| select((.source.sha // "") | test("^[0-9a-f]{40}$") | not)
| .name' .claude-plugin/marketplace.json)
if [ -n "$bad" ]; then echo "Unpinned plugins: $bad"; exit 1; fi
- name: Refuse command sources
run: |
jq -e '[.plugins[] | select(.source|type=="object") | select(.source.source=="command")]
| length == 0' .claude-plugin/marketplace.json
- name: Flag runtime fetches of unpinned code
run: |
! grep -rnE 'npx (-y|--yes)|@latest|curl [^|]*\| *(ba)?sh' plugins/
Step 4: the Codex side
Codex reads skills from the repository's .agents/skills folders up to the repository root, from $HOME/.agents/skills, from /etc/codex/skills for admins, and from skills bundled by OpenAI. Ship shared skills through the admin location with your configuration management, keep repository skills under code review, and switch one off without deleting it:
# ~/.codex/config.toml
[[skills.config]]
path = "/Users/dev/.agents/skills/web-scraper/SKILL.md"
enabled = false
Restart Codex afterwards. Codex follows symlinked skill folders to their target, so a symlink into a shared directory inherits whatever lives there.
Step 5: write down what "approved" means
Every executable component has an owner who can explain it. No runtime downloads without an exact version. No hook sends data off the machine unless that is its documented purpose. Skills that touch untrusted input get the narrowest tool grants. Each approval records the reviewer, the date and the commit.
How do you detect a plugin that changed behaviour after an update?
Detection works best when changes are rare and deliberate, which is another reason to turn off auto-update for third party marketplaces.
Claude Code keeps state under ~/.claude/plugins/: installed_plugins.json records each install's scope, path and version, known_marketplaces.json records each marketplace's source, last update and auto-update flag, and cache/<marketplace>/<plugin>/<version>/ holds the files. Hash it into a baseline and diff:
#!/usr/bin/env bash
# plugin-baseline.sh: hash cached plugin files and compare with the previous run
set -uo pipefail
ROOT="${CLAUDE_CODE_PLUGIN_CACHE_DIR:-$HOME/.claude/plugins}"
OUT="$HOME/.agent-baseline"; mkdir -p "$OUT"
NOW="$OUT/plugins-$(date +%Y%m%d%H%M%S).txt"
find "$ROOT/cache" -path '*/node_modules' -prune -o -type f -print 2>/dev/null \
| sort | xargs shasum -a 256 > "$NOW"
PREV=$(ls -1 "$OUT"/plugins-*.txt | tail -n 2 | head -n 1)
if [ "$PREV" != "$NOW" ] && ! diff "$PREV" "$NOW" > "$OUT/last-diff.txt"; then
echo "Plugin contents changed, see $OUT/last-diff.txt"
else
echo "No change since last baseline"
fi
When something changed, diff the two version directories (the old one stays for 14 days), starting with the parts that execute:
cd ~/.claude/plugins/cache/some-marketplace/some-plugin/
diff -ru OLDVERSION NEWVERSION | less
A changed hook command is the highest signal change a plugin update can contain. Beyond files:
- Save
claude plugin list --jsondaily and diff it; runclaude plugin details <name>to see a new hook or server in the component inventory. - Open
/hooks, a read only browser that labels each hook's source and shows its full command. - For a fleet, export the
claude_code.plugin_installedandclaude_code.plugin_loadedOpenTelemetry events, withOTEL_LOG_TOOL_DETAILS=1so third party names are not redacted. - For Codex, keep
~/.agents/skillsin git and rungit diffafter any install. - Since hooks and servers run outside the sandbox, watch behaviour with operating system tools: new egress destinations from processes under the agent, new long running processes, edits to shell profiles.
Should plugins and skills go through the same governance as packages?
Yes, with two additions.
| Control | Packages | Agent plugins | Agent skills |
|---|---|---|---|
| Approved source | Internal registry or proxy | Internal marketplace plus allowlist | Admin folder, reviewed repository folder |
| Exact pin | Lockfile with integrity hashes | sha or sha256 | The folder's commit |
| Install time code | Lifecycle scripts, often allowed | Claude Code: scripts disabled; command sources run | None by itself |
| Run time code | When your app imports it | Every session and tool call, via hooks, monitors, servers | Via the agent's tool calls and Claude Code ! injection |
| Runs with | Your app's privileges | Your user privileges, outside the sandbox | The agent's permissions |
| Vulnerability feeds | Mature | Cover the plugin's dependencies, not the plugin | None for instructions |
| Update channel | A bot opens a pull request | Auto-update, sync, command re-runs | Folder edits |
Plugins run in a more privileged place than most packages. A library runs inside your app; a hook runs in your terminal at every session, next to your credentials. That argues for a stricter bar. Skills need a prompt review no scanner replaces. A plugin's package.json can go through ordinary dependency scanning; a skill's text cannot be matched against an advisory.
For the governance case on its own, including skill bills of materials, see AI agent skills governance: the new supply chain risk. For vetting one downloaded server, see is it safe to install an MCP server from the internet?, and for proof of concept skills carrying harmful payloads, see ToxicSkills: when Claude skills become a malware distribution channel.
Is a plugin from an official marketplace automatically safe?
No. Claude Code sorts marketplaces into official, community and third party tiers and accepts official and community names only from repositories under github.com/anthropics/, and it rejects look alike names. That stops impersonation of the catalog. It does not vouch for each plugin: the trust warning shown before every install says Anthropic does not control what plugins include and cannot verify that they will behave as intended or stay unchanged. The tier tells you who curates the list. Your review tells you what the plugin does.
Where Safeguard fits
Safeguard does not read a skill and judge whether its instructions are hostile, and it does not claim to detect malicious plugins. That review stays with a person. Here is where it does help:
- Plugins and MCP servers are built from ordinary dependencies. Run
safeguard scan --diron your internal marketplace repository or a plugin under review; results land as JSON under./output, with vulnerability matching and reachability analysis. - Find every place a component lives. The platform shows which projects contain a given component and version, including scanned marketplace and plugin repositories.
- Policies and gates can block, warn or require approval when a marketplace change introduces a vulnerable component, and autonomous remediation can open the upgrade pull request. It ships switched off, with every category set to Manual.
- An assistant that asks first. The Safeguard CLI edits code and runs commands with your approval;
/permissionsoffers Ask, Accept edits, Plan, Auto (the default) and Strict. Ask or Strict is a sensible mode while evaluating unfamiliar tooling. - Scoped MCP access. Connecting Claude to Safeguard's MCP server uses OAuth and lets you choose which tools Claude may use. An MCP Guardrail that generates proxy URLs for MCP servers is in early access.
Fix it without waiting for upstream
For agent tooling, the part of this that applies is the code underneath: the npm and PyPI packages your plugins, hooks and MCP servers are built from. When one of those carries a known vulnerability, you do not have to wait for the plugin's maintainer to bump it.
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
Common mistakes
- Reviewing once, then leaving auto-update on for a marketplace you do not control.
- Pinning
refand calling it pinned. Only a full 40 charactershais a pin. - Trusting a pin in someone else's catalog that updates itself.
- Reading the details pane instead of
hooks/hooks.json. - Assuming permission prompts cover plugins. Hooks, monitors and servers run outside them.
- Giving broad
allowed-toolsto a skill that reads untrusted text, the classic indirect prompt injection setup. - Launching MCP servers with unpinned package runners.
- Allowlisting marketplaces without
disableSideloadFlags. - Keeping no record of the reviewed commit, so drift and approval look the same.
FAQ
Are Claude Code plugins safe to install?
As safe as the code inside them. Anthropic's documentation says a plugin can run arbitrary code with your user privileges and that hooks, monitors and MCP servers run outside the sandbox. Install from a marketplace you trust, read hooks and server commands, pin by commit, and keep auto-update off for marketplaces you do not control.
Are Claude skills safe?
The main risk is what a skill tells the agent to do. Read the description, body and allowed-tools, look for ! injection lines, set disable-model-invocation: true for on request skills, and set disableSkillShellExecution in managed settings if you do not need injected commands.
Are Codex skills safe to install?
The same rules apply. Review SKILL.md and its scripts, keep shared skills in version control, set policy.allow_implicit_invocation: false for skills that should run only when asked, and disable unwanted ones with [[skills.config]].
Do agent permission prompts protect me from a malicious plugin?
Partly. They govern the model's tool calls, including a plugin's MCP tools and bin/ executables. They do not govern hooks, monitors, MCP server processes or LSP servers.
Does pinning a plugin to a commit SHA make it safe?
It fixes the plugin's files. It does not fix the catalog holding the pin, runtime downloads, remote servers or command sources, and the Plugin4Shell report shows the checkout must also honour the pin. Pin by sha in a marketplace you control and keep your agent current.
How do I turn off plugin auto-update in Claude Code?
Set autoUpdate: false on the marketplace in extraKnownMarketplaces or use the toggle in /plugin under Marketplaces. DISABLE_AUTOUPDATER=1 stops the whole pass; add FORCE_AUTOUPDATE_PLUGINS=1 to keep plugin updates while Claude Code itself stops updating. command sources still re-run unless blocked.
How can a company allowlist agent plugins?
Run an internal marketplace with pinned entries and deliver managed settings with strictKnownMarketplaces, extraKnownMarketplaces, enabledPlugins and disableSideloadFlags, plus allowManagedHooksOnly and disableSkillShellExecution for tighter control. Review every marketplace change by pull request.
How do I know if a plugin changed after an update?
Diff the old and new version folders under ~/.claude/plugins/cache/, keep a hash baseline, save claude plugin list --json over time, check /hooks, and export the plugin_loaded OpenTelemetry event across the fleet.
See how Safeguard would help
This real Safeguard CLI session shows the assistant reading a finding, asking before each command, editing package.json, running install and tests with approval, and pushing nothing. The same approval flow is what you want while trying unfamiliar agent tooling, and the closing /permissions view shows the modes.
Click through it at your own pace, or open it full screen.
This demo connects Claude to the Safeguard MCP server with OAuth and chooses which tools Claude can use. It is a small, concrete version of this post's least privilege rule: an agent gets the tools you approve and nothing else.
Click through it at your own pace, or open it full screen.
This one sets up the local runner on your own machine, approves it with a device code, and scans from the terminal. Point the same scan at your internal marketplace repository or a plugin under review to see the open source components it brings with it.
Click through it at your own pace, or open it full screen.
To try it on your own plugins, download Safeguard and follow the CLI docs.
Quick check
Your team pinned a third party plugin to a full commit SHA in the vendor's marketplace, and that marketplace has auto-update on. Is the plugin frozen?
No. The pin lives in the vendor's catalog. When the marketplace refreshes, a new catalog commit can carry a new SHA and your machines follow it. List the plugin in a marketplace you control, pin the SHA there, and change it only by pull request.
You run the agent in its strictest permission mode. Can a plugin's SessionStart hook still make a network call?
Yes. Permission modes and the sandbox govern the model's tool calls. Command hooks, monitors and MCP server processes run on their own with your user permissions, so the hook command itself is what you review or block, for example with allowManagedHooksOnly.
Sources
- Claude Code docs: Plugin security and trust
- Claude Code docs: Plugin loading reference
- Claude Code docs: Manage Claude Code plugins for your organization
- Claude Code docs: Marketplace reference
- Claude Code docs: Plugin manifest reference
- Claude Code docs: Create a marketplace
- Claude Code docs: Skills
- Claude Code docs: Hooks reference
- Claude Code changelog
- OpenAI Codex docs: Agent Skills
- Model Context Protocol specification: Security best practices
- arXiv 2609.03884: A Blind Trust, the Bloody Thrust: When Attacker-Controlled Hook Updates Steer AI Agent Harnesses towards Malicious Behaviors
- arXiv 2609.14079: SkillSecurer: Detecting and Patching Prompt-Injection Vulnerabilities in AI Agent Skills
Last reviewed: 22 October 2026.