fix(cli): prevent shell environment variable leakage into agent secrets#6360
fix(cli): prevent shell environment variable leakage into agent secrets#6360
Conversation
Shell environment variables (from .zshrc/.bashrc) were leaking into ElizaOS plugin loading decisions and agent secrets because dotenv does NOT override existing process.env values by default. This fix introduces: - Plugin env var scanning: scans installed plugins for declared env vars - Global filter: only declared env vars are captured into agent secrets - Precedence loading: closest .env file values win over parent directories Changes: - Add scanPluginsForEnvDeclarations() to scan plugin package.json for agentConfig.pluginParameters declarations - Add setAllowedEnvVars() filter in secrets.ts to restrict captured vars - Add loadEnvFilesWithPrecedence() for proper .env hierarchy loading - Update start command to set filter before any character loading This prevents scenarios where a user's shell has ANTHROPIC_API_KEY but their project .env only has OPENROUTER_API_KEY - previously both plugins would load unexpectedly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Claude finished @0xbbjoker's task in 1m 49s —— View job PR Review: Environment Variable Leakage Prevention
Overall Assessment: ✅ APPROVEThis PR properly addresses a security concern where shell environment variables could leak into plugin loading decisions and agent secrets. The implementation is well-designed with comprehensive test coverage. What's GoodSecurity Fix: Properly prevents shell-based API keys from leaking into agent secrets Minor SuggestionsLines 209-210, 235, 250-251 in const fs = require('node:fs');
const nodePath = require('node:path');
const dotenv = require('dotenv');While this works fine and is intentional for tree-shaking in browser builds, consider documenting why these are runtime requires vs ES imports in a comment to prevent future contributors from "fixing" them.
Code Quality
Test Coverage
The tests properly cover the integration flow preventing shell leakage scenario. |
Move ENV_VAR_NAME_REGEX to module level to avoid recompiling on each call to detectShellOnlyVars(). Addresses: #6360 (comment) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Claude finished @0xbbjoker's task in 50s —— View job PR Review: Environment Variable Leakage Prevention
Overall Assessment: ✅ APPROVEThis PR properly addresses a security concern where shell environment variables could leak into plugin loading decisions and agent secrets. The implementation is well-designed with comprehensive test coverage. What's Good
Previous Review Comments AddressedThe regex caching suggestion from Greptile was addressed in commit Minor Observations (Non-blocking)Lines 209-210, 235, 250-251 use dynamic
Test Coverage Summary
All tests properly verify the integration flow preventing shell leakage. Security Check ✅
Verdict: Ship it! The implementation is solid, well-tested, and addresses a real security concern. |
The test for detecting browser environment was failing because detectEnvironment() now caches its result at module level. Added resetEnvironmentCache() call before mocking browser environment to ensure the test gets a fresh detection. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Claude finished @0xbbjoker's task in 1m 1s —— View job PR Review: Environment Variable Leakage Prevention
Overall Assessment: ✅ APPROVEThis PR properly addresses a security concern where shell environment variables could leak into plugin loading decisions and agent secrets. The implementation is well-designed with comprehensive test coverage (40 tests across 3 test files). What's Good
Code Quality Review
Minor Observations (Non-blocking)Lines 209-210, 235, 250-251 use dynamic
Test Coverage Summary
All tests properly verify the integration flow preventing shell leakage. Security Check ✅
Verdict: Ship it! The implementation is solid, well-tested, and addresses a real security concern. |
|
Claude finished @0xbbjoker's task in 1m 35s —— View job PR Review: Environment Variable Leakage Prevention
Overall Assessment: ✅ APPROVEThis PR properly addresses a security concern where shell environment variables could leak into plugin loading decisions and agent secrets. The implementation is well-designed with comprehensive test coverage. What's Good
Previous Review Comments StatusAll prior review comments have been addressed:
Minor Observations (Non-blocking)Lines 209-210, 235, 250-251 use dynamic
Test Coverage Summary
All tests properly verify the integration flow preventing shell leakage. Security Check ✅
Verdict: Ship it! The implementation is solid, well-tested, and addresses a real security concern. |
|
Claude finished @0xbbjoker's task in 1m 29s —— View job PR Review: Environment Variable Leakage Prevention
✅ APPROVEThis PR properly addresses a security concern where shell environment variables could leak into plugin loading decisions and agent secrets. The implementation is well-designed with comprehensive test coverage. Summary
Previous Review Comments - All Addressed
Code Quality Highlights
Minor Observations (Non-blocking)Lines 209-210, 235, 250-251 in
Verdict: Ship it! The implementation is solid, well-tested, and addresses a real security concern. |
| } else { | ||
| result.pluginsWithoutDeclarations.push(pkgName); | ||
| } | ||
| } |
There was a problem hiding this comment.
Missing type validation for pluginParameters from package.json
Low Severity
The processPluginDeclarations function accepts declarations typed as Record<string, EnvVarConfig> | null, but the value comes from JSON parsing with no runtime type check. If a package.json has pluginParameters set to a non-object value (like a string or array), Object.keys() and Object.entries() will process it incorrectly—for example, Object.keys("hello") returns ['0', '1', '2', '3', '4'], adding nonsensical index keys to allowedVars.
Summary
Fixes shell environment variable leakage into ElizaOS plugin loading decisions and agent secrets.
Problem:
dotenv.config()does NOT override existingprocess.envvalues by default. This means shell environment variables (from.zshrc/.bashrc) leak into plugin loading:Solution:
package.jsonagentConfig.pluginParameters.envfiles with proper precedence (closest file wins)Changes
packages/cli/src/utils/plugin-env-filter.ts- New utility to scan plugins for env var declarationspackages/core/src/secrets.ts- AddsetAllowedEnvVars()filter to restrict captured varspackages/core/src/utils/environment.ts- AddloadEnvFilesWithPrecedence()for proper hierarchypackages/cli/src/commands/start/index.ts- Integrate filter before character loadingTest plan
plugin-env-filter.test.ts)secrets-filtering.test.ts)env-precedence.test.ts)Notes
🤖 Generated with Claude Code
Note
Introduces plugin-based env var allowlisting and proper
.envprecedence to stop shell vars from leaking into agent config/secrets.utils/plugin-env-filter.tsto scan pluginpackage.json(agentConfig.pluginParameters) and build allowed var set; includes core defaults and warnings for undeclared official pluginssecrets.ts: newsetAllowedEnvVars/getAllowedEnvVars;setDefaultSecretsFromEnvmerges only allowed vars (character values take precedence);hasCharacterSecretsoptimizedutils/environment.ts: addsfindAllEnvFilesandloadEnvFilesWithPrecedence(closest wins, optional boundary/override); exposesresetEnvironmentCache; minor perf tidy-upsstartcommand: runs plugin scan, sets allowed vars filter, warns on missing declarations, and loads.envwith precedence before loading characters/agentsWritten by Cursor Bugbot for commit f30034e. This will update automatically on new commits. Configure here.
Greptile Overview
Greptile Summary
This PR implements a robust solution to prevent shell environment variable leakage into ElizaOS agent secrets by introducing plugin-based environment variable filtering.
Key Changes
plugin-env-filter.tsutility scans installed plugins'package.jsonfiles for declared environment variables viaagentConfig.pluginParameterssetAllowedEnvVars()to restrict which environment variables are captured into agent secretsloadEnvFilesWithPrecedence()ensures closest.envfile wins, respecting monorepo boundariesArchitecture
The solution follows a clear flow: scan plugins → set filter → load env files → filter on capture. Core environment variables (e.g.,
POSTGRES_URL,LOG_LEVEL) are always allowed. The filter is null by default for backward compatibility.Issues Found
plugin-env-filter.tsalready correctly checks for null declarations before accessing lengthsecrets.tsuses underscore variable as TypeScript idiom for unused loop variable (valid pattern)detectShellOnlyVarsfor performanceCORE_ALLOWED_VARShardcoded in CLI could be centralized in core packageTesting
40 tests passing across 3 new test files with comprehensive coverage of filtering, precedence, and integration scenarios.
Confidence Score: 4/5
packages/cli/src/utils/plugin-env-filter.tsfor the regex caching optimization and consider centralizingCORE_ALLOWED_VARSImportant Files Changed
File Analysis
.envfile precedence loading with boundary directory supportSequence Diagram
sequenceDiagram participant CLI as CLI Start Command participant Scanner as Plugin Scanner participant Core as Core (secrets.ts) participant Env as Environment Loader participant Agent as Agent/Character CLI->>Scanner: scanPluginsForEnvDeclarations(cwd) Scanner->>Scanner: Scan node_modules for plugins Scanner->>Scanner: Read package.json files Scanner->>Scanner: Extract pluginParameters Scanner-->>CLI: allowedVars Set + plugin info CLI->>Core: setAllowedEnvVars(allowedVars) Note over Core: Global filter set CLI->>Env: loadEnvFilesWithPrecedence(cwd) Env->>Env: findAllEnvFiles (parent dirs) Env->>Env: Load files (farthest to closest) Env-->>CLI: Loaded env files Note over CLI: process.env populated CLI->>Agent: Load character/agent Agent->>Core: setDefaultSecretsFromEnv(character) Core->>Core: Filter process.env by allowedVars Core->>Agent: Merge filtered vars into secrets Note over Agent: Only declared vars captured