Back to skill

Security audit

EnvRelay: Backup, Restore & Migrate Dev Environments

Security checks for vulnerabilities and agentic risk

Overview

This local environment backup skill handles sensitive developer files, but its high-impact behavior is disclosed, purpose-aligned, and gated by user review/confirmation.

Before installing, use the dry run, review the exact credential directories and agent sessions/extensions that would be carried, and remember that staging/restore folders are plaintext until you delete them. Prefer the bundled or pinned installer path over curl-to-shell, and review restored agent hooks, MCP servers, skills, and plugins because they can run code in future agent sessions.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (105)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

There is a substantial description/behavior mismatch. The declared purpose presents a broad migration/backup skill with envrelay installation and support for many environment components beyond AI agents. The actual code only inventories AI coding agent state on the current machine and emits JSON, with optional manifest diffing. While AI agent state is one subset mentioned in the description, the code neither backs up nor restores anything, does not encrypt data, does not process .envrelay files, and does not install envrelay. Its resource access is limited to local agent-related files and invoking agent CLIs for --version, which is narrower than declared, but the primary purpose is materially different from the claimed full backup/restore workflow.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description describes a broad environment-backup/migration skill with consent-based installation of envrelay and handling of many asset types beyond Git. The supplied code chunk does something much narrower: it scans for Git repositories and reports their status facts as JSON. While this could support a larger backup workflow, the code itself neither backs up nor restores anything, does not migrate environments, does not manage credentials or dotfiles, and does not install any binary. Therefore the code's actual behavior is only a small supporting component and does not accurately represent the declared end-to-end purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a high-level skill for environment backup, restore, migration, selection of what to include/exclude, manifesting, and installation of an encryption tool. The supplied code does not implement that primary purpose. Instead, it is a low-level staging copy helper that safely copies one source into a destination tree and reports results. While such a copier could support a backup workflow, this chunk by itself lacks the core declared behaviors and even states in its docstring that deciding what should travel is the skill's job, not this script's. Therefore the description materially overstates and misrepresents what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This code chunk has a much narrower and materially different purpose than the declared description. It inventories installed software from package managers (brew, apt, dnf, pacman, npm, pnpm, cargo, pipx, etc.), optionally enumerates GUI apps, lists unmanaged binaries from common bin directories, and can diff the discovered inventory against a manifest. Those functions may support a larger migration workflow, but by themselves they do not implement the declared core capabilities: collecting and backing up environment contents, handling secrets/credentials, packaging or encrypting backups, restoring/replaying onto another machine, or installing the envrelay binary. The code even states it never installs or uninstalls anything. Therefore the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The script is an installer that invokes shell commands extensively and downloads release artifacts, yet those capabilities are not covered by declared permissions. In a coding-agent environment, hidden shell/network capability materially increases risk because the skill can alter the host machine and install binaries without a complete permission model.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The script is an installer that invokes shell commands extensively and downloads release artifacts, yet those capabilities are not covered by declared permissions. In a coding-agent environment, hidden shell/network capability materially increases risk because the skill can alter the host machine and install binaries without a complete permission model.

Content

No source excerpt is available for this finding.

External Script Fetching

High
Category
Supply Chain
Confidence
98% confidence
Finding

The installer explicitly promotes the pattern 'curl ... | sh', which executes code fetched from the network immediately. Even though the script later verifies release artifacts, this bootstrap step bypasses any integrity check on the installer itself and creates a high-risk remote code execution path if the site, redirect, TLS trust chain, or distribution channel is compromised.

Content

Scanner excerpt · install.sh (reported line 5)May include surrounding context.

sh
# Install EnvRelay: the envrelay binary and the EnvRelay skill, in one command.
#
# Every release carries this file as install.sh, and the one-line command,
#   curl -fsSL https://envrelay.com/install.sh | sh
# runs the latest release's copy, which the site redirects to. The same file
# ships inside the skill, so an agent that has the skill but not the binary
# runs `sh <skill dir>/install.sh --bin-only` once the user says yes.

