T09 · Insecure Skill Coding Practices
- Location
- scripts/provider-engine.js:110
- Finding
- Custom Provider Configuration Can Exfiltrate Arbitrary Environment Secrets<![CDATA[ ## Vulnerability Details **File Location**: `scripts/provider-engine.js:110-153` **Additional Location**: `references/onboarding.md:124-145` **Vulnerability Type**: Unrestricted secret selection and transmission to a configurable endpoint **Risk Level**: High ### Vulnerable Code ```js async function callProvider(capability, specPath, resultBase) { const apiKey = process.env[capability.envKey]; if (!apiKey) throw new Error(`Missing env: ${capability.envKey}`); const spec = JSON.parse(fs.readFileSync(specPath, 'utf-8')); const prompt = buildPrompt(spec); const provider = resolveProvider(capability); if (!provider.endpoint) { throw new Error(`No endpoint for "${capability.api}". Set endpoint in config or add to data/providers.json`); } const sizeVal = resolveSize(provider.sizeMap, spec.ratio); const vars = { apiKey, prompt }; // ... const headers = {}; if (provider.auth) { headers['Authorization'] = interpolate(provider.auth, vars); } const body = interpolate(provider.body, vars); // ... const res = await fetchWithRetry(provider.endpoint, fetchOpts); ``` The documented custom-provider flow permits both values to be supplied through configuration: ```json { "api": "<api name>", "envKey": "<env var>", "endpoint": "<url>", "provider": { "auth": "Bearer {{apiKey}}", "body": { "prompt": "{{prompt}}" }, "response": { "type": "json", "imagePath": "data[0].url" } } } ``` ### Technical Analysis The implementation treats `capability.envKey` as an unrestricted lookup into `process.env` and sends the resulting value to `provider.endpoint`. Neither the environment-variable name nor the destination is constrained. Because `scripts/lib/env.js` imports every value from `~/.openclaw/marketplace.env`, a custom capability can select any loaded marketplace or provider secret. It can also select unrelated secrets already present in the parent process environment. The endpoint has no HTTPS requiremen ...[truncated 1275 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace arbitrary `process.env[capability.envKey]` access with an allowlist of credential identifiers supported by the Skill. 2. Bind each registered provider to a fixed credential name and fixed hostname. 3. Require explicit, separate user authorization before enabling a custom endpoint or credential. 4. Require HTTPS and reject URLs containing embedded credentials. 5. Resolve destination addresses and reject loopback, private, link-local, multicast, and metadata-service ranges. 6. Revalidate the destination after every redirect. 7. Store provider credentials in a secret manager where possible instead of exposing all credentials through the process environment. 8. Ensure logs and error responses never include authorization values. ]]>
