T09 · Insecure Skill Coding Practices
- Location
scripts/inject-gateway-env.sh:40- Finding
Persistent Plaintext Storage of API Credentials in the LaunchAgent Plist
- Content
View full analysis
Vulnerability Details
File Location:
scripts/inject-gateway-env.sh:40-54
Vulnerability Type: Plaintext sensitive-data storage
Risk Level: HighVulnerable 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
PlistBuddyserializes 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
- 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
View 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
0600on any file that can contain sensitive configuration. - Remove existing secret values from deployed plists and rotate every credential previously written there.
- Correct
SKILL.mdso its security claims accurately describe the implementation.
