Back to skill

Security audit

Ai Podcast Pipeline

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its podcast-production purpose, but thumbnail generation can run another local skill and pass it the full environment including credentials.

Review this skill before installing if your agent environment contains valuable tokens or private workspace access. Use --no-image or isolate the run unless you trust and have reviewed the referenced nano-banana-pro generator, and only run TTS on scripts you are comfortable sending to Google Gemini.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T08 · Insecure Dependencies

Warning
Location
scripts/build_podcast_assets.py:25
Finding

Unpinned External Script Receives the Complete Process Environment

Content
View full analysis

Vulnerability Details

File Location: scripts/build_podcast_assets.py, lines 25–34 and 115–133
Vulnerability Type: Untrusted external executable dependency with excessive environment inheritance
Risk Level: Medium

Vulnerable Code

python
SKILL_DIR = Path(__file__).resolve().parents[1]
WORKSPACE_DIR = SKILL_DIR.parents[1]

# Prefer explicit env override, then common local defaults.
QUARTZ_ROOT = Path(
    os.environ.get(
        'QUARTZ_ROOT',
        '/home/tw2/Documents/n8n/data/shared/syn/8.quartz',
    )
)
NANO_SCRIPT = WORKSPACE_DIR / 'skills/nano-banana-pro/scripts/generate_image.py'
FONT_BOLD = WORKSPACE_DIR / 'skills/youtube-editor/assets/fonts/Paperlogy-ExtraBold.ttf'
TG_MAX_BYTES = 20 * 1024 * 1024
python
def generate_bg_with_skill(prompt: str, out_path: Path, resolution: str = '1K'):
    api_key = os.environ.get('GEMINI_API_KEY') or os.environ.get('NANO_BANANA_KEY')
    if not api_key:
        raise RuntimeError('Set GEMINI_API_KEY or NANO_BANANA_KEY')
    if not NANO_SCRIPT.exists():
        raise FileNotFoundError(f'NANO script missing: {NANO_SCRIPT}')

    # Security hardening: provide API key via environment, not CLI args
    # (prevents exposure via process listings).
    env = os.environ.copy()
    env['GEMINI_API_KEY'] = api_key

    cmd = [
        'uv', 'run', str(NANO_SCRIPT),
        '--prompt', prompt,
        '--filename', str(out_path),
        '--resolution', resolution,
    ]
    subprocess.run(cmd, check=True, env=env, timeout=600)

Technical Analysis

Thumbnail generation delegates execution to skills/nano-banana-pro/scripts/generate_image.py, which is outside this project and was therefore not included in the audited code. The dependency is not version-pinned, bundled, or checked against an expected cryptographic hash before execution.

The child process receives a copy of the entire parent environment through os.environ.copy(). This includes the explicitly inserted GEMINI_API_KEY and ma ...[truncated 1900 chars]

Remediation
View remediation

Remediation Suggestions

  1. Establish dependency integrity

    • Bundle the required generator into the reviewed project, or use a versioned package from a trusted registry.
    • Pin the dependency to an immutable version or commit.
    • Verify the script or package against an expected SHA-256 digest or signed release before execution.
  2. Use a minimal environment

    • Replace os.environ.copy() with an explicit allowlist containing only variables required by uv, Python, and the image generator.
    • Do not forward unrelated credentials or configuration.
    • Supply only one intended API-key variable and remove the alias when it is unnecessary.

    Example:

    python
    env = {
        "PATH": os.environ.get("PATH", ""),
        "HOME": os.environ.get("HOME", ""),
        "GEMINI_API_KEY": api_key,
    }
    
  3. Constrain execution

    • Run the image generator in a sandbox or container with restricted filesystem and network access.
    • Use a dedicated low-privilege account.
    • Limit writable directories to the intended output directory.
  4. Validate the dependency path

    • Resolve the path and ensure it is an expected regular file.
    • Reject symbolic links and unexpected ownership or writable permissions where practical.
    • Fail closed if integrity verification does not succeed.
  5. Document the trust boundary

    • State clearly that thumbnail generation executes an external sibling Skill not included in this repository.
    • Include that component in future audits before treating the complete workflow as trusted.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (23)

