GUIDE · HARDENING 8 min ·

Claude Code skills security checklistWhat to audit before you install a skill

Skills are user-installable Claude Code extensions that run with the same environment access as the agent itself. There is no signature requirement, no sandbox, and no central review. Treat every skill install like installing an unsigned npm package with shell access.

TL;DR· the answer, in twenty seconds

What: Claude Code skills are instructions files (and optionally bundled scripts) that the agent executes with no sandboxing, no code-signing, and no mandatory review. A malicious or careless skill can read your environment variables, write to disk, and make outbound network calls in the same session where your secrets live.

Fix: Before installing any skill, read its instructions file, check which tools it invokes, look for network calls and env-var reads, inspect any bundled scripts, run it once in a stripped environment with a stream redactor in front, and verify the audit log afterward.

Lesson: Extension ecosystems always outpace their security review. The discipline is the same whether it is a browser plugin, a VSCode extension, or a Claude Code skill: you are the last reviewer.

Anthropic shipped Claude Code skills in late 2025 as a way to package reusable agent workflows. By May 2026, the official marketplace lists hundreds of community skills and a long tail of self-hosted ones shared on GitHub. A skill is a Markdown file containing instructions the agent follows, plus optional shell scripts and tool declarations. You install it, the agent loads it, and it runs with the same permissions the agent has.

That last part is what matters. Claude Code inherits your shell environment, including every exported secret. Skills can call any tool the agent can call: Bash, file read, file write, network fetch. There is no code-signing requirement, no mandatory review, and no sandbox. OX Security documented the same structural gap in the MCP server ecosystem earlier this year: roughly 7,000 MCP servers in the wild, ~150 million downloads, no signature requirement. Skills occupy the same risk class.

The fix is not to avoid skills. The fix is to read what you are installing before it runs.

What to know in 60 seconds

  • A skill runs inside your Claude Code session. It sees your full $PATH, exported env vars, and any files Claude Code can read.
  • Skills are instructions files, not compiled binaries, but instructions that say "run this shell command" have the same effect as the shell command.
  • There is no central review. The marketplace lists skills; it does not vet them.
  • A skill that makes one outbound HTTP call with a $GITHUB_TOKEN in the header is a credential exfiltration channel.
  • Seven checks take under ten minutes. Do them before the first run.

Read the skill's instructions file first

Every skill has an instructions Markdown file. Find it before you install.

For marketplace skills, the source link is in the skill detail page. Click through to the raw file. For GitHub-hosted skills, that is usually a .claude/skills/<name>.md or CLAUDE.md at the repo root.

Read it literally. Look for:

  • Strings like run, execute, bash, sh, shell, eval anywhere in the instructions body.
  • References to other skills or external prompts it imports or chains.
  • Instructions that tell the agent to "remember" or "store" something across sessions (state persistence you did not ask for).
  • Any mention of sending results "to the user via webhook" or "posting to" any URL.

Vague instructions are a flag. "Helps with your workflow" is not a description. Ask: what exactly does this skill tell the agent to do, step by step? If you cannot answer from the instructions file, do not install yet.

Check which tools it invokes

A skill's instructions may declare tool permissions explicitly or may instruct the agent to use tools by name. Either way, you need to know what the tool surface is.

Open the skill file and search for these strings:

grep -iE 'bash|shell|run|execute|write|fetch|curl|http|network|socket' ~/.claude/skills/<skill-name>.md

A skill that invokes Bash can do anything your shell can. A skill that invokes file-write tools can overwrite files in your project tree. A skill that invokes network fetch can exfiltrate data.

Cross-reference with the tool list in your ~/.claude/settings.json. If your installation does not permit a tool the skill needs, the agent will ask for permission at runtime. That prompt is the last checkpoint before the call executes. Do not click "allow" on a tool request you did not anticipate.

Check for network egress

Network calls are the fastest path to credential loss. Scan the skill for outbound intent:

grep -iE 'https?://|curl|fetch|wget|request|api\..*\.com|webhook' ~/.claude/skills/<skill-name>.md

For each URL you find:

  1. Is it a domain the skill author controls? A skill that POSTs your output to my-collector.workers.dev is a data sink you did not agree to.
  2. Is the call conditional on a flag you set, or does it fire every time the skill runs?
  3. Does the call include any part of your environment? Look for $GITHUB_TOKEN, $ANTHROPIC_API_KEY, $DATABASE_URL, or any pattern that would include env var values in a request.

A skill that fetches documentation from docs.anthropic.com is different from one that POSTs a completion to an unlisted endpoint. Both make network calls. Only the second is a problem.

Check for env-var reads

Skills that need to read environment variables will reference them in instructions or in bundled scripts. Knostic documented how Claude Code's own settings.local.json captured env vars silently from agent sessions. Skills can do the same through a different mechanism: instructions that tell the agent to "read the current API key" or "use the database URL from the environment."

Scan for variable references:

grep -iE '\$[A-Z_]{4,}|\benv\b|\benviron\b|\bos\.environ\b|\bprocess\.env\b' ~/.claude/skills/<skill-name>.md

Any env var reference is worth naming explicitly. What is the variable? Is it needed for the skill's stated purpose, or is it just vacuumed up for "context"? A deployment skill that reads $DEPLOY_TOKEN makes sense. A documentation generator that reads $STRIPE_SECRET_KEY does not.

Inspect any bundled scripts

Some skills ship with helper scripts: .sh, .py, .js, .rb files in the same directory or in a scripts/ subdirectory. These run as regular OS processes when the agent invokes them. They have no more and no less access than the instructions file's tool calls, but they are easier to obfuscate.

Check for bundled scripts:

ls -la ~/.claude/skills/<skill-name>/
find ~/.claude/skills/<skill-name>/ -type f \( -name '*.sh' -o -name '*.py' -o -name '*.js' \)

