Back to skill

Security audit

Speech Generation Skill

Security checks for vulnerabilities and agentic risk

Overview

This speech-generation skill is mostly aligned with its purpose, but it includes unsafe guidance to disable Codex approval controls and has a batch output path weakness.

Install only if you are comfortable using a local CLI that sends your text to the OpenAI Audio API and writes generated audio files. Do not follow the documented approval_policy = "never" or --ask-for-approval never guidance unless you fully understand the broader Codex security impact; prefer approving the specific speech command instead. Treat batch JSONL files as trusted input, avoid --force on untrusted batches, and review any per-job out paths before running.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/text_to_speech.py:406
Finding
Batch Output Path Traversal Allows Writes Outside the Designated Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/text_to_speech.py`, lines 406-413 **Vulnerability Type**: Path traversal and arbitrary file write **Risk Level**: Medium ### Vulnerable Code ```python explicit_out = job.get("out") if explicit_out: out_path = _normalize_output_path(str(explicit_out), response_format) if out_path.is_absolute(): out_path = out_dir / out_path.name else: out_path = out_dir / out_path else: slug = _slugify(input_text[:80]) out_path = out_dir / f"{idx:03d}-{slug}.{response_format}" ``` ### Technical Analysis The batch job's attacker-controlled `out` property is converted into a path and joined directly to `out_dir`. Although absolute paths are reduced to their basename, relative paths are not checked for parent-directory components such as `..`. For example, the following job produces a path outside the designated output directory: ```json {"input":"Attacker-controlled content","out":"../../target.mp3"} ``` If `--out-dir output/speech` is used, the resulting path is effectively: ```text output/speech/../../target.mp3 ``` The later `_write_audio()` call creates parent directories and streams the API response to this path. Existing files are protected by default, but passing `--force` permits replacement. The code also performs no resolved-path containment check, so path traversal and potentially symlink-based escapes remain possible. ### Attack Path 1. An attacker supplies or modifies a JSONL batch file. 2. The attacker sets a job's `out` property to a traversal path such as `../../target.mp3`. 3. A user invokes `speak-batch` with that JSONL file and a valid API key. 4. The application joins the traversal path to `out_dir` without canonicalization or containment validation. 5. The OpenAI API response is written outside the intended output directory. 6. If the user supplied `--force`, an existing writable file at the resolved destination can be replaced with generated audio da ...[truncated 625 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject absolute paths and any path containing `..` components. 2. Resolve both the output directory and candidate destination before writing. 3. Verify that the resolved destination is strictly contained within the resolved output directory. 4. Account for existing symlinks in parent directories and reject destinations that escape through symlink resolution. 5. Continue refusing to overwrite existing files by default. 6. Consider restricting batch `out` values to filenames rather than arbitrary relative paths. Example containment validation: ```python base_dir = Path(args.out_dir).resolve() relative_out = Path(str(explicit_out)) if relative_out.is_absolute() or ".." in relative_out.parts: _die(f"Invalid batch output path: {relative_out}") candidate = (base_dir / relative_out).resolve() try: candidate.relative_to(base_dir) except ValueError: _die(f"Output path escapes output directory: {relative_out}") out_path = candidate ``` For stronger protection against time-of-check/time-of-use and symlink attacks, open the destination using directory-relative operating-system APIs and no-follow semantics where the platform supports them. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
references/codex-network.md:11
Finding
Network Troubleshooting Guidance Recommends Disabling Agent Approval Controls<![CDATA[ ## Vulnerability Details **File Location**: `references/codex-network.md`, lines 11-26 **Vulnerability Type**: Excessive permission configuration and approval bypass **Risk Level**: Medium ### Vulnerable Configuration ```markdown If you trust the repo and want fewer prompts, enable network access for the relevant sandbox mode and relax the approval policy. Example `~/.codex/config.toml` pattern: ```toml approval_policy = "never" sandbox_mode = "workspace-write" [sandbox_workspace_write] network_access = true ``` Or for a single session: ```sh codex --sandbox workspace-write --ask-for-approval never ``` ``` ### Technical Analysis Speech generation legitimately requires outbound network access, but the recommended configuration is substantially broader than that requirement. Setting `approval_policy = "never"` disables command approval rather than approving only the known speech-generation operation. Combining that setting with network access allows later commands to communicate externally without interactive confirmation. The persistent `~/.codex/config.toml` example can affect subsequent repositories and sessions, not merely the current TTS invocation. The single-session command has a narrower lifetime but still disables approval for every command executed during that session. The document includes a caution that this reduces security, but the configuration still violates least-privilege principles and exposes users to avoidable risk when working with untrusted repositories. ### Attack Path 1. A user encounters network approval prompts while attempting speech generation. 2. The user follows the documented troubleshooting instructions. 3. The user either persistently configures `approval_policy = "never"` or starts a session with `--ask-for-approval never`. 4. Network access is enabled for commands running in the writable workspace sandbox. 5. Malicious or compromised repository instructions subsequently cause an untrusted command to run. ...[truncated 693 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the recommendation to set `approval_policy = "never"` persistently. 2. Preserve the environment's default approval policy. 3. Instruct users to approve only the specific, reviewed TTS invocation. 4. If supported, allowlist only the OpenAI API hostname and required HTTPS port rather than enabling unrestricted outbound access. 5. Prefer a dedicated session or narrowly scoped profile for speech generation. 6. Clearly distinguish temporary network enablement from global approval-policy changes. 7. Advise users to restore restrictive settings immediately after the operation if temporary relaxation is unavoidable. A safer document should recommend enabling only the minimum network access required while retaining command approval, rather than disabling approvals for the entire session or future sessions. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:41
Finding
OpenAI SDK Is Installed Without Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 41-52; corroborated by `references/cli.md`, lines 28-36 **Vulnerability Type**: Unpinned runtime dependency **Risk Level**: Low ### Vulnerable Dependency Instructions From `SKILL.md`: ```markdown ## Dependencies (install if missing) Prefer `uv` for dependency management. Python packages: ``` uv pip install openai ``` If `uv` is unavailable: ``` python3 -m pip install openai ``` ``` The CLI reference also resolves the dependency dynamically: ```sh uv run --with openai python "$TTS_GEN" speak \ --input "Today is a wonderful day to build something people love!" \ --voice cedar \ --instructions "Voice Affect: Warm and composed. Tone: upbeat and encouraging." \ --response-format mp3 \ --out speech.mp3 ``` ### Technical Analysis The Skill installs the `openai` package without a reviewed version constraint, lockfile, or integrity hash. Consequently, each installation can resolve to a different package release. The package name is legitimate and no currently malicious dependency was identified, but the installation process is mutable and not reproducible. A compromised package-distribution account, package index, network configuration, or future release could introduce unwanted code after the Skill itself has been audited. The dynamically installed SDK executes within the Python process and can access the same environment as the CLI, including `OPENAI_API_KEY`. ### Attack Path 1. A user follows the Skill's installation or execution instructions. 2. `pip` or `uv` resolves the latest available `openai` package from the configured package index. 3. The resolved package is installed or executed without verification against an audited lockfile or hash. 4. If the package source or selected release has been compromised, its code executes under the user's account. 5. The dependency can access process data, local files available to the user, network access, and the `OPENAI_API_KEY` ...[truncated 601 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the OpenAI SDK to a reviewed, compatible version. 2. Maintain a lockfile generated by the selected dependency manager. 3. Use cryptographic hashes for downloaded distributions where supported. 4. Document the expected package index and avoid untrusted mirrors. 5. Review and test dependency updates before changing the pinned version. 6. Prefer installation into an isolated virtual environment. 7. Consider publishing a requirements file with hash enforcement, for example: ```text openai==<reviewed-version> \ --hash=sha256:<verified-distribution-hash> ``` 8. Replace `uv run --with openai` with execution against the locked environment so routine Skill invocation does not dynamically resolve mutable dependencies. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (8)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
python "$TTS_GEN" speak-batch --input tmp/speech/jobs.jsonl --out-dir out --rpm 50

