Back to skill

Security audit

HN Podcast Transcribe

Security checks for vulnerabilities and agentic risk

Overview

This podcast archiving skill is mostly coherent, but it can automatically fetch arbitrary feed-provided URLs and disables TLS checks in its fallback downloader, which warrants Review before installation.

Install only if you are comfortable with the skill making outbound requests to feed-provided URLs and processing downloaded media locally. Prefer using trusted HTTPS podcast feeds, avoid untrusted RSS URLs, run it in a restricted network/container if possible, and remove the yt-dlp --no-check-certificates behavior before using it for unattended cron runs.

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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/fetch_episodes.py:38
Finding

Unrestricted RSS and enclosure URL fetching permits server-side request forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_episodes.py:38-77
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: High

python
def parse_rss(url: str) -> list[dict]:
    resp = requests.get(url, timeout=30, headers={"User-Agent": UA})
    resp.raise_for_status()
    root = ElementTree.fromstring(resp.content)
    episodes = []
    for item in root.iter("item"):
        title = item.findtext("title", "").strip()
        pub_date = item.findtext("pubDate", "").strip()
        desc = item.findtext("description", "").strip()
        enc = item.find("enclosure")
        audio_url = enc.get("url") if enc is not None else None
        link = item.findtext("link", "").strip()
        episodes.append({
            "title": title,
            "pub_date": pub_date,
            "description": desc,
            "audio_url": audio_url,
            "link": link,
        })
    return episodes


def existing_episodes(archive_dir: Path) -> set[str]:
    slugs = set()
    if not archive_dir.exists():
        return slugs
    for d in archive_dir.iterdir():
        if d.is_dir() and (d / "episode.json").exists():
            slugs.add(d.name)
    return slugs


def download_direct(url: str, dest: Path) -> bool:
    """Try direct HTTP download."""
    try:
        resp = requests.get(url, timeout=120, stream=True, headers={"User-Agent": UA})
        resp.raise_for_status()
        ct = resp.headers.get("content-type", "")
        if "audio" in ct or "octet-stream" in ct or url.endswith(".mp3"):
            with open(dest, "wb") as f:
                for chunk in resp.iter_content(chunk_size=8192):
                    f.write(chunk)
            return True
    except Exception:
        pass
    return False

Technical Analysis

The Skill accepts an arbitrary RSS URL through --rss or HN_PODCAST_RSS and sends an HTTP request to it with ...[truncated 1926 chars]

Remediation
View remediation

Remediation Suggestions

  • Permit only https URLs unless insecure HTTP support is explicitly required.
  • Resolve destination hostnames before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges for both IPv4 and IPv6.
  • Disable automatic redirects or validate every redirect destination before following it.
  • Re-resolve and revalidate the hostname at connection time to reduce DNS rebinding exposure.
  • Consider requiring enclosure hosts to match the RSS feed host or an explicit user-approved allowlist.
  • Apply the same validation policy to the RSS request, direct audio download, and yt-dlp fallback.
  • Run download processing in a sandbox with restricted network access where practical.
  • Enforce maximum RSS and audio response sizes to limit resource exhaustion from hostile endpoints.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_episodes.py:82
Finding

TLS certificate verification is disabled for fallback audio downloads

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_episodes.py:82-96
Vulnerability Type: Improper certificate validation
Risk Level: Medium

python
def download_ytdlp(url: str, dest: Path) -> bool:
    """Try yt-dlp download (handles some Cloudflare cases)."""
    if not shutil.which("yt-dlp"):
        return False
    try:
        result = subprocess.run(
            ["yt-dlp", "--no-check-certificates", "-f", "bestaudio",
             "--extractor-args", "generic:impersonate",
             "-o", str(dest), url],
            capture_output=True, text=True, timeout=120
        )
        return result.returncode == 0 and dest.exists()
    except Exception:
        return False

Technical Analysis

The --no-check-certificates option instructs yt-dlp not to verify the remote server's TLS certificate. This defeats HTTPS server authentication and allows a network-positioned attacker to impersonate the podcast host.

The downloaded file is subsequently handled as media and may be passed to FFmpeg and Whisper. Although the command is constructed as an argument list rather than through a shell, preventing direct shell injection, disabling certificate verification still removes an important integrity boundary for externally supplied media.

Attack Path

  1. A direct audio download fails, causing the Skill to invoke the yt-dlp fallback.
  2. The fallback connects with certificate verification disabled.
  3. An attacker capable of intercepting or redirecting network traffic impersonates the intended media server.
  4. The attacker supplies substituted audio or malformed media content.
  5. The Skill saves the substituted file and the pipeline automatically processes it using local media-decoding and transcription components.

Impact Assessment

