/prompt-security-hardening
Use when writing skills, CLAUDE.md files, agent prompts, or any directives that involve shell commands, environment variables, API credentials, file creation, or git operations - prevents secrets leakage into LLM context, unsafe shell patterns, and credential exposure
$ npx -y skills add ed3dai/ed3d-plugins --skill prompt-security-hardening --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/prompt-security-hardening
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when writing skills, CLAUDE.md files, agent prompts, or any directives that involve shell commands, environment variables, API credentials, file creation, or git operations - prevents secrets leakage into LLM context, unsafe shell patterns, and credential exposure
SKILL.md
prompt-security-hardening.SKILL.mdname: prompt-security-hardening
description: Use when writing skills, CLAUDE.md files, agent prompts, or any directives that involve shell commands, environment variables, API credentials, file creation, or git operations - prevents secrets leakage into LLM context, unsafe shell patterns, and credential exposure
user-invocable: false
Prompt Security Hardening
Your context window is sent to an API provider. Every secret that enters your context is a secret leaked to a third party. This skill defines the security boundaries you operate within.
1. Never Read Secret Values Into Context
When you need to verify an environment variable exists, check its existence without reading its value. The value should never appear in your context window, terminal output, or logs.
# SAFE: check existence without reading value
if [ -z "${STRIPE_SECRET_KEY+x}" ]; then
echo "STRIPE_SECRET_KEY is not set"
else
echo "STRIPE_SECRET_KEY is set"
fi
# SAFE: bash 4.2+ (macOS with brew bash, most Linux)
[[ -v STRIPE_SECRET_KEY ]] && echo "set" || echo "not set"
# SAFE: direnv / .envrc-aware check
[[ -v DATABASE_URL ]] && echo "DATABASE_URL is set" || echo "DATABASE_URL is not set"# DANGEROUS: reads the value into context
echo $STRIPE_SECRET_KEY
printenv STRIPE_SECRET_KEY
echo "Key is: ${STRIPE_SECRET_KEY}"
echo "Preview: ${STRIPE_SECRET_KEY:0:8}..." # partial values still leak
echo "Length: ${#STRIPE_SECRET_KEY}" # length leaks entropy info
set | grep STRIPE_SECRET_KEY # shows the value
export | grep STRIPE_SECRET_KEY # shows the value
env | grep STRIPE_SECRET_KEY # shows the value
env | grep -q '^VAR=' # -q is safe for existence check,
# but omitting -q leaks the value**Partial values and lengths are also leaks.** An 8-character prefix of a Stripe key narrows the search space enormously. The length of a secret confirms its format. Reveal nothing.
Grepping shell config files (`~/.zshrc`, `~/.bashrc`, `~/.envrc`) for a variable name will show the full export line including the value. Check for the variable name's presence without showing the line content:
# SAFE: check if the variable is configured in shell config (shows nothing about value)
grep -qc 'ANTHROPIC_API_KEY' ~/.zshrc && echo "found in .zshrc" || echo "not in .zshrc"
# DANGEROUS: shows the full export line, including the secret value
grep 'ANTHROPIC_API_KEY' ~/.zshrc
grep -n 'ANTHROPIC_API_KEY' ~/.zshrc
2. Never Hardcode Secrets in Generated Code or Directives
When writing skills, agents, or CLAUDE.md files that include code examples, use environment variable references. When generating code for users, always reference environment variables or secret managers.
# SAFE
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
# DANGEROUS: reproduces training data patterns
stripe.api_key = "sk_live_..."
stripe.api_key = "sk_test_..." # test keys are still keys
# SAFE: docker-compose referencing env vars
environment:
DATABASE_URL: ${DATABASE_URL}
# DANGEROUS: inline credentials
environment:
DATABASE_URL: postgresql://admin:password123@db:5432/myappPlaceholder values like `changeme`, `your-api-key-here`, `replace-me`, or `postgres://user:password@localhost/db` are not acceptable. They train developers to put real values in the same location, and they appear as false positives in secret scanners, desensitizing teams to alerts. Use empty values (`STRIPE_SECRET_KEY=`) or environment variable references as the primary pattern.
For `.env.example` or template files that get committed:
# SAFE: empty values in committed templates
STRIPE_SECRET_KEY=
DATABASE_URL=
JWT_SECRET=
# DANGEROUS: fake credentials that normalize the pattern
STRIPE_SECRET_KEY=sk_test_your_key_here
DATABASE_URL=postgres://user:password@localhost:5432/myapp
JWT_SECRET=change-this-to-something-secure
3. Set Restrictive File Permissions on Sensitive Files
When creating files that contain or will contain secrets (`.env`, `.envrc`, config files, key files), set restrictive permissions immediately.
# Create with restrictive permissions from the start
touch .env && chmod 600 .env
# Then populate the file
# SSH keys require restrictive permissions to function
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
# Application secret files
chmod 600 /etc/myapp/secrets.conf
Default file creation mode (typically 644) makes files world-readable. SSH will refuse to use a key with open permissions, but `.env` files and config files have no such guardrail.
4. Verify .gitignore Before Creating Secret-Bearing Files
Before creating `.env`, `.envrc`, or any file that will contain secrets, verify the gitignore rules will exclude it. If they won't, add the rule first.
# SAFE: check first, then create
git check-ignore -v .env || echo ".env" >> .gitignore
touch .env && chmod 600 .env
# Also check for .envrc (direnv)
git check-ignore -v .envrc || echo ".envrc" >> .gitignore
This applies to any file that will hold credentials: `.env`, `.envrc`, `secrets.conf`, `credentials.json`, key files, MCP configuration with embedded tokens.
5. Keep Secrets Out of URLs and Process-Visible Arguments
Tokens in URLs get logged in server access logs, proxy logs, and browser history. Tokens in command-line arguments are visible to other users via `ps aux`.
# SAFE: token in header, not URL
curl -H "Authorization: Bearer ${API_TOKEN}" https://api.example.com/data
# DANGEROUS: token in URL query parameter (logged in server logs)
curl "https://api.example.com/data?api_key=${API_TOKEN}"For git operations, avoid embedding tokens in clone URLs:
# DANGEROUS: token in URL, persists in .git/config and shell history
git clone "https://${GITHUB_TOKEN}@github.com/org/repo.git"
# SAFER: use credential helper or environment-basRead more
name: prompt-security-hardening description: Use when writing skills, CLAUDE.md files, agent prompts, or any directives that involve shell commands, environment variables, API credentials, file creation, or git operations - prevents secrets leakage into LLM context, unsafe shell patterns, and credential exposure user-invocable: false
Prompt Security Hardening
Your context window is sent to an API provider. Every secret that enters your context is a secret leaked to a third party. This skill defines the security boundaries you operate within.
1. Never Read Secret Values Into Context
When you need to verify an environment variable exists, check its existence without reading its value. The value should never appear in your context window, terminal output, or logs.
# SAFE: check existence without reading value
if [ -z "${STRIPE_SECRET_KEY+x}" ]; then
echo "STRIPE_SECRET_KEY is not set"
else
echo "STRIPE_SECRET_KEY is set"
fi
# SAFE: bash 4.2+ (macOS with brew bash, most Linux)
[[ -v STRIPE_SECRET_KEY ]] && echo "set" || echo "not set"
# SAFE: direnv / .envrc-aware check
[[ -v DATABASE_URL ]] && echo "DATABASE_URL is set" || echo "DATABASE_URL is not set"# DANGEROUS: reads the value into context
echo $STRIPE_SECRET_KEY
printenv STRIPE_SECRET_KEY
echo "Key is: ${STRIPE_SECRET_KEY}"
echo "Preview: ${STRIPE_SECRET_KEY:0:8}..." # partial values still leak
echo "Length: ${#STRIPE_SECRET_KEY}" # length leaks entropy info
set | grep STRIPE_SECRET_KEY # shows the value
export | grep STRIPE_SECRET_KEY # shows the value
env | grep STRIPE_SECRET_KEY # shows the value
env | grep -q '^VAR=' # -q is safe for existence check,
# but omitting -q leaks the value**Partial values and lengths are also leaks.** An 8-character prefix of a Stripe key narrows the search space enormously. The length of a secret confirms its format. Reveal nothing.
Grepping shell config files (`~/.zshrc`, `~/.bashrc`, `~/.envrc`) for a variable name will show the full export line including the value. Check for the variable name's presence without showing the line content:
# SAFE: check if the variable is configured in shell config (shows nothing about value) grep -qc 'ANTHROPIC_API_KEY' ~/.zshrc && echo "found in .zshrc" || echo "not in .zshrc" # DANGEROUS: shows the full export line, including the secret value grep 'ANTHROPIC_API_KEY' ~/.zshrc grep -n 'ANTHROPIC_API_KEY' ~/.zshrc
2. Never Hardcode Secrets in Generated Code or Directives
When writing skills, agents, or CLAUDE.md files that include code examples, use environment variable references. When generating code for users, always reference environment variables or secret managers.
# SAFE stripe.api_key = os.environ["STRIPE_SECRET_KEY"] # DANGEROUS: reproduces training data patterns stripe.api_key = "sk_live_..." stripe.api_key = "sk_test_..." # test keys are still keys
# SAFE: docker-compose referencing env vars
environment:
DATABASE_URL: ${DATABASE_URL}
# DANGEROUS: inline credentials
environment:
DATABASE_URL: postgresql://admin:password123@db:5432/myappPlaceholder values like `changeme`, `your-api-key-here`, `replace-me`, or `postgres://user:password@localhost/db` are not acceptable. They train developers to put real values in the same location, and they appear as false positives in secret scanners, desensitizing teams to alerts. Use empty values (`STRIPE_SECRET_KEY=`) or environment variable references as the primary pattern.
For `.env.example` or template files that get committed:
# SAFE: empty values in committed templates STRIPE_SECRET_KEY= DATABASE_URL= JWT_SECRET= # DANGEROUS: fake credentials that normalize the pattern STRIPE_SECRET_KEY=sk_test_your_key_here DATABASE_URL=postgres://user:password@localhost:5432/myapp JWT_SECRET=change-this-to-something-secure
3. Set Restrictive File Permissions on Sensitive Files
When creating files that contain or will contain secrets (`.env`, `.envrc`, config files, key files), set restrictive permissions immediately.
# Create with restrictive permissions from the start touch .env && chmod 600 .env # Then populate the file # SSH keys require restrictive permissions to function chmod 600 ~/.ssh/id_ed25519 chmod 644 ~/.ssh/id_ed25519.pub # Application secret files chmod 600 /etc/myapp/secrets.conf
Default file creation mode (typically 644) makes files world-readable. SSH will refuse to use a key with open permissions, but `.env` files and config files have no such guardrail.
4. Verify .gitignore Before Creating Secret-Bearing Files
Before creating `.env`, `.envrc`, or any file that will contain secrets, verify the gitignore rules will exclude it. If they won't, add the rule first.
# SAFE: check first, then create git check-ignore -v .env || echo ".env" >> .gitignore touch .env && chmod 600 .env # Also check for .envrc (direnv) git check-ignore -v .envrc || echo ".envrc" >> .gitignore
This applies to any file that will hold credentials: `.env`, `.envrc`, `secrets.conf`, `credentials.json`, key files, MCP configuration with embedded tokens.
5. Keep Secrets Out of URLs and Process-Visible Arguments
Tokens in URLs get logged in server access logs, proxy logs, and browser history. Tokens in command-line arguments are visible to other users via `ps aux`.
# SAFE: token in header, not URL
curl -H "Authorization: Bearer ${API_TOKEN}" https://api.example.com/data
# DANGEROUS: token in URL query parameter (logged in server logs)
curl "https://api.example.com/data?api_key=${API_TOKEN}"For git operations, avoid embedding tokens in clone URLs:
# DANGEROUS: token in URL, persists in .git/config and shell history
git clone "https://${GITHUB_TOKEN}@github.com/org/repo.git"
# SAFER: use credential helper or environment-basShowing the first part of this file.
This is my collection of plugins that I use on a day-to-day basis for getting stuff done with Claude Code. Most of these are development-oriented in some way or another, but also often end up being useful for other things.
Repo: ed3dai/ed3d-plugins
Other skills on ed3d-plugins.
- /doing-a-simple-two-stage-fanout
Use when analyzing a large corpus of text, code, or data that exceeds a single agent's effective context - orchestrates parallel Worker subagents, Critic review subagents, and a final Summarizer subagent with task tracking and failure recovery
Open skill - /using-generic-agents
Use to decide what kind of generic agent you should use
Open skill - /creating-a-plugin
Use when creating a new Claude Code plugin or setting up plugin structure - provides complete file organization, manifest format, and component definitions for commands, agents, skills, hooks, and MCP servers
Open skill - /creating-an-agent
Use when creating specialized subagents for Claude Code plugins or the Task tool - covers description writing for auto-delegation, tool selection, prompt structure, and testing agents
Open skill - /maintaining-a-marketplace
Use when creating, releasing, or maintaining a Claude Code Plugin Marketplace - covers marketplace.json schema, version management, release checklists, changelog conventions, and validation to prevent sync drift between plugin.json and marketplace.json
Open skill - /maintaining-project-context
Use when completing development phases or branches to identify and update CLAUDE.md or AGENTS.md files that may have become stale - analyzes what changed, determines affected contracts and documentation, and coordinates updates
Open skill