Chaining Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

The documented '| sh' chaining pattern directly connects network retrieval to shell execution, eliminating an opportunity for review and making compromise of the distribution path immediately exploitable. In an agent-assisted installation context, this can lead to one-step remote code execution on developer machines.

Content

Scanner excerpt · install.sh (reported line 5)May include surrounding context.

sh
# Install EnvRelay: the envrelay binary and the EnvRelay skill, in one command.
#
# Every release carries this file as install.sh, and the one-line command,
#   curl -fsSL https://envrelay.com/install.sh | sh
# runs the latest release's copy, which the site redirects to. The same file
# ships inside the skill, so an agent that has the skill but not the binary
# runs `sh <skill dir>/install.sh --bin-only` once the user says yes.

External Script Fetching

High
Category
Supply Chain
Confidence
98% confidence
Finding

This second occurrence repeats the unsafe one-line installation pattern that pipes remote content directly into the shell. In the context of an agent skill that may guide automated setup, this is especially dangerous because it normalizes immediate execution of mutable network content on the user's workstation.

Content

Scanner excerpt · install.sh (reported line 46)May include surrounding context.

sh
Every release carries this file as install.sh; envrelay.com redirects to the
latest release's copy. Through the one-line command, options go after sh -s --:

  curl -fsSL https://envrelay.com/install.sh | sh -s -- --dry-run

Options:
  --version X.Y.Z    install that release instead of the latest one

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · install.sh (reported line 217)May include surrounding context.

sh
if [ "$dry_run" = 1 ]; then
		say "    would remove $1/envrelay"
	else
		rm -f "$1/envrelay"
		say "    removed $1/envrelay"
	fi
}

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · install.sh (reported line 455)May include surrounding context.

sh
if [ "$dry_run" = 1 ]; then
			say "    would remove $bin_dir/envrelay"
		else
			rm -f "$bin_dir/envrelay"
			say "    removed $bin_dir/envrelay"
		fi
	else

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · references/ai-agents.md (reported line 80)May include surrounding context.

md
| Path | Plane | Note |
|---|---|---|
| `~/.claude/settings.json`, `settings.local.json` | config | permissions, hooks, model, statusline |
| `~/.claude/CLAUDE.md` | config | the user's global instructions; usually the single most valuable file here |
| `~/.claude.json` | config **and** credential | one JSON holding `mcpServers` (with their `env`), `oauthAccount`, and a per-project history blob for every directory ever opened. Carry the parts that matter; do not carry it whole without looking at what it grew into |
| `~/.claude/skills/`, `agents/`, `commands/`, `hooks/` | extensions | hand-written. Carry as files |

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · references/ai-agents.md (reported line 273)May include surrounding context.

md
| Path | Plane | Note |
|---|---|---|
| `~/.claude/settings.json`, `settings.local.json` | config | permissions, hooks, model, statusline |
| `~/.claude/CLAUDE.md` | config | the user's global instructions; usually the single most valuable file here |
| `~/.claude.json` | config **and** credential | one JSON holding `mcpServers` (with their `env`), `oauthAccount`, and a per-project history blob for every directory ever opened. Carry the parts that matter; do not carry it whole without looking at what it grew into |
| `~/.claude/skills/`, `agents/`, `commands/`, `hooks/` | extensions | hand-written. Carry as files |

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · scripts/agent_inventory.py (reported line 95)May include surrounding context.

python
| Path | Plane | Note |
|---|---|---|
| `~/.claude/settings.json`, `settings.local.json` | config | permissions, hooks, model, statusline |
| `~/.claude/CLAUDE.md` | config | the user's global instructions; usually the single most valuable file here |
| `~/.claude.json` | config **and** credential | one JSON holding `mcpServers` (with their `env`), `oauthAccount`, and a per-project history blob for every directory ever opened. Carry the parts that matter; do not carry it whole without looking at what it grew into |
| `~/.claude/skills/`, `agents/`, `commands/`, `hooks/` | extensions | hand-written. Carry as files |

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · references/ai-agents.md (reported line 111)May include surrounding context.

