T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/inject-gateway-env.sh:40
- Finding
- Persistent Plaintext Storage of API Credentials in the LaunchAgent Plist## Vulnerability Details **File Location**: `scripts/inject-gateway-env.sh:40-54` **Vulnerability Type**: Plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```bash inject_key() { local key="$1" val="$2" [ -z "$val" ] && return $BUDDY -c "Delete :EnvironmentVariables:$key" "$PLIST" 2>/dev/null || true $BUDDY -c "Add :EnvironmentVariables:$key string $val" "$PLIST" echo " ✅ $key" } inject_key "OPENAI_API_KEY" "$OPENAI_API_KEY" inject_key "ANTHROPIC_API_KEY" "$ANTHROPIC_API_KEY" inject_key "GEMINI_API_KEY" "$GEMINI_API_KEY" inject_key "MISTRAL_API_KEY" "$MISTRAL_API_KEY" inject_key "VOYAGE_API_KEY" "$VOYAGE_API_KEY" inject_key "HF_TOKEN" "$HF_TOKEN" inject_key "OP_SERVICE_ACCOUNT_TOKEN" "$(cat "$TOKEN_FILE")" inject_key "SHERPA_ONNX_RUNTIME_DIR" "$HOME/.openclaw/sherpa-onnx/runtime" inject_key "SHERPA_ONNX_MODEL_DIR" "$HOME/.openclaw/sherpa-onnx/models/vits-piper-en_US-lessac-high" inject_key "OTLP_ENDPOINT" "localhost:4317" ``` ### Technical Analysis `PlistBuddy` serializes the environment-variable values into `~/Library/LaunchAgents/ai.openclaw.gateway.plist`. Consequently, the OpenAI, Anthropic, Gemini, Mistral, Voyage, Hugging Face, and 1Password credentials are stored persistently in plaintext on disk. This behavior directly contradicts the Skill's claim that it injects credentials “without leaving plaintext keys on disk.” No code verifies or enforces restrictive permissions on the resulting plist. Although restarting an existing LaunchAgent is consistent with the declared gateway-management function, persistent storage of all credentials in its configuration is not a minimum-privilege secret-delivery mechanism. ### Attack Path 1. An attacker obtains local execution under the user account, access to the user's files, a readable backup, or a diagnostic archive containing the LaunchAgent plist. ...[truncated 901 chars]
- Remediation
- ## Remediation Suggestions - Do not place secret values directly in a persistent LaunchAgent plist. - Use a narrowly scoped runtime secret broker or wrapper that obtains credentials immediately before launching the gateway and does not serialize them into persistent configuration. - Prefer platform-supported secret storage, such as macOS Keychain, with access controls restricted to the intended executable and user. - If runtime environment variables remain unavoidable, ensure the persistent plist contains only non-secret configuration and references a protected runtime-loading mechanism. - Never include the 1Password service-account token in the gateway environment. - As defense in depth, verify ownership and enforce mode `0600` on any file that can contain sensitive configuration. - Remove existing secret values from deployed plists and rotate every credential previously written there. - Correct `SKILL.md` so its security claims accurately describe the implementation.