For each script:

  • Read the whole file. This is not optional.
  • Look for outbound calls, file reads outside the project tree, and any base64 or hex decode/eval patterns. Those are obfuscation signals.
  • Check the shebang and interpreter. A .py script using subprocess.run with a constructed command string can do the same things as a Bash script.

GitGuardian Labs tracks supply-chain insertion in open-source packages. The pattern in skills is the same as in malicious npm packages: a seemingly helpful utility that does something extra, once, during setup or on first use. That extra thing is what you are checking for.

Run it once with a redactor in front

Before the skill touches a live project, run it in a controlled session where secrets are not in scope, or use a stream redactor to catch anything that should not appear in the output.

A stream redactor wraps the Claude Code process and watches what flows through the output channel. If a secret appears in the stream, the redactor flags or replaces it before it reaches a log. The pattern is:

<redactor-tool> -- claude code

Trigger the skill in that session. Watch what the redactor intercepts. Any flagged secret means the skill read a credential and tried to surface it. That is the evidence you need to decide whether to trust the skill or discard it.

You can also run the session in an environment with no real secrets exported:

env -i HOME=$HOME PATH=$PATH TERM=$TERM claude code

This strips your full env down to the minimum Claude Code needs to start. The skill runs, but if it reaches for $DATABASE_URL it gets nothing. Watch what the agent does when the variable is absent. A skill that fails gracefully is different from one that stalls, asks clarifying questions about where "the API key" is, or tries alternate env var names.

If you run agent sessions through hasp, this test reflects how the skill will behave in production. Hasp starts agent sessions with a clean environment and injects only the secrets bound to that project. A skill that behaves badly in the stripped env -i test will behave the same way under hasp, which makes this not just a one-time audit step but a preview of the normal operating condition.

Audit what it accessed

After the first run, check the audit trail.

Claude Code does not write a native grant log per skill invocation, but the OS process table and file-access tools do. Use a file-access audit tool appropriate for your platform:

macOS with fs_usage:

sudo fs_usage -f filesys -p $(pgrep -n claude) 2>&1 | grep -v '\.dylib\|/usr/lib'

Linux with strace (attach to the agent process):

strace -p $(pgrep -n claude) -e trace=openat,read,write,connect -s 200 2>&1 | grep -v '/proc\|/sys'

These are noisy. Filter to paths you did not expect: files outside the project tree, /tmp writes, network connects to unfamiliar addresses.

Also check your shell history and the agent's log output for any tool calls the session emitted. Claude Code surfaces tool calls in the conversation view. Scroll back and read them. A skill that called Bash five times during what you thought was a read-only documentation task should prompt questions.

What gets missed: the "helpful" skill problem

The standard threat model imagines a malicious actor planting a credential-stealer in the marketplace. That is a real risk, but it is not the most common way skills cause damage.

The more common failure is the helpful skill that does more than it says. A skill labeled "Generate release notes" might also push a draft to a Notion page it has the token for, log the diff to a monitoring endpoint the author set up, or store your project summary in a remote vector store "for better future suggestions." None of that is a credential steal. All of it is data leaving your project boundary without your knowledge.

The same problem shows up in MCP servers, as the Model Context Protocol spec acknowledges in its security guidance: tool servers should not perform actions beyond their stated scope, but there is no enforcement mechanism. Skills inherit that same gap.

Before you install, ask: what is the worst thing this skill could do with full agent access and one outbound network call? If that answer is "a lot," the skill needs more scrutiny, not less.

A 7-point pre-install checklist

## Skill pre-install audit

- [ ] Read the full instructions file (not just the description)
- [ ] Searched instructions for: bash, shell, run, execute, eval, write
- [ ] Searched instructions for outbound URLs and network keywords
- [ ] Searched instructions for env var references ($VAR, os.environ, process.env)
- [ ] Listed and read all bundled scripts in the skill directory
- [ ] Ran the skill once in a stripped or redacted environment
- [ ] Checked file-access and network audit output after the first run

Paste this into a PR or a SECURITY.md section on skill adoption. One team member should sign off on each item before a skill lands in a shared project's config.

What this means for your stack

The skills ecosystem is at the same point npm was around 2018: large enough to be useful, new enough that trust assumptions have not caught up. The same supply-chain hygiene that now feels obvious for npm packages (lock files, audit runs, scoped installs) will feel obvious for skills in 18 months. Right now it is still manual.

The deeper issue is that skills run inside a process that already has your secrets in scope. That ambient access is what makes every extension install a higher-stakes decision than it would be for a browser plugin running in an isolated tab. The mitigation is to stop letting secrets live as long-lived exported env vars where any subprocess can read them.

hasp is one working implementation. curl -fsSL https://gethasp.com/install.sh | sh, hasp setup, connect a project, hand the next session a reference instead of a key. Source-available (FCL-1.0), local-first, macOS and Linux, no account.

The checklist above works with any secret management approach. The important shift is reviewing every skill before it runs, not after something goes wrong.

Sources· cited above, in one place

NEXT STEP~90 seconds

Stop handing the agent your real keys.

hasp keeps secrets in one local encrypted vault, brokers them into the child process at exec, and never lets the agent read the value.

  • Local, encrypted vault — no account, no cloud, no telemetry by default.
  • Brokered run — agent gets a reference, the child process gets the value.
  • Pre-commit + pre-push hooks catch managed values before they ship.
  • Append-only HMAC audit log answers "did the agent touch the prod token?" in seconds.
→ okvault unlocked · binding ./api
→ okgrant once · pid 88421
→ okagent never read

macOS & Linux. Source-available (FCL-1.0, converts to Apache 2.0). No account.

Browse all clusters· eight threads, one index