md
| Path | Plane | Note |
|---|---|---|
| `~/.gemini/settings.json`, `GEMINI.md` | config | |
| `~/.gemini/config/mcp_config.json` | config | MCP servers |
| `~/.gemini/config/skills/`, `~/.gemini/extensions/`, `commands/` | extensions | `gemini extensions list` / `gemini skills list` name what is installed |
| `~/.gemini/oauth_creds.json`, `google_accounts.json` | credential | `gemini` re-authenticates in a browser; carrying these is rarely worth it |

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · scripts/agent_inventory.py (reported line 169)May include surrounding context.

python
| Path | Plane | Note |
|---|---|---|
| `~/.gemini/settings.json`, `GEMINI.md` | config | |
| `~/.gemini/config/mcp_config.json` | config | MCP servers |
| `~/.gemini/config/skills/`, `~/.gemini/extensions/`, `commands/` | extensions | `gemini extensions list` / `gemini skills list` name what is installed |
| `~/.gemini/oauth_creds.json`, `google_accounts.json` | credential | `gemini` re-authenticates in a browser; carrying these is rarely worth it |

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · references/ai-agents.md (reported line 139)May include surrounding context.

md
| **Copilot in VS Code** | `~/Library/Application Support/Code/User/settings.json`, repo `.github/copilot-instructions.md` | `code --list-extensions` | — | GitHub login |
| **opencode** | `~/.config/opencode/opencode.json` | `~/.config/opencode/{agent,command,plugin}/` | `~/.local/share/opencode/storage/` | `~/.local/share/opencode/auth.json` |
| **Windsurf** | `~/.codeium/windsurf/mcp_config.json` | `~/Library/Application Support/Windsurf/User/` | same | Codeium login |
| **Continue** | `~/.continue/config.yaml` (or `config.json`) | `~/.continue/{assistants,rules}/` | `~/.continue/sessions/`, `index/` | inside the config |
| **Aider** | `~/.aider.conf.yml`, repo `.aider.conf.yml` | — | repo `.aider.chat.history.md`, `.aider.tags.cache.v*/` | `~/.aider/` / env vars |
| **Cline / Roo Code** | VS Code `globalStorage/<publisher>.<ext>/settings/` | — | same `globalStorage` tree | inside VS Code secret storage |
| **Zed** | `~/.config/zed/settings.json` | `~/.config/zed/extensions/` | `~/Library/Application Support/Zed/`, `~/.local/share/zed/` | Zed login |

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 96)May include surrounding context.

md
`file` a credential file — not to check a key's type, not to count keys,
  not to confirm a file is what its name says.
- **Do not name files in conversation.** `~/.ssh` is a path.
  `~/.ssh/id_ed25519_acme_prod` names a key and an employer. Directory-level
  discussion is enough for every decision the user has to make.
  `test -d ~/.aws` is a fine existence check; `ls ~/.aws` is not necessary.
- **Ask before the backup, not after.** Step 2 of the backup is where the user

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/credential-locations.md (reported line 15)May include surrounding context.

md
`file` a credential file — not to check a key's type, not to count keys,
  not to confirm a file is what its name says.
- **Do not name files in conversation.** `~/.ssh` is a path.
  `~/.ssh/id_ed25519_acme_prod` names a key and an employer. Directory-level
  discussion is enough for every decision the user has to make.
  `test -d ~/.aws` is a fine existence check; `ls ~/.aws` is not necessary.
- **Ask before the backup, not after.** Step 2 of the backup is where the user

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/credential-locations.md (reported line 45)May include surrounding context.

