Back to skill

Security audit

Podcast Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it can make real SkillPay billing calls and write generated files with weak safety controls.

Review before installing if this would run automatically or in a shared environment. Only use it where SkillPay charges are explicitly approved, output paths are controlled, and podcast text is suitable for external TTS processing. Prefer a pinned dependency setup and isolate execution from sensitive files and environment variables.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_audio.py:50
Finding
Predictable Shared Temporary Files Allow Local File Overwrite and Cross-Run Interference<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_audio.py`, lines 50–72 **Vulnerability Type**: Unsafe predictable temporary files **Risk Level**: Medium ### Vulnerable Code ```python tmps = [] for i, s in enumerate(segs): if not s["text"].strip(): continue tmp = f"/tmp/pod_{i:03d}.mp3" v = voice_for(s["speaker"], custom_voice) try: subprocess.run( ["edge-tts", "--voice", v, "--text", s["text"], "--write-media", tmp], capture_output=True, timeout=60, check=True ) tmps.append(tmp) except Exception as e: print(f"⚠️ 片段{i}失败: {e}") if not tmps: return {"segments": len(segs), "output": None, "error": "no segments generated"} has_ffmpeg = os.system("which ffmpeg >/dev/null 2>&1") == 0 if has_ffmpeg and len(tmps) > 1: lst = "/tmp/pod_list.txt" with open(lst, "w") as f: for t in tmps: f.write(f"file '{t}'\n") ``` ### Technical Analysis The audio generation process creates temporary media files using predictable names such as `/tmp/pod_000.mp3` and writes the FFmpeg input list to the fixed path `/tmp/pod_list.txt`. The system temporary directory is commonly writable by all local users. The code does not: - Create a private, randomly named temporary directory. - Use exclusive file creation. - Reject symbolic links. - Verify file ownership or type before writing. - Isolate files belonging to concurrent executions. The call to `open("/tmp/pod_list.txt", "w")` follows symbolic links and truncates the resolved target. A local attacker who can write to `/tmp` can pre-create that path as a symbolic link to another file writable by the victim. Predictable media names also allow concurrent or malicious executions to overwrite, replace, or mix audio segments. The temporary list file is not included in the cleanup routine, leaving stale state in the shared temporary directory. ### Attack Path 1. A local attacker identifies that the ...[truncated 1546 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use Python’s `tempfile` module to create a private, randomly named directory for every invocation: ```python import tempfile from pathlib import Path with tempfile.TemporaryDirectory(prefix="podcast-generator-") as temp_dir: temp_path = Path(temp_dir) tmps = [] for i, segment in enumerate(segs): if not segment["text"].strip(): continue media_file = temp_path / f"segment_{i:03d}.mp3" voice = voice_for(segment["speaker"], custom_voice) subprocess.run( [ "edge-tts", "--voice", voice, "--text", segment["text"], "--write-media", str(media_file), ], capture_output=True, timeout=60, check=True, ) tmps.append(media_file) concat_file = temp_path / "concat.txt" with concat_file.open("x", encoding="utf-8") as handle: for media_file in tmps: handle.write(f"file '{media_file}'\n") ``` Additional hardening should include: 1. Keep all temporary artifacts inside the private temporary directory. 2. Use exclusive creation mode where practical. 3. Do not run the Skill with elevated privileges. 4. Check the FFmpeg return code by using `check=True`. 5. Write the final output to a temporary file in the destination directory and atomically rename it after successful generation. 6. Ensure cleanup covers every temporary artifact, including the FFmpeg concat list. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:46
Finding
Unpinned Third-Party TTS Dependency Creates a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, line 46 **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Medium ### Vulnerable Code ```markdown Requires `edge-tts` (`pip install edge-tts`). Uses different voices for Host A (female) and Host B (male). Falls back to segment list if edge-tts unavailable. ``` ### Technical Analysis The documented command installs the latest version of `edge-tts` and its transitive dependencies available at installation time. The project does not provide: - An exact reviewed package version. - A lockfile. - Cryptographic hashes for downloaded distributions. - Constraints on transitive dependencies. - A reproducible dependency installation process. Consequently, the dependency graph can change after the Skill has been audited. A compromised package release, compromised maintainer account, or malicious transitive dependency could introduce arbitrary code into the installation or runtime environment. This is particularly relevant because `scripts/generate_audio.py` executes the installed `edge-tts` command and supplies podcast text to it: ```python subprocess.run( ["edge-tts", "--voice", v, "--text", s["text"], "--write-media", tmp], capture_output=True, timeout=60, check=True ) ``` Although the subprocess invocation correctly avoids shell interpolation, it still trusts the installed executable to process potentially sensitive script content. ### Attack Path 1. A user follows the documented setup command: ```bash pip install edge-tts ``` 2. pip resolves the latest available `edge-tts` package and current transitive dependencies. 3. If a resolved package version or dependency has been compromised, attacker-controlled package code is installed. 4. Malicious code may run during installation or when `edge-tts` is invoked by `generate_audio.py`. 5. The malicious dependency executes with the permissions of the user running the Skill and receives access to the ...[truncated 1041 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace the unconstrained installation instruction with a reproducible, reviewed dependency process: 1. Pin `edge-tts` and every transitive dependency to reviewed versions. 2. Generate a hash-locked requirements file, for example with `pip-compile --generate-hashes`. 3. Require hash verification during installation: ```bash python3 -m pip install --require-hashes -r requirements.txt ``` 4. Commit the lockfile to the project and review dependency updates before merging them. 5. Run the dependency in an isolated virtual environment or container with minimal filesystem and credential access. 6. Avoid exposing unrelated environment variables to the TTS subprocess. 7. Document that podcast text is provided to an external TTS component and may be transmitted to its service provider. 8. Where feasible, provide a reviewed offline TTS alternative for sensitive content. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (22)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Claiming TTS generation and billing integration when they may not exist is dangerous because it obscures the actual trust boundary and data flow. Users may assume audio generation and payment handling happen through vetted components, while the real implementation could bypass expected controls or fail open.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
Claiming TTS generation and billing integration when they may not exist is dangerous because it obscures the actual trust boundary and data flow. Users may assume audio generation and payment handling happen through vetted components, while the real implementation could bypass expected controls or fail open.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Claiming TTS generation and billing integration when they may not exist is dangerous because it obscures the actual trust boundary and data flow. Users may assume audio generation and payment handling happen through vetted components, while the real implementation could bypass expected controls or fail open.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
def synth(segs, output, custom_voice=None):
    has_tts = os.system("which edge-tts >/dev/null 2>&1") == 0
    if not has_tts:
        print("⚠️ edge-tts 未安装,运行: pip install edge-tts")
        print(f"📝 共 {len(segs)} 个片段待转换")
Confidence
85% confidence
Finding
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
if not tmps:
        return {"segments": len(segs), "output": None, "error": "no segments generated"}

    has_ffmpeg = os.system("which ffmpeg >/dev/null 2>&1") == 0
    if has_ffmpeg and len(tmps) > 1:
        lst = "/tmp/pod_list.txt"
        with open(lst, "w") as f:
Confidence
85% confidence
Finding
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Unvalidated Output Injection

High
Category
Output Handling
Content
with open(lst, "w") as f:
            for t in tmps:
                f.write(f"file '{t}'\n")
        subprocess.run(["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", lst, "-c", "copy", output],
                       capture_output=True, timeout=120)
    elif tmps:
        import shutil
Confidence
95% confidence
Finding
The script accepts an unvalidated output path and passes it to ffmpeg as the write target with `-y`, which forces overwrite. In a hosted agent environment, an attacker could choose sensitive writable paths to overwrite files, alter application state, or disrupt service operation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises shell, network, environment-variable, and file-write capable workflows but does not declare any explicit tool scope or permissions boundary. This increases the chance the skill is invoked with broader capabilities than necessary, enabling unintended network access, filesystem writes, or secret exposure if downstream scripts are compromised or behave unexpectedly.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The activation criteria are broad enough to match many generic text-transformation requests, which can cause the skill to trigger on content outside the user's intent. In context, this is more dangerous because the skill can invoke billing, shell commands, file writes, and external services, so over-triggering may lead to unintended charges or data processing.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill does not clearly disclose that user-provided content may be sent to an external TTS service during audio generation. This creates a privacy and compliance risk, especially if users submit proprietary, personal, or regulated text under the assumption processing is local.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The listed voice options are all zh-CN voices and the surrounding labels are in Chinese, which indicates the skill template is oriented to a single language/locale. Because the file does not state that this is an optional or region-specific template, it can be read as forcing a specific locale without user opt-in.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script retrieves SKILLPAY_API_KEY from the environment and sends it as the X-API-Key header on outbound requests. There is no visible warning, comment, or user-facing message informing operators that credentials will be consumed and sent to an external service.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The charge() function can initiate a real billing action against an external payment service using only a user_id and API key, with no built-in confirmation, authorization check, or user-notice safeguard in this code path. In a skill context that auto-processes user requests, this increases the risk of unintended or unauthorized charges if the function is invoked without explicit consent handling elsewhere.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script hard-codes Chinese TTS voices (`zh-CN-*`) and also uses Chinese user-facing strings such as `主播` and status/error messages. This imposes a specific language/locale on all users without offering a language choice or documenting that the skill is intentionally region-specific.

Context-Inappropriate Capability

Medium
Confidence
85% confidence
Finding
The manifest says the skill converts text into podcast scripts and TTS audio, but does not disclose that it will invoke OS commands and spawn subprocesses. While TTS generation itself is in-scope, shelling out to local binaries is a broader execution capability than the stated purpose implies and could matter for auditability and deployment trust.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
tmp = f"/tmp/pod_{i:03d}.mp3"
        v = voice_for(s["speaker"], custom_voice)
        try:
            subprocess.run(
                ["edge-tts", "--voice", v, "--text", s["text"], "--write-media", tmp],
                capture_output=True, timeout=60, check=True
            )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
with open(lst, "w") as f:
            for t in tmps:
                f.write(f"file '{t}'\n")
        subprocess.run(["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", lst, "-c", "copy", output],
                       capture_output=True, timeout=120)
    elif tmps:
        import shutil
Confidence
84% confidence
Finding
The ffmpeg call itself is made safely with an argument list, but the destination `output` comes directly from user input and is written without validation or restriction. In an agent or service context this can overwrite arbitrary files writable by the process, enabling file clobbering or corruption of application data.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The natural-language templates and fallback strings are entirely in Chinese, so the skill always generates Chinese podcast scripts regardless of user preference. This is a language-policy issue because the file provides no opt-in, locale selection, or justification for restricting output to Chinese.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The formatted user-facing output is hardcoded in Chinese, which imposes a specific language choice on all users. The file does not offer a language option or explain a justified locale restriction, so this is a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The documentation states that Host A uses a female voice and Host B uses a male voice as a fixed default. This imposes a presentation/locale-style choice in natural language without indicating that users can select alternatives, which may conflict with organizational policies favoring user choice for such settings.

Missing User Warnings

Low
Confidence
85% confidence
Finding
The balance() function transmits a user identifier to an external billing service without any visible minimization, consent, or authorization checks in this file. While expected for billing functionality, it still creates a privacy and misuse risk if arbitrary user IDs can be queried or if users are not informed that their identifier is sent off-platform.

Missing User Warnings

Low
Confidence
87% confidence
Finding
The payment_link() function sends user_id and billing-related metadata to an external payment provider without any visible access control or disclosure in this code. Although generating a payment link is less sensitive than charging directly, it can still expose user data and enable phishing-like misuse or unauthorized payment requests if called on behalf of other users.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This code saves generation history, including titles, timestamps, durations, and cost data, to a persistent JSON file under the user's home directory. While the behavior is visible in code, there is no confirmation prompt, user-facing notice, or explanatory comment/docstring near the write operation to disclose that user-derived metadata is being stored locally.

Static analysis

No suspicious patterns detected.