Skip to content

Credential Hygiene for Agent Skill Authorship

Credentials embedded in skill definitions leak when skills are shared, committed, or reproduced verbatim in agent outputs — a distinct risk from runtime injection that existing secrets management patterns do not cover.

Why Skills Are a Different Surface

Runtime secrets management — env var injection, wrapper scripts, proxy isolation — addresses how credentials enter a running agent session. It does not address credentials baked into the skill files themselves.

Skills are reusable Markdown artifacts encoding API usage and workflow steps. A skill demonstrating an authenticated API call often includes a working example from the author's environment — and that example may contain a live token, API key, or endpoint with embedded credentials.

Three propagation paths expose embedded credentials:

  1. Sharing and publication — Skills are published to community corpora (awesome-copilot, agent registries) or committed to repos. The credential travels with the file.
  2. Version control history — A credential removed in a later commit remains in git history. Shallow mitigation (git filter-repo) is rarely applied to skill directories.
  3. Verbatim LLM reproduction — Agents may echo credential-containing examples into generated code, CI configs, or conversation history. The model treats the skill text as authoritative and reproduces it.

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 prevent the model from reproducing a working credential.

Never use a real credential as an example even temporarily. Pre-commit hooks do not catch credentials that existed only in a draft; git history does.

Scan Skill Files at Pre-commit Time

Extend secret-scanning to cover skill directories. gitleaks, trufflehog, and detect-secrets support custom path patterns:

# .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 contains 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
trufflehog filesystem .claude/skills/ --only-verified
detect-secrets scan .claude/skills/ --all-files

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 safer authoring stage.

Structural Successors: Treat Hygiene as a Holding Pattern

Placeholder syntax and wrapper-script indirection reduce embedded leakage but do not address 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 and argues an untrusted requester causing an authorized operation must never hold reusable authority (Yu, Geng, Knottenbelt 2026). On the runtime side, workload identity federation for agent runtimes 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 the holding pattern: long-term, the credentials skill examples are protecting 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 the interface; no credential is present.

Key Takeaways

  • Skills are authoring artifacts that 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 all skill examples; never use live credentials even as temporary examples
  • 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 authoring-time 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 all vectors:

  • 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, producing credential-containing outputs. Wrapper-script indirection mitigates this; placeholder-only syntax does not.
  • Coverage gaps in CI — Gitleaks path rules for .claude/skills/ only work if CI runs on all branches and PRs. Skills committed before the rule was added remain unscanned.
  • 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.

Apply wrapper-script isolation and pre-commit scanning together; neither alone closes all paths.

Feedback