The attacker can compromise the confidentiality and integrity of the fallback network connection and replace podcast content. At minimum, t ...[truncated 335 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove --no-check-certificates and fail securely when certificate validation fails.
  • If a particular service requires a nonstandard trust configuration, provide a narrowly scoped trusted CA bundle instead of disabling verification globally.
  • Log certificate failures clearly and require explicit user intervention rather than silently weakening transport security.
  • Validate fallback URLs using the same scheme, hostname, IP-range, and redirect policy used for direct downloads.
  • Keep yt-dlp, FFmpeg, and related media libraries patched and process untrusted media in a sandbox with restricted filesystem and network permissions.
  • Consider verifying expected file size, MIME type, and publisher-provided integrity information when available.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:119
Finding

Third-party dependencies are installed without pinned versions or integrity hashes

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:119-124
Vulnerability Type: Unpinned third-party dependencies
Risk Level: Medium

markdown
## Requirements

- Python 3.10+
- `openai-whisper` (`pip install openai-whisper`)
- `requests` (`pip install requests`)
- `static-ffmpeg` (`pip install static-ffmpeg`) — auto-provides ffmpeg
- `yt-dlp` (optional, for fallback downloads)
- Whisper models auto-download to `~/.cache/whisper` on first use

Technical Analysis

The installation instructions identify packages only by name. They do not pin exact versions, provide cryptographic hashes, or define a locked transitive dependency set. Installations are therefore not reproducible and can silently obtain changed future releases.

Python packages may execute installation-time code and are imported by the Skill at runtime. In particular, static_ffmpeg is imported and allowed to modify executable search paths, while Whisper and its dependency stack process untrusted media. The absence of version and integrity controls increases supply-chain risk.

The audit did not identify typosquatted package names or prove that any listed package is malicious. The finding concerns insufficient dependency integrity controls.

Attack Path

  1. A user follows the documented installation commands.
  2. The package index supplies whatever package version is current at installation time.
  3. A compromised release, compromised package account, or unsafe future version is downloaded without a lockfile or hash mismatch stopping installation.
  4. Package installation hooks or imported package code execute with the installing user's privileges.
  5. The compromised component can affect transcription, executable resolution, file access, or network behavior.

Impact Assessment

A compromised dependency could execute with the same operating-system privileges as the user running the Skill. This could expose archive data and other ...[truncated 284 chars]

Remediation
View remediation

Remediation Suggestions

  • Add a dependency lockfile containing exact direct and transitive package versions.
  • Generate and verify package hashes, and install with pip --require-hashes.
  • Use an approved package index and require TLS certificate verification.
  • Separate optional dependencies such as yt-dlp into an explicitly versioned optional dependency group.
  • Document supported versions of FFmpeg and Whisper model artifacts.
  • Use automated dependency scanning and controlled update reviews.
  • Install dependencies inside an isolated virtual environment or container rather than into a privileged global Python environment.
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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 (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises a limited archive/transcription workflow but also references undeclared external download behavior via yt-dlp/subprocess. Even if framed as a fallback, invoking external tooling against arbitrary RSS-linked media increases attack surface and can bypass reviewer expectations about how downloads are performed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises a limited archive/transcription workflow but also references undeclared external download behavior via yt-dlp/subprocess. Even if framed as a fallback, invoking external tooling against arbitrary RSS-linked media increases attack surface and can bypass reviewer expectations about how downloads are performed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises a limited archive/transcription workflow but also references undeclared external download behavior via yt-dlp/subprocess. Even if framed as a fallback, invoking external tooling against arbitrary RSS-linked media increases attack surface and can bypass reviewer expectations about how downloads are performed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill advertises a limited archive/transcription workflow but also references undeclared external download behavior via yt-dlp/subprocess. Even if framed as a fallback, invoking external tooling against arbitrary RSS-linked media increases attack surface and can bypass reviewer expectations about how downloads are performed.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
96% confidence
Finding

Using yt-dlp with --no-check-certificates disables TLS certificate validation for downloads from attacker-controlled or compromised hosts referenced by the RSS feed. In this skill, feeds and enclosure URLs are network-derived and may be arbitrary, so this weakens transport security and can enable man-in-the-middle tampering, causing the tool to fetch malicious or altered content while believing the transfer succeeded.

Content

Scanner excerpt · scripts/fetch_episodes.py (reported line 92)May include surrounding context.

python
return False
    try:
        result = subprocess.run(
            ["yt-dlp", "--no-check-certificates", "-f", "bestaudio",
             "--extractor-args", "generic:impersonate",
             "-o", str(dest), url],
            capture_output=True, text=True, timeout=120

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill describes operations that require network access, shell execution, environment variable use, and file read/write, but it does not declare any explicit tool scope or permissions. This weakens least-privilege controls and can let a host agent grant broader capabilities than users or reviewers expect, especially because the workflow includes downloading remote content and invoking external tools like Python and yt-dlp.

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 · scripts/fetch_episodes.py (reported line 91)May include surrounding context.

python
if not shutil.which("yt-dlp"):
        return False
    try:
        result = subprocess.run(
            ["yt-dlp", "--no-check-certificates", "-f", "bestaudio",
             "--extractor-args", "generic:impersonate",
             "-o", str(dest), url],

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The file docstring at L02 describes building a searchable index, which could be read as a read/compute operation, but the implementation persists a new JSON file into the archive directory. While this is plausibly part of indexing, it is still a behavior-changing write operation not stated in the description of this file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

This code writes archive_index.json into the archive directory, which modifies user data on disk. Although there is a print after the write succeeds, there is no confirmation prompt or warning before the modification, and the module docstring does not explicitly disclose that running the script will create or overwrite this file.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.