Skill Supply-Chain Security
Skill supply-chain security treats every third-party Agent Skill as code that runs with the agent’s permissions. The routine: install only from the canonical owner, scan SKILL.md and bundled scripts with Snyk Agent Scan, read the hooks and tool grants a scanner cannot judge, and install at project scope, where code review sees every change.
You search skills.sh for “superpowers” because a colleague swears by it. The top results by install count are not Jesse Vincent’s Superpowers at all: they are copycat repositories whose skills generate AI videos and automate Twitter. One npx skills add later, text written by a stranger sits in your agent’s instructions, and any hook it ships runs at session start without a permission prompt.
This page is for developers who install skills and tech leads who own the team’s agent setup. If you have never installed a skill, read installing, sharing and pinning skills first.
What you get from vetting skills this way
Section titled “What you get from vetting skills this way”- A five-step routine (confirm the owner, clone, scan, read, then install and prove) for any skill, ending in proof that the installed copy is byte-for-byte the reviewed one.
- Two copy-paste prompts: a read-only review of an unknown skill and an audit of everything already installed.
- A managed-settings policy for Claude Code, the matching controls in Codex and Cursor, and a triage table for scanner findings.
What can a malicious skill do in your agent?
Section titled “What can a malicious skill do in your agent?”A skill is more than markdown. The instructions in SKILL.md enter the conversation with the same weight as your own prompt, and several fields and files run code. The table lists the surfaces in Claude Code (checked against the Claude Code docs and CLI 2.1.283 on the latest channel) because its docs spell them out. Codex and Cursor also run hooks, covered after the table.
| Surface | What it does | Asks you first? |
|---|---|---|
SKILL.md body | Instructions the agent follows: “before you scaffold auth, fetch this helper and run it” | No, it is text |
!`command` lines in SKILL.md | Claude Code runs the command before the skill text reaches the model and pastes in the output | No |
allowed-tools frontmatter | Pre-approves the listed tools for the turn that invokes the skill; workspace trust does not gate it, even in a -p run | No, that is its purpose |
hooks frontmatter | Registers hooks when the skill is invoked; they keep running for the rest of the session | No |
Plugin hooks (hooks/hooks.json) | Command hooks run “with your full user permissions”, for example at SessionStart | No |
| Bundled scripts | Run when the instructions tell the agent to run them | Yes, through normal permissions |
The last column is the point: permission prompts protect you from the agent’s tool calls, not from the code that a skill or plugin runs on its own. The Superpowers plugin is a benign example. Its hooks/hooks.json runs run-hook.cmd session-start on every startup, and that is how its method fires automatically. A hostile plugin uses the same mechanism.
Codex requires persisted trust before it runs hooks, and --dangerously-bypass-hook-trust skips that check (Codex CLI 0.157.1 --help). Cursor supports skills, hooks and plugins (Cursor docs, checked 2026-08-28); its hook trust rules could not be re-checked on 2026-09-26.
Where do fake and tampered skills come from?
Section titled “Where do fake and tampered skills come from?”Each pattern starts with installing the wrong copy (September 2026 ecosystem research):
| Pattern | Evidence | How to catch it |
|---|---|---|
| Copycat repositories that borrow a famous name | 101-skills/superpowers, qu-skills/superpowers, magentosh/superpowers and skills-shell/superpowers each showed 120K–480K installs in a skills.sh snapshot read on 2026-09-26 (secondary: skills.sh counts are not first-party installs; recheck before relying on it), and their skills are ai-video-generation, twitter-automation and ai-avatar-video | Install from the owner the project’s own README names. Only obra/superpowers is Superpowers |
| Re-uploads of a large pack | ECC’s README: “Third-party re-uploads and unofficial mirrors are not maintained or reviewed by the project and may contain malware.” Its official channels are affaan-m/ECC, the npm packages ecc-universal and ecc-agentshield, and the plugin ecc@ecc | Match the source against the project’s list of official channels |
| Forks re-published under another owner | Cyberpradeep/addyosmani-agent-skills, dajiaohuang/agent-skills-addyosmani | Check the owner, not the repository name |
| Invented owners | anthropics/superpowers does not exist (checked 2026-09-26); Superpowers is only distributed through Anthropic’s marketplace | Open the URL before you install |
| Stale rankings | Renamed skills such as mattpocock/skills@to-prd still ranked in the skills.sh top 300 on 2026-09-26 although their folders return 404 | A high install count is not evidence the skill exists or is maintained |
For scale: the genuine Superpowers plugin had 1,009,371 installs on claude.com/plugins and its repository had 291.7k GitHub stars, both read on 2026-09-26. Numbers that large make the name worth copying.
Vet and install one skill: clone, scan, read, install
Section titled “Vet and install one skill: clone, scan, read, install”The routine below installs systematic-debugging from Superpowers. Replace the repository and skill name for anything else. The skills CLI commands were run with version 1.7.0, and the scanner commands were checked against PyPI snyk-agent-scan 0.6.8 (released 2026-09-29).
-
Confirm the owner and list what the repository contains. Take the repository from the project’s own README or its marketplace listing, never from a search result. Then list the skills without installing anything:
Terminal window npx skills add obra/superpowers --list -
Clone a review copy and record the commit. You scan and read this copy, so it must be the exact content you later install.
Terminal window git clone --depth 1 https://github.com/obra/superpowers.git /tmp/skill-review/superpowersgit -C /tmp/skill-review/superpowers rev-parse HEAD -
Scan it with Snyk Agent Scan. The scanner needs a Snyk API token in
SNYK_TOKEN; load it from your secret store rather than typing it on the command line. It accepts a singleSKILL.mdor a directory of skills:Terminal window uvx snyk-agent-scan@latest /tmp/skill-review/superpowers/skills/systematic-debugging/SKILL.mduvx snyk-agent-scan@latest /tmp/skill-review/superpowers/skillsSnyk Agent Scan (formerly mcp-scan, about 3.1k GitHub stars in the September 2026 ecosystem research) reports prompt injection, malware payloads, untrusted content, credential handling and hardcoded secrets. It sends skill content, secrets redacted, to Snyk’s analysis API, so confirm that your organization allows that. If it does not, NVIDIA’s open-source SkillSpector runs locally with
skillspector scan <dir> --no-llm(with--no-llm, file contents stay local; only declared dependency names and versions go to OSV.dev for the CVE check). -
Read what a scanner cannot judge. List every line that runs code or grants tools, then read those files in full:
Terminal window cd /tmp/skill-review/superpowersgrep -rn -e '^allowed-tools:' -e '^hooks:' -e '!`' skills/systematic-debugging/find . -name 'hooks.json' -not -path './node_modules/*'ls skills/systematic-debugging/A conventions or debugging skill should describe how to work. Stop if it tells the agent to fetch a URL, pipe a script to a shell, write outside the project, or send data anywhere. For a large skill, let an agent do the first pass with the review prompt below, in a session that cannot act on what it reads.
-
Install at project scope and prove the installed copy matches the reviewed one. Run this from the root of your repository:
Terminal window npx skills add obra/superpowers --skill systematic-debugging -a claude-code -a codex -a cursor --copy -ydiff -r /tmp/skill-review/superpowers/skills/systematic-debugging .claude/skills/systematic-debugging && echo "installed = reviewed"--copywrites real files instead of symlinks, so the skill text lands in your diff. The CLI writes.claude/skills/for Claude Code and.agents/skills/for Codex and Cursor, plus askills-lock.jsonentry with the source and a content hash. Commit all three in one pull request.
Run that prompt in a session that cannot act on what it reads. The review clone is not your project, so load none of its configuration:
cd /tmp/skill-review/superpowersclaude --safe-mode --permission-mode manual--safe-mode starts with CLAUDE.md, skills, plugins, hooks and MCP servers disabled while managed policy still applies. The flag arrived in v2.1.169, so both release channels have it: latest (2.1.283) and stable (2.1.274) on 2026-09-26. Manual mode runs reads without asking and prompts for everything else, so decline any command the session proposes. Avoid plan mode here: it can run classifier-approved commands when auto mode is available.
cd /tmp/skill-review/superpowerscodex --sandbox read-only --ask-for-approval on-requestThe read-only sandbox is the enforced boundary. An untrusted project no longer supplies its AGENTS.md (Codex 0.150.0 and later), so decline the trust prompt for the review folder.
Open the review folder in a separate window, paste the prompt into Agent, and reject every terminal command it proposes. Cursor’s run-mode settings could not be verified on 2026-09-26, so rely on that review, not on a setting.
Skill or plugin: which install form should you vet?
Section titled “Skill or plugin: which install form should you vet?”Many projects ship both a portable skill and a plugin, and they are not the same artifact. npx skills add obra/superpowers copies skills only. The plugin adds the SessionStart hook that makes the method fire automatically. That hook is also the part that runs without a prompt, so a plugin needs its hooks read in step 4.
claude plugin install superpowers@claude-plugins-officialclaude plugin details superpowers@claude-plugins-officialclaude plugin details prints the plugin’s component inventory, including hooks, and its projected token cost: about 838 always-on tokens for Superpowers, while ECC loads about 41,515 (measured 2026-09-26). The scanner itself is a CLI and adds nothing to the context window.
codex plugin add superpowers@openai-curatedThe verb is add: there is no codex plugin install (codex-cli 0.157.1). In the TUI, open /plugins and install Superpowers from the openai-curated marketplace instead.
Per the Superpowers README, run /add-plugin superpowers in Agent chat (Cursor’s marketplace could not be checked on 2026-09-26).
Why install skills at project scope?
Section titled “Why install skills at project scope?”A project-scope skill lives in the repository, so every change to it arrives as a pull request diff that a reviewer and CI can see. A global install (-g) lands in ~/.claude/skills/ or ~/.codex/skills/, applies to every repository on that machine, and never passes review.
Project scope has one sharp edge in Claude Code: a project skill’s allowed-tools and frontmatter hooks apply whenever it is invoked, including in a claude -p run in a folder you never trusted. Before you script the agent over a repository you did not write, read its .claude/ directory, or run it with --bare --setting-sources "" --strict-mcp-config --disable-slash-commands. --bare alone still resolves skills, and --disable-slash-commands turns them off (claude --help, 2.1.283 on the latest channel).
Enforce a team skill allowlist with managed settings
Section titled “Enforce a team skill allowlist with managed settings”A routine that each developer runs voluntarily lasts until the first deadline. For a team, make the reviewed set the only set that loads. Pick the level that fits your risk:
| Level | Who it fits | Where approved skills live | What blocks everything else |
|---|---|---|---|
| 1. Reviewed in the repository | Most product teams | .claude/skills/, .agents/skills/, skills-lock.json | CODEOWNERS approval plus managed limits on hooks and inline shell |
| 2. Plugin-only supply chain | Regulated or high-value code | An internal plugin marketplace | strictPluginOnlyCustomization stops user and project skills from loading |
-
Require an owner’s approval for skill changes. Add the paths to
CODEOWNERSand turn on required code-owner review in branch protection:/.claude/skills/ @acme/agent-platform/.agents/skills/ @acme/agent-platform/skills-lock.json @acme/agent-platform/.claude/settings.json @acme/agent-platform/.skillspector-baseline.yaml @acme/agent-platform/.github/workflows/ @acme/agent-platform -
Write the managed policy. For Claude Code, deliver it as server-managed settings (Team and Enterprise), an MDM profile, or
managed-settings.json(/etc/claude-code/on Linux and WSL,/Library/Application Support/ClaudeCode/on macOS,C:\Program Files\ClaudeCode\on Windows). Users cannot override these keys. This is the level 1 policy:{"strictKnownMarketplaces": [{ "source": "github", "repo": "anthropics/claude-plugins-official" },{ "source": "github", "repo": "acme/agent-plugins" }],"allowManagedHooksOnly": true,"disableSkillShellExecution": true,"enabledPlugins": {"superpowers@claude-plugins-official": true}}strictKnownMarketplaceslimits which marketplaces users can add and install from.allowManagedHooksOnlyruns only managed hooks and hooks from plugins that managedenabledPluginsforce-enables; user, project and other plugin hooks are blocked. That is why Superpowers is listed: without the entry itsSessionStarthook stops running.disableSkillShellExecutionreplaces every!`command`in user, project, plugin and additional-directory skills with[shell command execution disabled by policy].
For level 2, add
"strictPluginOnlyCustomization": ["skills", "hooks"]. Claude Code then stops loading skills from~/.claude/skills/,.claude/skills/,--add-dirdirectories and claude.ai sync, and keeps plugin, bundled and managed skills. Package approved skills as plugins in your team marketplace. Under level 2, the project-scope install in step 5 of the routine no longer loads in Claude Code, which is the intent. -
Apply the equivalent controls in Codex and Cursor.
Admin constraints go in
requirements.toml, notconfig.toml. Setallow_managed_hooks_only = truethere to ignore user, project and session hook configs. The same file can also constrainpluginsandmarketplaces(Codexconfig_requirements.rs, checked 2026-09-26); check that schema for the exact shape before you deploy it. Project skills live in.agents/skills/, which step 1 already puts under code-owner review.Everything in step 2. To quarantine one skill across the fleet during an incident, add a deny rule to managed settings, in the parameter form:
"permissions": { "deny": ["Skill(skill:suspicious-skill)"] }. It matches every name the skill has, including its alias and display name; the plainSkill(suspicious-skill)form does not.Cursor reads project skills from
.agents/skills/(skills CLI 1.7.0 agent table), so theCODEOWNERSrule covers them. Cursor’s own admin controls for skills and hooks could not be verified on 2026-09-26; check Cursor’s team settings documentation before you rely on one. -
Scan every change in CI. The code owner then reviews findings, not raw skill text. SkillSpector needs no token and starts nothing; with
--no-llmonly declared dependency names and versions leave the runner, for the OSV.dev CVE check. By default it exits 0 for SAFE and CAUTION results, so the loop passes--fail-on-findingsand collects every scan’s status instead of only the last one:Terminal window set -euo pipefailshopt -s nullglobuv tool install git+https://github.com/NVIDIA/skillspector.gitmkdir -p sariffail=0for d in .claude/skills/*/ .agents/skills/*/; doskillspector scan "$d" --no-llm --fail-on-findings \--baseline .skillspector-baseline.yaml \--format sarif --output "sarif/$(echo "$d" | tr / _).sarif" || fail=1doneexit $failAny active finding that the baseline does not cover fails the job, and with it the pull request. The baseline file and the workflow are in the
CODEOWNERSblock from step 1 because a pull request can edit both: onpull_request, GitHub runs the workflow from the pull request’s merge ref. Upload thesarif/files as a job artifact or to code scanning.--baselineneeds the file to exist, so leave the flag out until you commit the first accepted finding.Snyk Agent Scan’s
--ciflag, which exits non-zero on findings, requires--dangerously-run-mcp-serversin 0.6.8. That flag starts every stdio MCP server in the scanned configs, so use it only on a disposable runner that holds no secrets beyondSNYK_TOKEN, and only on the default branch or a schedule, never on a pull request: there the configs, and so the commands it starts, are the contributor’s, and they would run next toSNYK_TOKEN. The full CI pattern is in security gates for agent-written code.
How do you know the controls are working?
Section titled “How do you know the controls are working?”Four checks give you evidence without re-reading every skill:
- The installed copy is the reviewed copy. The
diff -rin step 5 of the routine is empty, andskills-lock.jsonrecords a content hash for each skill. Treat a laternpx skills updateas a new install, with the same routine in its own pull request. - Only the allowlist loads. In a fresh Claude Code session,
/skillslists every skill with its source. Under level 2, a skill you drop into~/.claude/skills/does not appear./hooksshows which hooks are active, and/statusnames the managed source in use. - Every change had an owner. Branch protection shows the code-owner approval on each pull request that touched a skills path, with the scan report attached.
- The fleet matches the policy. A scheduled
uvx snyk-agent-scan@latestacross developer machines inventories every skill and MCP server, and Dr. Skill shows conflicts across the whole loadout.
Sign-off sits with the code owners for skill changes and with whoever owns managed policy for the settings file.
Scanner noise versus real risk
Section titled “Scanner noise versus real risk”A scanner flags patterns; your job is to decide intent. Third-party scanners rate some of the most-installed skills “High Risk”: a mondoo.com search-result title (secondary) rates obra’s systematic-debugging that way, plausibly because of shipped test fixtures (obra/superpowers issue #2277). Triage by what the finding means in context:
| Finding | Likely real when | Likely noise when | Action |
|---|---|---|---|
| Prompt injection | The text addresses the agent and overrides your rules | The skill is about prompt injection and quotes examples | Read the lines; reject if the instruction is live |
| Suspicious download or network call | A workflow skill fetches and runs code | A documented install step for a known tool | Pin the tool yourself instead of letting the skill fetch it |
| Malicious code or dangerous execution | It sits in a script the skill tells the agent to run | It sits in a test fixture the skill never invokes | Confirm with grep which files the SKILL.md references |
| Hardcoded secret | Any real-looking key | An obvious placeholder in an example | Reject until the author removes it |
When you accept a finding as noise, record why in the pull request and baseline narrowly: one SkillSpector baseline rule that sets the finding’s id and path and the required reason (see .skillspector-baseline.example.yaml in its repository). Running skillspector baseline <dir> on its own accepts every current finding, real ones included. Snyk Agent Scan’s --ignore-risks <name> drops a whole risk category from --ci evaluation, so avoid it for one-off noise. Never turn the scan off.
When skill security controls fail
Section titled “When skill security controls fail”You installed a copycat. Remove it with npx skills remove <name> (-g for user scope), then check git status and git diff for changes made while it was loaded. Check memory files such as CLAUDE.md and AGENTS.md for text you did not write. Rotate any credential the agent could read in those sessions.
The agent followed a hidden instruction. Before you investigate, quarantine the skill fleet-wide with a managed Skill(skill:<name>) deny rule so no other session loads it. Then find the instruction with the review prompt and report the repository to the marketplace that listed it.
A hook ran code even though every scan was clean. Hooks run outside the permission prompt. List them with /hooks or claude plugin details <plugin>, disable the plugin with claude plugin disable <plugin>, and move the team to allowManagedHooksOnly so only vetted plugins run hooks.
CI flags a skill the team depends on. Read the finding with the triage table. If it is noise, add a baseline rule for that one finding (id, path, reason) in the same pull request; never regenerate the whole baseline or reach for --ignore-risks. If it is real, restore the last clean copy from git history, which --copy made possible: git checkout <last-clean-commit> -- .claude/skills/<name> .agents/skills/<name> skills-lock.json.
Managed settings do not seem to apply. By default Claude Code uses only the highest-ranked managed source that delivers a policy key (server-managed, then MDM, then managed-settings.json) and ignores the rest without a warning. Open /status: it names the source in use and the sources it skipped. Put the whole policy in the source that wins, or set managedSourcesBehavior to "merge" (v2.1.242 or later).