Tainted flow: 'req' from os.environ.get (line 99, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/gemini_multispeaker_tts.py (reported line 112)May include surrounding context.

python
last_err = None
    for attempt in range(1, retries + 1):
        try:
            with request.urlopen(req, timeout=timeout_seconds) as resp:
                data = json.loads(resp.read().decode("utf-8"))
            try:
                b64 = data["candidates"][0]["content"]["parts"][0]["inlineData"]["data"]

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The description presents a broader podcast-production pipeline centered on script creation, multi-speaker TTS audio generation, subtitle fixing, and edition-length support. The supplied code does not implement those core capabilities. Instead, it focuses on packaging/marketing assets: extracting topics from QuickView notes, writing YouTube copy, generating a thumbnail background image through another script, compositing thumbnail and Telegram card images, and making a short static-image MP4 preview. While thumbnail/MP4 packaging and YouTube metadata generation partially align with the description, the major claimed functions—podcast script writing, TTS audio creation, subtitle timing/render fixes, and full/compressed edition handling—are absent. The code also produces Telegram preview assets, which are undeclared but related. Overall, the actual behavior is materially narrower and different from the declared purpose.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/build_podcast_assets.py (reported line 124)May include surrounding context.

python
# Security hardening: provide API key via environment, not CLI args
    # (prevents exposure via process listings).
    env = os.environ.copy()
    env['GEMINI_API_KEY'] = api_key

    cmd = [

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill advertises and instructs use of sensitive capabilities including environment-variable access, filesystem reads/writes, network access, and shell/subprocess execution, but it does not declare any explicit tool scope or permission boundary. In an agent setting, this weakens least-privilege controls and can allow broader-than-expected execution, especially since the document contains runnable shell commands and external API usage.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The natural-language instructions, labels, and workflow assumptions are entirely in Korean, including Korean-specific subtitle generation and Korean prompt template usage. There is no user opt-in, language alternative, or explicit justification that this skill is intentionally limited to a Korean-only context.

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/build_dualvoice_audio.py (reported line 21)May include surrounding context.

python
def run_cmd(cmd: list[str], timeout: int = 1800):
    subprocess.run(cmd, check=True, timeout=timeout)


def main():

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/build_korean_srt.py (reported line 16)May include surrounding context.

python
def audio_duration_sec(path: Path) -> float:
    out = subprocess.check_output(
        [
            "ffprobe",
            "-v",

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/render_subtitled_video.py (reported line 58)May include surrounding context.

python
def audio_duration_sec(path: Path) -> float:
    out = subprocess.check_output(
        [
            "ffprobe",
            "-v",

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code replaces speaker names with Korean labels ("캘리" and "닉") unconditionally, which forces a specific locale in generated subtitles. This is a natural-language policy concern because the skill does not provide any user opt-in or configuration for language selection.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The generated YouTube titles and description are written entirely in Korean, with no option for the user to choose another language or locale. This is a natural-language policy concern because the skill forces a specific language in its user-facing output rather than offering opt-in or documenting a justified region-specific limitation.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest describes a podcast packaging pipeline, including thumbnail and MP4 generation, but this implementation achieves that by spawning uv run on another skill's script and later using ffmpeg via subprocesses. While media generation is in-scope, arbitrary local process execution and cross-skill script invocation are more powerful capabilities than the stated purpose itself and are not explicitly declared in the manifest text.

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/build_podcast_assets.py (reported line 133)May include surrounding context.

python
'--filename', str(out_path),
        '--resolution', resolution,
    ]
    subprocess.run(cmd, check=True, env=env, timeout=600)


def _fonts():

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The thumbnail and Telegram card rendering embed Korean text directly into user-facing assets, and the prompt explicitly targets a Korean AI discussion show. Because the skill does not expose a language selection or justify the locale restriction, it imposes a fixed language/locale in generated outputs.

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/build_podcast_assets.py (reported line 215)May include surrounding context.

python
'-movflags', '+faststart',
        str(out_mp4),
    ]
    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=600)


def build_telegram_preview_mp4(card_png: Path, out_mp4: Path):

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script sends full dialogue content to Google's Gemini API but provides no runtime disclosure or consent prompt, which can lead users to unknowingly upload proprietary, personal, or embargoed script material to a third-party service. In a podcast-production pipeline, source notes and generated scripts may contain sensitive business content, making silent transmission a meaningful privacy and data-governance issue.

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/gemini_multispeaker_tts.py (reported line 147)May include surrounding context.

python
pcm.write_bytes(pcm_bytes)

    subprocess.run(
        ["ffmpeg", "-y", "-f", "s16le", "-ar", "24000", "-ac", "1", "-i", str(pcm), str(wav)],
        check=True,
        stdout=subprocess.DEVNULL,

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/gemini_multispeaker_tts.py (reported line 154)May include surrounding context.

python
stderr=subprocess.DEVNULL,
        timeout=600,
    )
    subprocess.run(
        ["ffmpeg", "-y", "-i", str(wav), "-codec:a", "libmp3lame", "-q:a", "4", str(mp3)],
        check=True,
        stdout=subprocess.DEVNULL,

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/render_subtitled_video.py (reported line 156)May include surrounding context.

python
str(output),
    ]

    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=1800)

    size = human_size(output.stat().st_size)
    dur = probe_duration(output)

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The heading explicitly labels the guideline as Korean '(KR)', indicating a fixed locale. The file does not offer an alternative language option or explain why Korean is required, which can violate language/locale choice policy for general-purpose skills or references.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
73% confidence
Finding

The document specifies saving generated scripts to archive/YYMMDD_*.txt, and later steps generate SRT, MP3, and MP4 outputs. Because this markdown serves as procedural guidance, it should disclose that the workflow writes multiple local files affecting user storage and workspace contents.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This markdown file includes commands that set GEMINI_API_KEY from another environment variable, which involves use of sensitive credentials. The runbook does not include any warning about protecting API keys, avoiding shell history leaks, or ensuring the key is set securely.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The manifest says the skill creates podcast packages from QuickView trend notes, but the code additionally supports resolving an arbitrary HTTPS-style source into a local QUARTZ_ROOT markdown path controlled by an environment variable. That local repository mapping behavior is not part of the user-facing packaging purpose and gives the skill a broader content-access mechanism than the manifest describes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The script writes raw PCM and WAV files to disk and later deletes some of them by default, which affects the user's filesystem. While this behavior is part of audio generation, the script does not visibly warn users that temporary files will be created and removed during execution.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.