T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- scripts/engine.py:352
- Finding
- Unrestricted Environment Variable Disclosure to Child Skills and External Tools<![CDATA[ ## Vulnerability Details **File Location**: `scripts/engine.py:352-359`; repeated at `scripts/engine.py:467-473`, `scripts/engine.py:528-533`, and `scripts/engine.py:569-574` **Vulnerability Type**: Violation of least privilege through unrestricted credential inheritance **Risk Level**: High ### Vulnerable Code ```python # Execute the script while inheriting environment variables env = os.environ.copy() result = subprocess.run( cmd, capture_output=True, text=True, timeout=timeout, env=env ) ``` Equivalent unrestricted inheritance occurs when invoking the OpenClaw, mcporter, and web-fetch command-line tools: ```python result = subprocess.run( cmd, capture_output=True, text=True, timeout=30, env=os.environ.copy() ) ``` ### Technical Analysis The execution engine copies the entire parent process environment and supplies it to every selected skill script and external command-line tool. The environment may contain `TAVILY_API_KEY`, `BRAVE_API_KEY`, `OPENCLAW_TOKEN`, proxy credentials, cloud credentials, database credentials, CI/CD secrets, and unrelated application tokens. Individual skills generally require only a small subset of these variables. Passing the complete environment breaks the principle of least privilege and expands the trust boundary from SkillPilot to every discovered or configured executable. The use of `shell=False` and list-form command arguments prevents ordinary shell injection in these execution paths, but it does not protect environment secrets from the child process itself. A child process can directly read inherited variables and use its legitimate network access to disclose them. ### Attack Path 1. An attacker introduces or compromises a skill under the local OpenClaw skills directory. 2. The skill presents a supported script such as `scripts/search.py`, `scripts/fetch.py`, or `scripts/search.js`. 3. The skill is selected through the configured tool pool, default routing, or full c ...[truncated 1231 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Construct a minimal environment instead of copying `os.environ`: ```python base_env = { "PATH": os.environ.get("PATH", ""), "LANG": os.environ.get("LANG", "C.UTF-8"), } ``` 2. Define a per-skill allowlist of required variables: ```python SKILL_ENV_ALLOWLIST = { "tavily-search": {"TAVILY_API_KEY"}, "brave-search": {"BRAVE_API_KEY"}, "multi-search-engine": set(), } ``` 3. Add only the explicitly approved variables for the selected skill: ```python env = base_env.copy() for name in SKILL_ENV_ALLOWLIST.get(self.skill_name, set()): value = os.environ.get(name) if value is not None: env[name] = value ``` 4. Do not forward `OPENCLAW_TOKEN`, cloud credentials, proxy passwords, or unrelated application secrets unless a documented invocation specifically requires them. 5. Run third-party skills in a restricted subprocess, container, or sandbox with limited filesystem and network access. 6. Require an explicit trust decision before executing newly discovered scripts. 7. Resolve external executables through trusted absolute paths and verify ownership and permissions. 8. Add tests confirming that undeclared secrets are absent from every child process environment. ]]>