md
| `~/.config/gcloud` | gcloud config and credential DB | Yes, refresh tokens | Re-authenticating is usually quicker than debugging a stale token cache |
| `~/.azure` | Azure CLI profile and token cache | Yes | Token caches often need refreshing anyway |
| `~/.kube` | Cluster configs and contexts | Often — embedded tokens or client certs | Carry it, and expect some tokens to have expired |
| `~/.docker/config.json` | Registry auth | Yes | Or `docker login` again. Do not carry the rest of `~/.docker` |
| `~/.netrc` | Machine/password pairs, plaintext | Yes | Say "plaintext by design" out loud when proposing it |
| `~/.npmrc` | npm registry auth token | Yes | Small file, always worth carrying |
| `~/.pypirc` | PyPI upload credentials | Yes | Same |

Credential Access

High
Category
Privilege Escalation
Confidence
80% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/credential-locations.md (reported line 46)May include surrounding context.

md
| `~/.azure` | Azure CLI profile and token cache | Yes | Token caches often need refreshing anyway |
| `~/.kube` | Cluster configs and contexts | Often — embedded tokens or client certs | Carry it, and expect some tokens to have expired |
| `~/.docker/config.json` | Registry auth | Yes | Or `docker login` again. Do not carry the rest of `~/.docker` |
| `~/.netrc` | Machine/password pairs, plaintext | Yes | Say "plaintext by design" out loud when proposing it |
| `~/.npmrc` | npm registry auth token | Yes | Small file, always worth carrying |
| `~/.pypirc` | PyPI upload credentials | Yes | Same |
| `~/.cargo/credentials.toml` | crates.io token | Yes | Carry the file; leave the rest of `~/.cargo` behind |

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/credential-locations.md (reported line 47)May include surrounding context.

md
| `~/.kube` | Cluster configs and contexts | Often — embedded tokens or client certs | Carry it, and expect some tokens to have expired |
| `~/.docker/config.json` | Registry auth | Yes | Or `docker login` again. Do not carry the rest of `~/.docker` |
| `~/.netrc` | Machine/password pairs, plaintext | Yes | Say "plaintext by design" out loud when proposing it |
| `~/.npmrc` | npm registry auth token | Yes | Small file, always worth carrying |
| `~/.pypirc` | PyPI upload credentials | Yes | Same |
| `~/.cargo/credentials.toml` | crates.io token | Yes | Carry the file; leave the rest of `~/.cargo` behind |
| `~/.m2/settings.xml` | Maven server passwords | Sometimes | Carry the file, not `~/.m2/repository` |

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/ai-agents.md (reported line 91)May include surrounding context.

md
| `~/.config/*/credentials`, `~/.config/*/config.json`, `~/.config/*/auth.json` | Assorted CLI tools — Fly, Railway, Supabase, Doppler, Vercel, Stripe, Cloudflare | Usually | Enumerate with `ls -d ~/.config/*/` and ask about the ones you recognise |
| `~/.config/op`, `~/.1password` | Password manager CLI state | Device-bound | Usually needs re-authorising on the new machine regardless |
| `~/.terraform.d/credentials.tfrc.json` | Terraform Cloud token | Yes | |
| `~/.claude` | Claude Code settings, skills — and, on Linux, `.credentials.json` | Yes, the token file | Settings and skills go in `files/`; the token file follows the rules on this page. On macOS the OAuth token is in the login Keychain instead and cannot travel |
| `~/.codex/auth.json`, `~/.gemini/oauth_creds.json`, `~/.qwen/oauth_creds.json`, `~/.local/share/opencode/auth.json` | AI coding agent logins | Yes | Every one of these agents has a login command; re-authenticating beats carrying the token. See `ai-agents.md` |
| `~/.claude.json`, `~/.codex/config.toml`, `~/.cursor/mcp.json`, `~/.gemini/config/mcp_config.json`, any repo `.mcp.json` | Agent config — **and MCP `env` blocks** | Whenever a server sets a key inline | A config file with an inline `env` key is a credential file, whatever its name. `scripts/agent_inventory.py` reports the key *names*; that is enough to classify it without reading the value |
| `~/.databrickscfg`, `~/.snowflake`, `~/.dbt/profiles.yml` | Data tooling | Yes | `profiles.yml` often has database passwords |

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/credential-locations.md (reported line 55)May include surrounding context.

