Safeguard
AI Security

Auditing AI agent skill registries for hardcoded keys

29M new hardcoded secrets hit public GitHub in 2025, up 34% YoY — and 3% of MCP servers in production carry hardcoded credentials as theft traps.

Safeguard Research Team
Research
7 min read

GitGuardian's State of Secrets Sprawl 2026 logged 29 million newly exposed hardcoded secrets on public GitHub in 2025 — a 34% year-over-year jump, the largest single-year increase the firm has recorded, with an 81% surge specifically in AI-service credential leaks. That spike lines up with a distribution channel almost nobody was auditing two years ago: public registries of Model Context Protocol (MCP) servers and AI agent "skills," installed by developers who want their coding assistant to touch a database, a ticketing system, or a cloud account in one command. Clutch Security's December 2025 research found that roughly 38% of MCP servers running in production come from unofficial sources, and that 3% of sampled servers contained hardcoded credentials acting as outright credential-theft traps for the developers who connect to them. Separately, Trend Micro found about 48% of MCP servers recommend insecure secret storage — plaintext JSON or .env values, or credentials baked directly into source — often because the server never implemented environment-variable expansion in the first place. This post lays out a concrete methodology for auditing these registries: what to scan, how to verify a hit is live without touching production systems, and why the audit tooling itself has to be treated as untrusted code.

Why do MCP servers and agent skills leak secrets more than typical npm packages?

MCP servers and agent skills leak secrets more than typical packages because their entire purpose is holding credentials for someone else's service, and the ecosystem's tooling hasn't caught up to that responsibility. A typical npm utility package never touches a Stripe key; an MCP server that wraps the Stripe API is designed to. Trend Micro's research found that close to half of MCP servers it reviewed lacked support for reading secrets from environment variables or a secrets manager at all, so the fastest path for a server author writing example config — or a rushed real deployment — is to paste the live key into a config file that ends up committed. Stacklok's State of MCP report adds scale to the problem: in a typical 10,000-person organization, an estimated 15.28% of employees run at least one MCP server, averaging two each, almost always without a security review gate. That is a large population of self-service installs, each one a candidate for a hardcoded credential nobody vetted.

What made the Shai-Hulud campaign relevant to skill and plugin registries specifically?

The 2025 Shai-Hulud npm supply-chain campaign — across its original wave and the later "Shai-Hulud 2.0" resurgence — compromised well over 1,000 packages combined and systematically harvested AWS, Azure, GCP, GitHub, and npm credentials from the machines that installed them — and it matters here because MCP servers and agent skills are distributed through the exact same npm and PyPI registries as any other package. There is no separate, more-trusted channel; npm install for an MCP server runs the same postinstall-script risk as npm install for a logging library. Truffle Security's earlier documentation of the compromised @ctrl/tinycolor package (2.2 million weekly downloads) showed the escalation path clearly: the malicious update didn't just steal secrets, it ran TruffleHog against the victim's own environment to find more of them. Any audit methodology for skill registries has to assume a downloaded package can behave the same way — which is why analysis should run in an isolated, network-restricted sandbox, never on a developer's live workstation with cloud credentials already loaded.

How do you actually verify a hit is a live secret and not a placeholder?

You verify a hit by calling the issuing service with the credential itself, using the lowest-privilege read-only endpoint that service exposes, rather than trusting a regex match. A pattern like AKIA[0-9A-Z]{16} or a Stripe sk_live_ prefix tells you a string has the right shape for a credential; it does not tell you the key is still active. The verification step calls the issuer directly — an AWS key against sts:GetCallerIdentity, a GitHub token against /user, a Stripe key against /v1/balance, a Slack token against auth.test — and treats a successful response as proof the secret is live right now, versus an expired or already-rotated one. This distinction is what separates a useful audit from noise: Truffle Security's Forager project re-scanned 3,240 npm packages with previously known leaked secrets and found 85% of secrets leaked in packages published between 2020 and 2023 were still live on re-scan, and only about 22% of secrets leaked in 2023 had ever been revoked, compared with 3–18% in earlier years. Disclosure alone does not fix the problem — verification tells you which findings still need to.

What does a registry-wide audit pipeline need to check beyond the obvious API key patterns?

A registry-wide audit needs to check generic high-entropy strings and PII patterns alongside issuer-specific keys, because skill manifests and example configs routinely embed test data that turns out to be real. Beyond named-issuer regexes (AWS, GitHub, Stripe, Slack, OpenAI, Anthropic, and the 200-plus patterns most secrets scanners maintain), a thorough pass should flag generic JWTs, private keys (RSA/EC/Ed25519), OAuth refresh tokens, and database connection strings that carry embedded passwords — categories GitGuardian and other scanners classify separately from issuer-specific keys because they require structural rather than pattern matching. PII leakage is the second axis: skill READMEs and test fixtures for "customer support" or "CRM" agent skills frequently ship with real customer records used as demo data — names, emails, and account IDs copy-pasted from a production export rather than synthesized. Both categories need the same triage discipline: an allowlist for genuine placeholders (sk_live_EXAMPLE...), and a documented severity split between verified-live findings and unverified pattern matches, so a registry audit doesn't drown reviewers in false positives from documentation examples.

What should maintainers and downstream users actually do with a finding?

Maintainers and downstream users need different responses to the same finding, and conflating them is where audits stall. A maintainer who finds a hardcoded key in their own skill's source needs to revoke it at the issuer, purge it from Git history (not just the latest commit, since git log -p still exposes it), and rotate before republishing — the Forager data above shows how often this last step gets skipped. A developer who is about to install a third-party skill needs a pre-install check, not a post-mortem: scanning the package tarball for hardcoded secrets and known-malicious postinstall behavior before it runs on a machine with real cloud credentials, mirroring the isolation lesson from Shai-Hulud and @ctrl/tinycolor. Registry operators sit in between and can raise the floor for everyone by running automated secrets and PII scans on every published version and surfacing verified findings publicly, the way security advisories work for known-CVE packages today — a step most MCP and skill registries had not yet adopted as of this writing.

How Safeguard helps

Safeguard's secrets scanning applies this same verify-don't-guess methodology across source code, Git history, container layers, packaged artifacts, and model weights — the last of these matters directly for AI agent skills, since fine-tuned models and cached configs can bake in tokens that never appear in a readable source file. Detection runs across issuer-specific patterns for 200+ providers, generic patterns (private keys, JWTs, connection strings), and entropy-based structural analysis, with every issuer-specific hit verified live against a low-privilege endpoint — sts:GetCallerIdentity for AWS, /user for GitHub, /v1/balance for Stripe — so a security team triaging a skill registry sees verified, exploitable findings separated from unverified pattern noise. When a finding is confirmed live, Safeguard's remediation playbook can revoke via the issuer's API, open a rotation PR, and optionally purge the secret from Git history, closing the gap that let 85% of Forager's sampled leaks stay live years after disclosure.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.