Back to skill

Security audit

OATDA Generate Speech

Security checks for vulnerabilities and agentic risk

Overview

This speech-generation skill is mostly purpose-aligned, but its shell command template can mishandle user text and may allow command injection if followed directly.

Install only if you are comfortable sending the text you provide to OATDA and with the skill reading your OATDA API key. Agents should not paste arbitrary user text directly into the shown shell template; they should build JSON with a proper encoder such as jq --arg and choose non-conflicting output paths.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:69
Finding

Shell Command Injection Through Unsafe TTS Input Interpolation

Content
View full analysis
/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}" && \ curl -s -X POST "https://oatda.com/api/v1/llm/speech" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OATDA_API_KEY" \ -d '{ "provider": "", "model": "", "input": "", "voice": "alloy", "response_format": "mp3", "speed": 1.0 }' \ --output speech.mp3 ``` ### Technical Analysis The Skill instructs the Agent to insert user-supplied speech text directly into a single-quoted shell argument. This does not provide safe separation between data and shell syntax. If the supplied text contains an apostrophe, it can terminate the single-quoted `curl -d` argument. Subsequent shell metacharacters may then be interpreted as commands rather than speech content. Independently of command injection, quotes, backslashes, control characters, and line breaks are not correctly JSON-escaped, which can corrupt the request body. The affected command runs after loading `OATDA_API_KEY` from the environment or `~/.oatda/credentials.json`. Consequently, successful command injection executes in a process that may have access to the OATDA credential and any other files or environment variables accessible to the Agent. Access to the OATDA-specific credential is otherwise consistent with the declared speech-generation function and does not, by itself, exceed the minimum privileges needed for API authentication. ### Attack Path 1. An attacker supplies text for speech generation containing a single quote followed by shell control syntax and an attacker-selected command. 2. The Agent substitutes that text into the `` p ...[truncated 1518 chars]
Remediation
View remediation
"$request_file" curl --fail-with-body --silent --show-error \ -X POST "https://oatda.com/api/v1/llm/speech" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OATDA_API_KEY" \ --data-binary "@$request_file" \ --output speech.mp3 ``` 3. Keep user data in shell variables or files and never reinterpret it with `eval`, `sh -c`, or equivalent mechanisms. 4. Validate enumerated parameters such as provider, model, voice, and response format against explicit allowlists or API-discovered supported values. 5. Validate `speed` as a numeric value within the documented range before passing it through `--argjson`. 6. Use `mktemp` and a cleanup trap for temporary request files; do not use predictable shared temporary paths. 7. Use `curl --fail-with-body --silent --show-error` and verify the HTTP status and content type before treating the output as audio. 8. Select a user-approved or uniquely generated output path to avoid unintentionally overwriting an existing `speech.mp3`. 9. Continue preventing the API key from appearing in logs, command output, error messages, or generated files. 10. Add tests containing apostrophes, quotation marks, backslashes, newlines, Unicode text, and shell metacharacters to verify that all input remains inert data. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 10)May include surrounding context.

md
"openclaw":
      {
        "emoji": "🔊",
        "requires": { "bins": ["curl", "jq"], "env": ["OATDA_API_KEY"], "config": ["~/.oatda/credentials.json"] },
        "primaryEnv": "OATDA_API_KEY",
      },
  }

Credential Access

High
Category
Privilege Escalation
Confidence
88% confidence
Finding

The skill reads an API key from a local credentials file and exports it into the shell environment for subsequent commands. While common, this expands credential exposure within the execution context and increases the chance of accidental leakage through subprocesses, debugging, shell history, or error handling if the environment is not tightly controlled.

Content

Scanner excerpt · SKILL.md (reported line 25)May include surrounding context.

All commands need the OATDA API key. Resolve it inline for each exec call:

bash
export OATDA_API_KEY="${OATDA_API_KEY:-$(cat ~/.oatda/credentials.json 2>/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}"

If the key is empty or null, tell the user to get one at https://oatda.com and configure it.

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

This command again loads the API key from the local credential store and immediately uses it in an outbound request. The combination of credential access plus network use increases risk because compromise of the execution environment or logging pipeline could expose the bearer token.

Content

Scanner excerpt · SKILL.md (reported line 53)May include surrounding context.

Query available audio models and inspect supported_params before sending optional fields:

bash
export OATDA_API_KEY="${OATDA_API_KEY:-$(cat ~/.oatda/credentials.json 2>/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}" && \
curl -s -X GET "https://oatda.com/api/v1/llm/models?type=audio" \
  -H "Authorization: Bearer $OATDA_API_KEY" | jq '.audio_models[] | {id, supported_params}'

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

The skill accesses a local credential file to retrieve a bearer token for a remote API call. Although the instructions advise not printing the key, the pattern still handles sensitive material in a shell pipeline, which can be risky in shared or instrumented agent environments.

Content

Scanner excerpt · SKILL.md (reported line 69)May include surrounding context.

The speech endpoint returns binary audio, not JSON. Always save the response to a file.

bash
export OATDA_API_KEY="${OATDA_API_KEY:-$(cat ~/.oatda/credentials.json 2>/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}" && \
curl -s -X POST "https://oatda.com/api/v1/llm/speech" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OATDA_API_KEY" \

Credential Access

High
Category
Privilege Escalation
Confidence
85% confidence
Finding

The example command repeats the practice of extracting a secret from local storage and exporting it for use in a network request. Repetition in examples matters because implementers may copy-paste this pattern broadly, propagating unsafe credential handling.

Content

Scanner excerpt · SKILL.md (reported line 114)May include surrounding context.

Example

bash
export OATDA_API_KEY="${OATDA_API_KEY:-$(cat ~/.oatda/credentials.json 2>/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}" && \
curl -s -X POST "https://oatda.com/api/v1/llm/speech" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OATDA_API_KEY" \

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: oatda-generate-speech
description: Generate speech or audio from text using OATDA's unified audio API. Triggers when the user wants to convert text to speech, create narration, voiceovers, accessibility audio, or use TTS models such as OpenAI tts-1 through OATDA.
homepage: https://oatda.com
metadata:
  {

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The skill sends user-provided text and an API credential to an external third-party service over the network. This is an inherent data egress behavior: if users supply sensitive text, that content leaves the local environment and is transmitted to OATDA/OpenAI-backed services, which can create privacy, compliance, or data-handling risk.

Content

Scanner excerpt · SKILL.md (reported line 70)May include surrounding context.

bash
export OATDA_API_KEY="${OATDA_API_KEY:-$(cat ~/.oatda/credentials.json 2>/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}" && \
curl -s -X POST "https://oatda.com/api/v1/llm/speech" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OATDA_API_KEY" \
  -d '{

External Transmission

Medium
Category
Data Exfiltration
Confidence
93% confidence
Finding

This example performs the same external POST of text to the speech API, so it carries the same egress risk as the main API call. Even though the example is illustrative, an agent following it would transmit content off-host and could expose sensitive user text to a remote provider.

Content

Scanner excerpt · SKILL.md (reported line 115)May include surrounding context.

bash
export OATDA_API_KEY="${OATDA_API_KEY:-$(cat ~/.oatda/credentials.json 2>/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}" && \
curl -s -X POST "https://oatda.com/api/v1/llm/speech" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OATDA_API_KEY" \
  -d '{

Static analysis

No suspicious patterns detected.