md
| `~/.config/*/credentials`, `~/.config/*/config.json`, `~/.config/*/auth.json` | Assorted CLI tools — Fly, Railway, Supabase, Doppler, Vercel, Stripe, Cloudflare | Usually | Enumerate with `ls -d ~/.config/*/` and ask about the ones you recognise |
| `~/.config/op`, `~/.1password` | Password manager CLI state | Device-bound | Usually needs re-authorising on the new machine regardless |
| `~/.terraform.d/credentials.tfrc.json` | Terraform Cloud token | Yes | |
| `~/.claude` | Claude Code settings, skills — and, on Linux, `.credentials.json` | Yes, the token file | Settings and skills go in `files/`; the token file follows the rules on this page. On macOS the OAuth token is in the login Keychain instead and cannot travel |
| `~/.codex/auth.json`, `~/.gemini/oauth_creds.json`, `~/.qwen/oauth_creds.json`, `~/.local/share/opencode/auth.json` | AI coding agent logins | Yes | Every one of these agents has a login command; re-authenticating beats carrying the token. See `ai-agents.md` |
| `~/.claude.json`, `~/.codex/config.toml`, `~/.cursor/mcp.json`, `~/.gemini/config/mcp_config.json`, any repo `.mcp.json` | Agent config — **and MCP `env` blocks** | Whenever a server sets a key inline | A config file with an inline `env` key is a credential file, whatever its name. `scripts/agent_inventory.py` reports the key *names*; that is enough to classify it without reading the value |
| `~/.databrickscfg`, `~/.snowflake`, `~/.dbt/profiles.yml` | Data tooling | Yes | `profiles.yml` often has database passwords |

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/agent_inventory.py (reported line 125)May include surrounding context.

python
| `~/.config/*/credentials`, `~/.config/*/config.json`, `~/.config/*/auth.json` | Assorted CLI tools — Fly, Railway, Supabase, Doppler, Vercel, Stripe, Cloudflare | Usually | Enumerate with `ls -d ~/.config/*/` and ask about the ones you recognise |
| `~/.config/op`, `~/.1password` | Password manager CLI state | Device-bound | Usually needs re-authorising on the new machine regardless |
| `~/.terraform.d/credentials.tfrc.json` | Terraform Cloud token | Yes | |
| `~/.claude` | Claude Code settings, skills — and, on Linux, `.credentials.json` | Yes, the token file | Settings and skills go in `files/`; the token file follows the rules on this page. On macOS the OAuth token is in the login Keychain instead and cannot travel |
| `~/.codex/auth.json`, `~/.gemini/oauth_creds.json`, `~/.qwen/oauth_creds.json`, `~/.local/share/opencode/auth.json` | AI coding agent logins | Yes | Every one of these agents has a login command; re-authenticating beats carrying the token. See `ai-agents.md` |
| `~/.claude.json`, `~/.codex/config.toml`, `~/.cursor/mcp.json`, `~/.gemini/config/mcp_config.json`, any repo `.mcp.json` | Agent config — **and MCP `env` blocks** | Whenever a server sets a key inline | A config file with an inline `env` key is a credential file, whatever its name. `scripts/agent_inventory.py` reports the key *names*; that is enough to classify it without reading the value |
| `~/.databrickscfg`, `~/.snowflake`, `~/.dbt/profiles.yml` | Data tooling | Yes | `profiles.yml` often has database passwords |

Static analysis

No suspicious patterns detected.