# Cleanup (recommended)
rm -f tmp/speech/jobs.jsonl
```

Notes:
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Agent Config Directory Access

High
Category
Agent Snooping
Content
If you trust the repo and want fewer prompts, enable network access for the relevant sandbox mode and relax the approval policy.

Example `~/.codex/config.toml` pattern:

```
approval_policy = "never"
Confidence
92% confidence
Finding
The document advises users to set `approval_policy = "never"` and enable network access in a persistent agent config, which materially weakens execution safeguards for future sessions. In the context of a skill that requires outbound API calls, this guidance can normalize disabling approvals and expanding trust boundaries, increasing the chance that untrusted code or prompts trigger networked actions without user review.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
if instructions and model in {"tts-1", "tts-1-hd"}:
        _warn("instructions are not supported for tts-1 / tts-1-hd; ignoring.")
        return None
    return instructions


def _print_payload(payload: Dict[str, Any]) -> None:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill instructs the agent to read files, access environment configuration, install dependencies, and make live network API calls, but it declares no explicit tool scope or permission boundaries. That mismatch can cause the agent to invoke higher-risk capabilities implicitly, increasing the chance of overreach, accidental secret exposure, or unauthorized network/file access during normal skill execution.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
2. Set `OPENAI_API_KEY` as an environment variable in their system.
3. Offer to guide them through setting the environment variable for their OS/shell if needed.

- Never ask the user to paste the full key in chat. Ask them to set it locally and confirm when ready.

If installation isn't possible in this environment, tell the user which dependency is missing and how to install it locally.
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Vague Triggers

Low
Confidence
83% confidence
Finding
This is a markdown/manifest-context trigger description, so SQP-1 applies. Phrases like "accessibility reads" and "audio prompts" are relatively open-ended and the file does not provide negative examples or a bounded activation context, which could cause unintended invocation for general audio-related requests.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
The markdown specifies "Voice: `cedar`" as a default, which can function as a language/locale or presentation constraint without any indication that the user may choose alternatives. Under the policy rule, fixed defaults that may impose a specific locale or voice characteristic without opt-in can be considered a natural-language policy concern.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
def _extract_retry_after_seconds(exc: Exception) -> Optional[float]:
    for attr in ("retry_after", "retry_after_seconds"):
        val = getattr(exc, attr, None)
        if isinstance(val, (int, float)) and val >= 0:
            return float(val)
    msg = str(exc)
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.