Back to skill

Security audit

Podcast To Script

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent podcast-transcription purpose, but it can fetch unrestricted URLs and install unpinned code into the user's Python environment, so it should be reviewed before installation.

Install only if you are comfortable letting the skill fetch podcast URLs and write episode outputs locally. Run it in an isolated environment, avoid untrusted feed/audio URLs, do not run preflight --install or tests on a sensitive machine unless you accept unpinned package/model downloads, and prefer preinstalling pinned dependencies in a virtual environment.

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/transcribe.py:74
Finding

<![CDATA[Unrestricted URL Retrieval Enables SSRF and Local Resource Access]]>

Content
View full analysis
bytes: handlers = [] if proxy: p = f"http://{proxy}" handlers.append(urllib.request.ProxyHandler({"http": p, "https": p})) opener = urllib.request.build_opener(*handlers) req = urllib.request.Request(url, headers=UA) with opener.open(req, timeout=timeout) as r: return r.read() def download(url: str, dest: Path, proxy: str | None = None) -> None: if dest.exists() and dest.stat().st_size > 0: print(f"[cache] {dest.name} already downloaded") return print(f"[download] {url[:100]}...") data = http_get(url, proxy=proxy, timeout=300) dest.write_bytes(data) print(f"[download] saved {dest} ({len(data)/1e6:.1f} MB)") ``` The direct-audio path passes an untrusted URL directly to the vulnerable retrieval function: ```python if args.audio: title = args.title or Path(urllib.parse.urlparse(args.audio).path).stem or "episode" slug = args.slug or slugify(title) out_dir = Path(args.out) if args.out else default_out_dir(slug) out_dir.mkdir(parents=True, exist_ok=True) audio_path = out_dir / f"raw{audio_ext_from_url(args.audio)}" download(args.audio, audio_path, proxy=args.proxy) transcribe_downloaded_audio(audio_path, out_dir, args) print(f"[dir] {out_dir}") return ``` Bare feed URLs and URLs supplied by RSS content are also fetched without destination validation: ```python else: print(f"[rss] treating input as a feed URL: {url[:100]}") resolved = {"title": None, "feed": url} feed_xml = http_get(resolved["feed"], proxy=args.proxy) title = args.title or resolved["title"] if title: ...[truncated 3301 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/pipeline.py:47
Finding

<![CDATA[Unpinned Runtime Dependency Installation Creates Supply-Chain Exposure]]>

Content
View full analysis
bool: """Install faster-whisper into the current interpreter: uv first, pip fallback.""" import shutil import subprocess candidates = [] uv = shutil.which("uv") if uv: candidates.append([uv, "pip", "install", "--python", sys.executable, "faster-whisper"]) candidates.append([sys.executable, "-m", "pip", "install", "--user", "faster-whisper"]) for cmd in candidates: print(f"[install] {' '.join(cmd)}", flush=True) if subprocess.run(cmd).returncode == 0: return True print(f"[install] failed, trying next installer...") return False def _prefetch_model(model_name: str = "small.en") -> None: """Warm the HF cache so the first transcription does not stall on a download.""" try: from faster_whisper import WhisperModel print(f"[install] pre-downloading ASR model '{model_name}' " f"(first time ~480MB) ...", flush=True) WhisperModel(model_name, device="cpu", compute_type="int8") ``` The default test suite invokes the installation and model-warmup path: ```python def test_preflight(): print("\n== preflight ==") r = run(PIPELINE, "preflight") check("preflight runs (exit 0 or warn-only)", "preflight" in r.stdout and r.returncode in (0, 1)) check("preflight checks python+deps", "python" in r.stdout and "faster_whisper" in r.stdout) r = run(PIPELINE, "preflight", "--install") ok_msg = "ASR model" in r.stdout or "just installed" in r.stdout check("preflight --install (deps present -> model warmup)", r.returncode == 0 and ok_msg, r.stdout.strip().splitlines()[-1 ...[truncated 2774 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (20)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill clearly instructs the agent to use Python scripts that fetch remote podcast/audio URLs, write transcripts and generated content to disk, inspect environment/dependencies, and run shell commands, yet it does not declare an explicit tool/permission scope. That creates an over-broad execution surface where a caller or hosting platform may not understand that network, filesystem, and shell access are required, increasing the risk of unintended remote fetches, local file modification, and command execution in sensitive environments.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest description states the skill produces a "Chinese two-host script package," and later instructions consistently require Chinese output and Chinese formatting. This imposes a language constraint by default without indicating user choice or opt-in, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Line L49 states 'Chinese throughout' as a blanket requirement. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly justified, which this file does not do.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The spec explicitly requires Chinese role names only and rejects any speaker name outside that whitelist. Elsewhere it also mandates conversational Chinese, which together imposes a fixed language/locale on skill output rather than offering user choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code hard-codes Chinese speaker roles and validates Chinese-specific content formats, while also emitting user-facing Chinese-only error/output strings elsewhere in the file. For a general-purpose checker, this enforces a specific language/locale rather than offering a user choice or clearly documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The argparse description, usage errors, and validation output shown to users are all in Chinese, with no option to select another language. This is a natural-language policy concern because the skill forces a specific locale in its user-facing interaction rather than offering choice or documenting a justified locale limitation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The segment-matching regex hard-codes Chinese header syntax (第 N 段) and later commands reject files that do not follow that format. This imposes a specific locale/language convention in behavior rather than merely documenting an optional format, and there is no opt-in or alternative locale path.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The preflight flow can install Python packages into the current interpreter context, including a --user pip fallback, which modifies the execution environment and introduces third-party code. In this skill's context, that increases supply-chain and environment-integrity risk, especially if the skill is run on a shared workstation, automation host, or agent runtime with broader permissions.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
88% confidence
Finding

The preflight routine can execute package-install commands in the host environment via subprocess, which changes system state and pulls code from package repositories. In a skill context, this is risky because running an assistant-provided workflow should not silently bootstrap executable dependencies into the current interpreter or user environment without strong isolation and trust controls.

Content

Scanner excerpt · scripts/pipeline.py (reported line 58)May include surrounding context.

python
candidates.append([sys.executable, "-m", "pip", "install", "--user", "faster-whisper"])
    for cmd in candidates:
        print(f"[install] {' '.join(cmd)}", flush=True)
        if subprocess.run(cmd).returncode == 0:
            return True
        print(f"[install] failed, trying next installer...")
    return False

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/pipeline.py (reported line 88)May include surrounding context.

python
for pkg, pip_name in (("numpy", "numpy"), ("faster_whisper", "faster-whisper")):
        try:
            mod = __import__(pkg)
            print(f"[OK] {pkg} {getattr(mod, '__version__', '?')}")
        except ImportError:
            if getattr(args, "install", False) and pkg == "faster_whisper":

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/pipeline.py (reported line 95)May include surrounding context.

python
for pkg, pip_name in (("numpy", "numpy"), ("faster_whisper", "faster-whisper")):
        try:
            mod = __import__(pkg)
            print(f"[OK] {pkg} {getattr(mod, '__version__', '?')}")
        except ImportError:
            if getattr(args, "install", False) and pkg == "faster_whisper":

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The verifier instructs that outline.md must use a Chinese header format (## 第 N 段 · 子标题), which enforces a locale-specific structure on users. The file does not provide a language choice or indicate that this restriction is a justified region-specific requirement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The usage text sets the default model to "small.en" and the default language hint to "en", which makes English the default behavior for all users. This is a natural-language locale policy issue because the tool imposes a specific language unless the user notices and overrides it.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The parser defaults --model to small.en and --lang to en, reinforcing an English-only default at runtime. This forces a locale choice in code rather than offering user choice or auto-detection.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/run_tests.py (reported line 50)May include surrounding context.

python
def run(tool, *args, env=None):
    return subprocess.run([sys.executable, str(tool), *args],
                          capture_output=True, text=True, env=env)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This Python test file embeds and validates only Chinese-language script and notes formats, including Chinese headings, dialogue, and expected status text. Because SQP-3 applies to natural-language content in code files too, this can reflect a forced language/locale assumption without offering a user choice or documenting a justified regional scope.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/run_tests.py (reported line 343)May include surrounding context.

python
skip("asr e2e", "faster-whisper not installed")
        return
    audio = tmp / "t.m4a"
    subprocess.run(["say", "-o", str(audio), "Hello, this is a pipeline test."], check=True)
    out = tmp / "asr_ep"
    r = run(TRANSCRIBE, "--audio", f"file://{audio}", "--title", "ASR Test",
            "--model", "tiny.en", "--workers", "2", "--out", str(out))

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The manifest emphasizes a self-contained staged pipeline for podcast transcription and script generation, but this script also performs model cache warming by downloading a large external model artifact. That may be operationally convenient, yet it is a distinct capability beyond simply processing a user-provided podcast episode.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The test suite is configured to execute a validator from an external, user-specific path outside the skill repository. That creates a trust-boundary violation: running tests may execute arbitrary local code if that path exists or if an attacker influences PODCAST_PIPELINE_DIR, making the skill less self-contained and potentially causing unintended code execution during testing.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The section labeled 'downstream parity' iterates over content-bearing test cases, but unlike the checker tests above it never writes content to the temporary files before invoking either validator. This contradicts the apparent intent of validating parity on those cases, because the code is actually testing behavior on nonexistent or empty inputs rather than the declared fixtures.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/pipeline.py:41

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/run_tests.py:57