T09 · Insecure Skill Coding Practices
- Location
scripts/dubber.py:227- Finding
Unsafe Temporary Python File Is Written and Executed
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to perform Douyin/TikTok dubbing, but it handles account cookies and transcript data with under-disclosed remote processing and weak temporary-file practices.
Install only after reviewing the data exposure tradeoffs. Use a throwaway Douyin account, keep the cookie file isolated, avoid confidential videos or transcripts, prefer scoped/rotated ElevenLabs keys, run in a dedicated virtual environment, and be aware that selected TTS providers and AI translation may receive subtitle text despite the local-only wording.
scripts/dubber.py:227Unsafe Temporary Python File Is Written and Executed
scripts/dubber.py:417Predictable Shared Translation Files Permit Data Exposure and Translation Poisoning
scripts/dubber.py:500ElevenLabs API Key Is Collected Through Echoed Terminal Input
SKILL.md:83Third-Party Python and Browser Dependencies Are Installed Without Version or Integrity Pinning
The skill claims a 'fully local pipeline' but its documented behavior relies on external services such as gTTS, Edge TTS, ElevenLabs, and an external translation agent, and the finding notes undeclared downloader fallback behavior. This mismatch is dangerous because users may provide sensitive video content, subtitles, or credentials under the false assumption that data never leaves the machine.
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
dge TTS — Tự nhiên hơn, miễn phí
pip install edge-tts
vi-VN-HoaiMyNeural (Nữ) / vi-VN-NamMinhNeural (Nam)Export cookies từ Chrome/Edge khi đang đăng nhập Douyin:
Cách 1 — Extension EditThisCookie:
douyin_cookies.txt trong thư mục skillCách 2 — DevTools (không cần extension):
www.douyin.comsessionid, sid_tt, ttwid, uid_tt,The Playwright helper subprocess inherits the entire parent environment via {**os.environ, ...} instead of a minimal allowlist. That exposes unrelated secrets such as API keys, tokens, and credentials to the child process and any imported dependencies it runs. In this skill, launching browser automation code from a generated script increases the chance that unnecessary secrets are accessible where they are not needed.
result = subprocess.run(
[PYTHON311, str(pw_script), url, cookie_str],
capture_output=True, timeout=60,
env={**os.environ, "PYTHONUTF8": "1"}
)
stdout = (result.stdout or b"").decode("utf-8", errors="replace").strip()
stderr = (result.stderr or b"").decode("utf-8", errors="replace")
File contents flow to a network sink. This may indicate data exfiltration of sensitive files.
req.add_header("Referer", "https://www.douyin.com/")
req.add_header("Accept", "*/*")
try:
with _r.urlopen(req, timeout=120) as resp:
total = int(resp.headers.get("Content-Length", 0))
done = 0
with open(out_path, "wb") as f:
Subtitle text is sent to ElevenLabs over the network along with an API key, contradicting the claimed fully local pipeline and exposing user content to a third-party service. If the subtitles contain sensitive or copyrighted material, this creates confidentiality and compliance risk. In this skill, automated dubbing of downloaded media makes external transmission especially relevant because users may assume all processing stays local.
req.add_header("xi-api-key", api_key)
req.add_header("Content-Type", "application/json")
req.add_header("Accept", "audio/mpeg")
with urllib.request.urlopen(req, timeout=30) as resp:
out_path.write_bytes(resp.read())
The Edge TTS subprocess also inherits the full process environment while executing dynamically generated Python code and third-party libraries. This unnecessarily exposes all ambient credentials to code that only needs a tiny runtime context, increasing damage if the dependency chain is compromised.
_time.sleep(delay)
r = subprocess.run([_sys.executable, "-c", script],
capture_output=True, text=True,
env={**os.environ, "PYTHONUTF8": "1"})
if r.returncode == 0 and out_path.exists() and out_path.stat().st_size > 100:
return
print(f"[TTS/Edge] Retry {attempt+1}/5…")
Generated subtitle content is incorporated into an ASS file and then consumed by ffmpeg/libass without strong validation. This creates an untrusted-output-to-interpreter boundary where malformed or adversarial text can trigger renderer issues, processing failures, or exploitation of parser bugs in the media stack. The risk is elevated because the text originates from transcription/translation and may be attacker-influenced through the source media.
ass_path_fwd = str(ass_path).replace("\\", "/").replace(":", "\\:")
sub_filter = f"subtitles='{ass_path_fwd}':fontsdir='C\\:/Windows/Fonts'"
subprocess.run([
FFMPEG, "-y",
"-i", str(video), "-i", str(tts_track),
"-filter_complex",
The skill declares significant capabilities—reading environment variables, handling sensitive files, writing outputs, invoking shell tools, and making network requests—but does not scope or declare permitted tools. In an agent environment, this weakens containment and informed consent, making it easier for the skill to access credentials or perform external actions beyond what a user may expect.
The user-facing instructions and examples are written entirely in Vietnamese, and the documented voices and defaults are centered on Vietnamese output without presenting a language-choice policy or opt-in. Under the policy rule, forcing a specific language or locale in natural-language instructions can be a violation unless the constraint is explicitly justified as region-specific or optional.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if progress and sub:
progress.update(sub=sub)
print(f"[CMD] {' '.join(str(c) for c in cmd)}", flush=True)
result = subprocess.run(cmd, check=True, **kw)
return result
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if progress: progress.update(sub="Playwright: dang load trang...", pct=5)
try:
result = subprocess.run(
[PYTHON311, str(pw_script), url, cookie_str],
capture_output=True, timeout=60,
env={**os.environ, "PYTHONUTF8": "1"}
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
if progress: progress.update(sub="Playwright: dang load trang...", pct=5)
try:
result = subprocess.run(
[PYTHON311, str(pw_script), url, cookie_str],
capture_output=True, timeout=60,
env={**os.environ, "PYTHONUTF8": "1"}
The skill description claims a fully local pipeline, but the code uses gTTS and ElevenLabs, both of which require sending text to external services. This is a security-relevant integrity and privacy issue because users can be misled about where their data is processed.
The code transmits subtitle text to ElevenLabs over HTTPS, which is external data sharing. In a tool advertised as fully local, this materially increases privacy risk because content from user videos is sent to a third party along with authentication credentials.
"""ElevenLabs TTS — hỗ trợ model, stability, language_code tùy chỉnh."""
import unicodedata, urllib.request, json as _json
text_clean = unicodedata.normalize("NFC", text).strip() or "..."
url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}"
body = {
"text": text_clean,
"model_id": model_id,
The Edge TTS path is not local and explicitly patches fake headers to access a remote service, directly contradicting the skill's 'fully local' framing. Misrepresenting remote processing and disguising request origin both increase trust and compliance concerns.
This executes dynamically generated Python code via python -c, which is a risky pattern because any future interpolation mistake can become arbitrary code execution. Even though current interpolation uses repr() for text and voice, the design relies on constructing executable code strings and inherits environment variables into the child, increasing blast radius if the embedded code or dependencies are compromised.
# Delay tăng dần: lần 1 = 2-4s, retry = 5-10s để tránh rate limit
delay = 2.0 + random.uniform(1.0, 2.0) if attempt == 0 else 5.0 + random.uniform(2.0, 5.0)
_time.sleep(delay)
r = subprocess.run([_sys.executable, "-c", script],
capture_output=True, text=True,
env={**os.environ, "PYTHONUTF8": "1"})
if r.returncode == 0 and out_path.exists() and out_path.stat().st_size > 100:
A dynamically assembled Python program is executed through python -c, which is a genuine dangerous sink for any tainted content. Although current values are repr()-escaped, this architecture is brittle and one modification could turn user text or voice parameters into code execution, making it an unsafe pattern in security-sensitive tooling.
# Delay tăng dần: lần 1 = 2-4s, retry = 5-10s để tránh rate limit
delay = 2.0 + random.uniform(1.0, 2.0) if attempt == 0 else 5.0 + random.uniform(2.0, 5.0)
_time.sleep(delay)
r = subprocess.run([_sys.executable, "-c", script],
capture_output=True, text=True,
env={**os.environ, "PYTHONUTF8": "1"})
if r.returncode == 0 and out_path.exists() and out_path.stat().st_size > 100:
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def stretch_audio(src: Path, dst: Path, target_duration_s: float):
"""Stretch/compress audio to fit target_duration_s using FFmpeg atempo."""
probe = subprocess.run([FFMPEG, "-i", str(src), "-hide_banner"],
capture_output=True, text=True)
dur_m = re.search(r"Duration: (\d+):(\d+):(\d+)\.(\d+)", probe.stderr)
if not dur_m:
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
while r > 2.0: filters.append("atempo=2.0"); r /= 2.0
while r < 0.5: filters.append("atempo=0.5"); r *= 2.0
filters.append(f"atempo={r:.4f}")
subprocess.run(
[FFMPEG, "-y", "-i", str(src), "-filter:a", ",".join(filters), str(dst)],
check=True, capture_output=True
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
progress.set_step("tts_4c", "Tạo silence base…")
progress.update(pct=0)
silence = workdir / "silence.mp3"
subprocess.run(
[FFMPEG, "-y", "-f", "lavfi", "-i",
f"anullsrc=r=44100:cl=stereo:d={total_dur+1}",
"-acodec", "libmp3lame", "-q:a", "4", str(silence)],
This ffmpeg call builds a filter_complex string from subtitle timing data and generated segment count without any hard limit. Malicious or malformed subtitle input could create extremely large filter graphs or pathological delays, leading to denial of service via CPU, memory, or process failure. In this skill context, user-supplied videos and subtitles make resource-abuse more plausible.
)
tts_track = workdir / "tts_track.mp3"
subprocess.run(
[FFMPEG, "-y"] + inputs + [
"-filter_complex", ";".join(filter_parts),
"-map", "[aout]",
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
progress.update(sub="Đang tạo ASS subtitle…", pct=5)
# Detect video resolution
probe = subprocess.run([FFMPEG, "-i", str(video), "-hide_banner"],
capture_output=True, text=True)
w, h = 1080, 1920 # default dọc
m = re.search(r"(\d{3,4})x(\d{3,4})", probe.stderr)
The final ffmpeg invocation embeds a dynamically constructed subtitles filter referencing a generated ASS file derived from translated text. Although shell injection is avoided, untrusted subtitle content is passed into ffmpeg/libass processing, creating output-injection and parser-abuse risk, and malformed ASS/text can break processing or trigger vulnerabilities in underlying media libraries if present. This is more dangerous here because subtitle text comes from external transcription/translation flows and is then burned into media automatically.
ass_path_fwd = str(ass_path).replace("\\", "/").replace(":", "\\:")
sub_filter = f"subtitles='{ass_path_fwd}':fontsdir='C\\:/Windows/Fonts'"
subprocess.run([
FFMPEG, "-y",
"-i", str(video), "-i", str(tts_track),
"-filter_complex",
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
prog.update(sub=f"Tải xong: {video_path.name}", pct=100)
# Lấy thời lượng video
probe = subprocess.run([FFMPEG, "-i", str(video_path), "-hide_banner"],
capture_output=True, text=True)
dur_m = re.search(r"Duration: (\d+):(\d+):(\d+)\.(\d+)", probe.stderr)
total_dur = 60.0
The embedded Playwright script hard-codes locale='zh-CN', which imposes a specific language/locale behavior regardless of user preference. This matches the policy category for language or locale constraints without an explicit opt-in or documented justification in the file.
No suspicious patterns detected.