Credential Hygiene for Agent Skill Authorship¶
Credentials embedded in skill definitions leak when skills are shared, committed, or reproduced verbatim by agents — a risk runtime secrets management does not cover.
Why skills are a different surface¶
Runtime secrets management — env var injection, wrapper scripts, proxy isolation — covers how credentials enter a running session. It does not cover credentials baked into the skill files.
Skills are reusable Markdown artifacts that encode API usage and workflow steps. A skill demonstrating an authenticated call often ships a working example from the author's environment. That example may carry a live token, key, or credential-bearing endpoint.
Three propagation paths expose them:
- Sharing and publication — you publish skills to community corpora (awesome-copilot, agent registries) or commit them to repos. The credential travels with the file.
- Version control history — a credential removed in a later commit remains in git history. Teams rarely apply a fix such as
git filter-repoto skill directories. - Verbatim LLM reproduction — agents may echo credential-containing examples into generated code, CI configs, or conversation history, treating the skill text as authoritative.
Empirical research documents credential leakage in publicly available skills at scale. (Source: arxiv:2604.03070)
Leakage forms¶
| Form | Example | Risk |
|---|---|---|
| Inline token in example invocation | curl -H "Authorization: Bearer ghp_abc123..." |
Committed to repo; published with skill |
| Hardcoded API key in config snippet | api_key: sk-live-xyz... |
Reproduced verbatim by agent in generated files |
| Env var with default value | API_KEY=${API_KEY:-sk-live-xyz} |
Default used when env var is unset in new environments |
| Endpoint with embedded credential | https://user:pass@api.example.com/v1/ |
Logged in request traces and agent outputs |
Mitigations¶
Use placeholder syntax in all examples¶
Replace live credentials with unambiguous placeholders in every skill example:
# In skill file — placeholder, never a live value
curl -H "Authorization: Bearer $MY_SERVICE_API_KEY" \
https://api.example.com/v1/endpoint
Use shell variable syntax ($VAR_NAME) or angle-bracket placeholders (<token>). Both signal that substitution is required and stop the model reproducing a working credential.
Never use a real credential as an example, even temporarily. Pre-commit hooks miss credentials that existed only in a draft; git history does not.
Scan skill files at pre-commit time¶
Extend secret-scanning to cover skill directories. Best practice pairs a fast regex/entropy scanner at the commit and CI edge with a validation scanner over full history on a schedule — they are complementary, not either-or.
For the edge scan, prefer betterleaks — the MIT-licensed, actively-developed successor to gitleaks, written by gitleaks' original author. As of 2026 gitleaks is feature-complete and ships security patches only; feature and detector work moved to betterleaks, which also lifts detection recall substantially. Existing gitleaks config files, ignore files, and CLI flags carry over, so migration is a drop-in. (Source: BleepingComputer, 2026; betterleaks)
Scope scanning to skill paths so the rule travels with the repo (a gitleaks-format config, read by both tools):
# .gitleaks.toml — extend scanning to skill directories
[[rules]]
description = "API key in skill file"
regex = '''(?i)(api[_-]?key|token|secret)\s*[:=]\s*['"]?[A-Za-z0-9_\-]{20,}['"]?'''
paths = [".claude/skills/**", "skills/**", ".github/copilot-skills/**"]
Run the same scanner in CI to catch leaks from contributors who bypass local hooks.
Decouple skill invocation from credential holding¶
Structure skills to invoke wrapper scripts rather than calling authenticated endpoints directly. The skill encodes what to call; the credential stays outside the skill file:
<!-- skill: query-analytics -->
To fetch the latest report, run:
scripts/analytics-fetch.sh <report-id>
The script handles authentication internally. Do not pass credentials as arguments.
The wrapper reads $ANALYTICS_API_KEY from the environment. The skill text holds no credential, so publication does not expose it.
This is the authoring-time complement to Secrets Management for Agent Workflows (runtime injection) and Scoped Credentials via Proxy (runtime isolation).
Audit before publishing¶
Before publishing or sharing a skill, run a credential audit:
# Quick scan before publishing a skill — fast edge scan + verified deep scan
betterleaks dir .claude/skills/ -v
trufflehog filesystem .claude/skills/ --only-verified
Community corpora rely on contributor inspection — registry-level scanning is not universal. The awesome-copilot notice — "inspect any agent and its documentation before installing" — puts this burden on consumers. Scanning before publishing shifts it to the authoring stage.
Structural successors: treat hygiene as a holding pattern¶
Placeholder syntax and wrapper-script indirection reduce embedded leakage but not the deeper problem: any reusable bearer secret inside the model-steerable boundary is exposed by definition. The Secret-Use Delegation Protocol (SUDP) frames this as the 'Agent Secret Use' problem — an untrusted requester causing an authorized operation must never hold reusable authority (Yu, Geng, Knottenbelt 2026). On the runtime side, workload identity federation replaces long-lived API keys with short-lived OIDC tokens minted on demand — removing the bearer secret rather than hiding it.
Apply authoring-time hygiene today, but treat it as a holding pattern: long-term, the credentials skill examples protect should not exist in their current form.
Example¶
A skill that demonstrates Stripe API access before and after applying hygiene:
Before — live credential embedded in skill:
<!-- skill: check-stripe-balance -->
To check the account balance, run:
curl -s -H "Authorization: Bearer sk_live_abc123xyz..." \
https://api.stripe.com/v1/balance | jq '.available[0].amount'
After — placeholder and wrapper script:
<!-- skill: check-stripe-balance -->
To check the account balance, run:
scripts/stripe-balance.sh
The script reads $STRIPE_API_KEY from the environment.
Inject the key before the agent starts — see Secrets Management for Agent Workflows.
The skill now encodes the intent and interface; no credential is present.
Key Takeaways¶
- Skills persist in version control and travel with publication — credentials embedded at authoring time are not bounded by runtime controls
- Use shell-variable placeholders or angle-bracket tokens in every skill example; never use live credentials, even temporarily
- Extend pre-commit secret scanning to skill directories explicitly — scanners do not cover them by default
- Structure skill invocations to call wrapper scripts rather than authenticated endpoints directly
- Audit skill files before publishing to any shared corpus or registry
- Treat hygiene as a holding pattern; SUDP and workload identity federation remove the reusable secret entirely
When this backfires¶
Placeholder syntax and wrapper scripts reduce leakage at authoring time but do not eliminate every vector:
- Private corpora without scanning — teams that never publish externally may skip scanner setup. Leaked credentials remain exploitable if the repo is later open-sourced or an insider extracts the history.
- Agents that resolve placeholders — an agent with both the skill file and environment secrets may substitute real values into placeholder slots such as
$STRIPE_API_KEY, producing credential-containing outputs. Wrapper-script indirection mitigates this; placeholder-only syntax does not. - Coverage gaps in CI — scanner path rules for
.claude/skills/only work if CI runs on all branches and PRs, and an edge scan only covers the working tree. Skills committed before the rule was added remain unscanned unless you also scan full history on a schedule (betterleaks git/trufflehog git). - Registry-level credential reuse — credentials rotated after publication remain exposed in any consumer that cached the older skill version. Pre-commit scanning prevents new leaks but does not revoke already-distributed credentials — only removing the reusable secret via workload identity federation closes that path.
Apply wrapper-script isolation and pre-commit scanning together; neither alone closes all paths.
Related¶
- Agent Skills: Cross-Tool Task Knowledge Standard — the standard format that defines skill structure, discovery paths, and frontmatter
- SUDP: Secret-Use Delegation Protocol — the structural alternative: a three-role protocol so the agent never holds reusable authority
- Workload Identity Federation for Agent Runtimes — runtime alternative: short-lived OIDC tokens that remove long-lived API keys entirely
- Secrets Management for Agent Workflows — runtime injection: keeping credentials out of agent context during execution
- Scoped Credentials via Proxy Outside the Agent Sandbox — runtime isolation: proxy-held credentials that the agent never touches
- Protecting Sensitive Files from Agent Context — permission rules to block agent reads of credential files
- Blast Radius Containment: Least Privilege for AI Agents — limiting the impact when a credential is exposed
- Skill Supply-Chain Poisoning — malicious credentials and payloads embedded in published community skills