Back to skill

Security audit

Youtube Transcriber

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its YouTube transcription purpose, but its script has unsafe path handling that can let a crafted output filename run local code or overwrite files.

Install only if you are comfortable reviewing or fixing the shell script first. Avoid using untrusted or unusual --out paths, do not run it on sensitive videos unless you accept audio being sent to OpenAI during fallback, and prefer pinned, verified dependency installs.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/transcribe.sh:117
Finding
Python Code Injection Through the User-Controlled Output Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/transcribe.sh:117-153` **Vulnerability Type**: Python source-code injection **Risk Level**: High ### Vulnerable Code ```bash python3 -c " import re, sys with open('$SUB_FILE', 'r') as f: content = f.read() # Remove VTT header content = re.sub(r'^WEBVTT.*?\n\n', '', content, flags=re.DOTALL) # Remove timestamps and cue metadata lines = [] seen = set() for line in content.split('\n'): line = line.strip() # Skip timestamp lines if re.match(r'^\d{2}:\d{2}', line): continue # Skip empty lines and position metadata if not line or line.startswith('align:') or line.startswith('position:'): continue # Remove inline timestamps line = re.sub(r'<\d{2}:\d{2}:\d{2}\.\d{3}>', '', line) # Remove HTML tags line = re.sub(r'<[^>]+>', '', line) # Deduplicate if line not in seen: seen.add(line) lines.append(line) text = ' '.join(lines) # Clean up whitespace text = re.sub(r'\s+', ' ', text).strip() with open('$OUT_FILE', 'w') as f: f.write(text + '\n') print(f'Transcript saved ({len(text)} chars)', file=sys.stderr) " 2>&1 ``` ### Technical Analysis The `--out` option is accepted as user-controlled input and stored in `OUT_FILE`. The value is subsequently interpolated directly into the source text passed to `python3 -c`. Shell quoting does not make this value safe in the generated Python program. An output path containing a single quote and additional Python syntax can terminate the string literal in: ```python with open('$OUT_FILE', 'w') as f: ``` The remainder of the supplied value can then introduce arbitrary Python statements. The injected Python runs with the same operating-system identity, filesystem permissions, environment, and network access as the transcription script. The vulnerability is reached when the subtitle fast path successfully downloads a nonempty VTT file. It does not require the OpenAI fallback path. ### ...[truncated 1016 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not interpolate paths or other external data into dynamically generated Python source. Pass both paths as positional arguments: ```bash python3 - "$SUB_FILE" "$OUT_FILE" <<'PY' import re import sys sub_file = sys.argv[1] out_file = sys.argv[2] with open(sub_file, "r", encoding="utf-8") as f: content = f.read() # Process the subtitle content here. with open(out_file, "w", encoding="utf-8") as f: f.write(content) PY ``` Additional hardening should include: 1. Validate that `--out` has an associated argument before shifting command-line parameters. 2. Use explicit text encodings and controlled error handling. 3. If output must be restricted to an approved directory, canonicalize the path and verify that it remains inside that directory. 4. Add regression tests containing quotes, newlines, command substitutions, spaces, and other metacharacters in output filenames. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/transcribe.sh:228
Finding
Predictable Shared Temporary Paths Allow Symlink-Based File Overwrites<![CDATA[ ## Vulnerability Details **File Location**: `scripts/transcribe.sh:65-67, 170-171, 188-201, 228-236` **Vulnerability Type**: Unsafe temporary-file handling and symlink following **Risk Level**: Medium ### Vulnerable Code ```bash if [[ -z "$OUT_FILE" ]]; then OUT_FILE="/tmp/yt_transcript_${VIDEO_ID}.txt" fi ``` ```bash AUDIO_RAW="/tmp/yt_audio_raw_${VIDEO_ID}" AUDIO_COMPRESSED="/tmp/yt_audio_${VIDEO_ID}.m4a" ``` ```bash if [[ "$AUDIO_SIZE" -gt "$MAX_SIZE" ]]; then echo ">>> Audio ${AUDIO_SIZE} bytes, compressing to mono ${AUDIO_BITRATE}kbps..." >&2 $FFMPEG -i "$AUDIO_ACTUAL" -b:a "${AUDIO_BITRATE}k" -ac 1 "$AUDIO_COMPRESSED" -y 2>/dev/null WHISPER_INPUT="$AUDIO_COMPRESSED" else WHISPER_INPUT="$AUDIO_ACTUAL" fi FINAL_SIZE=$(stat -f%z "$WHISPER_INPUT" 2>/dev/null || stat -c%s "$WHISPER_INPUT" 2>/dev/null) if [[ "$FINAL_SIZE" -gt "$MAX_SIZE" ]]; then echo ">>> Still too large, compressing to 32kbps..." >&2 $FFMPEG -i "$AUDIO_ACTUAL" -b:a 32k -ac 1 "${AUDIO_COMPRESSED}.low.m4a" -y 2>/dev/null WHISPER_INPUT="${AUDIO_COMPRESSED}.low.m4a" fi ``` ```bash echo "$RESULT" > "$OUT_FILE" if [[ "$KEEP_AUDIO" == false ]]; then rm -f "$AUDIO_RAW" "$AUDIO_ACTUAL" "$AUDIO_COMPRESSED" "${AUDIO_COMPRESSED}.low.m4a" 2>/dev/null else echo ">>> Audio kept at: ${WHISPER_INPUT}" >&2 fi ``` ### Technical Analysis Temporary and default output names are derived deterministically from the public YouTube video ID and placed directly in the shared `/tmp` directory. The script does not create these files atomically, verify ownership, reject symbolic links, or isolate them in a private temporary directory. A local attacker who can write to `/tmp` can predict the filenames before execution and create symbolic links at those locations. The shell redirection used to save the transcript follows symbolic links. The `ffmpeg -y` operations also explicitly permit replacing existing destinations. This is a time-of-check/time-of-use and unsafe-temporary-file flaw. ...[truncated 1362 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Create one private temporary directory per invocation and place all generated files inside it: ```bash WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/youtube-transcriber.XXXXXXXX") chmod 700 "$WORK_DIR" trap 'rm -rf -- "$WORK_DIR"' EXIT HUP INT TERM AUDIO_RAW="$WORK_DIR/audio_raw" AUDIO_COMPRESSED="$WORK_DIR/audio.m4a" ``` Additional controls should include: 1. Create the default transcript securely rather than writing to a predictable shared path. If the transcript must remain after exit, use `mktemp` or require an explicit destination. 2. Before writing a user-supplied destination, reject symbolic links and validate its parent directory. Where possible, use atomic creation with no-follow semantics. 3. Write completed output to a securely created temporary file and atomically rename it into place. 4. Quote every command variable consistently, including executable variables. 5. Install cleanup traps immediately after temporary-directory creation so failures and signals do not leave audio artifacts behind. 6. Set a restrictive `umask`, such as `umask 077`, before creating transcripts or audio files. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:15
Finding
Unpinned Package Installation Commands Create Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `README.md:15-30`; `SKILL.md:13-17, 80-82` **Vulnerability Type**: Unpinned third-party package installation **Risk Level**: Medium ### Vulnerable Code From `README.md`: ```bash npx clawhub install youtube-transcriber ``` ```markdown - **yt-dlp**: `brew install yt-dlp` or `pip install yt-dlp` ``` From `SKILL.md`: ```markdown - `yt-dlp` — `brew install yt-dlp` or `pip install yt-dlp` ``` ```markdown - **403 from YouTube**: Update yt-dlp (`pip install -U yt-dlp`) ``` ### Technical Analysis The documentation instructs users to install or execute mutable package releases without specifying reviewed versions or integrity hashes. `npx` can download and execute registry-provided package code during installation. The unversioned `pip install` and upgrade commands resolve whatever release is current at execution time. These instructions do not prove that the named packages are presently malicious. However, they make the effective installation payload dependent on mutable third-party package registries after the Skill has been reviewed. A compromised publisher account, registry compromise, malicious future release, or dependency-chain compromise could therefore introduce code that was not included in this audit. ### Attack Path 1. A package publisher account, distribution channel, or transitive dependency is compromised, or a future release becomes malicious. 2. A user follows the documented unpinned `npx`, `pip install`, or upgrade command. 3. The package manager retrieves the mutable compromised release. 4. Installation hooks or subsequently invoked package code execute under the installing user's privileges. 5. The malicious package can access data and credentials available to that user. ### Impact Assessment Impact depends on the privileges used for installation. In a normal user installation, compromised package code could access that user's files, environment variables, credentials, and network conn ...[truncated 351 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every documented dependency to a version that has been reviewed and tested. 2. For Python installation, publish a requirements file with hashes and recommend hash verification, for example: ```text yt-dlp==<reviewed-version> --hash=sha256:<verified-hash> ``` 3. Avoid recommending unconditional `pip install -U` as a troubleshooting step. Specify a known-good minimum or exact version instead. 4. Pin the ClawHub package version where the package manager supports version selection. 5. Document trusted package sources and advise installation in an isolated virtual environment. 6. Maintain a dependency update process that reviews release notes, provenance, integrity metadata, and transitive dependency changes before updating the pinned versions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README states that when subtitles are unavailable, the skill downloads audio and sends it to the OpenAI Whisper API, but it does not give a clear privacy or data-transmission warning near the workflow description. Users may unknowingly upload potentially sensitive audio content or copyrighted material to a third-party service, creating confidentiality, compliance, or policy risks. This skill's purpose makes the issue more significant because external transmission is a core fallback behavior, not an edge case.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
The README instructs users to run `npx clawhub install youtube-transcriber` without pinning a specific version. This can cause users to execute whatever package version is current at install time, increasing supply-chain risk if the package is compromised, typosquatted, or publishes a malicious update. In the context of an install command, this is more dangerous because it directly encourages code execution on the user's machine.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill advertises shell, file read, and file write behavior but does not declare any tool scope or permission boundaries. That creates an authorization and review gap: users and orchestrators cannot clearly see what capabilities the skill may exercise, increasing the chance of unintended command execution, file access, or unsafe composition with other agent behaviors.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The invocation text uses broad trigger phrases such as 'transcribe this video' and 'what does this video say,' which can overlap with ordinary conversation and cause the skill to activate unexpectedly. In context, unexpected activation is risky because the skill can invoke shell commands, download remote content, write files, and potentially send audio to a third-party API without a deliberate, informed user action.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The description says the skill transcribes with OpenAI Whisper API but does not clearly foreground that audio content may be uploaded to a third party when captions are unavailable. This is a real privacy and data-handling risk because users may assume transcription is local, while the skill can transmit potentially sensitive spoken content from arbitrary videos to an external service.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script uploads audio content to the OpenAI Whisper API, but the help text does not clearly warn users that video audio may be transmitted to a third-party service. In an agent setting, this can cause unintentional disclosure of sensitive spoken content, especially if users assume processing is local or are not aware of fallback behavior when subtitles are unavailable.

External Transmission

Medium
Category
Data Exfiltration
Content
CURL_ARGS=(
  -s
  -X POST
  "https://api.openai.com/v1/audio/transcriptions"
  -H "Authorization: Bearer ${OPENAI_API_KEY}"
  -F "file=@${WHISPER_INPUT}"
  -F "model=whisper-1"
Confidence
88% confidence
Finding
This code performs external transmission of downloaded audio to https://api.openai.com/v1/audio/transcriptions. While expected for a Whisper-based transcription feature, it is security-relevant because the transmitted media may contain sensitive personal, confidential, or regulated content, and in this skill context the transfer is automatic once subtitle fallback occurs.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The script defaults to saving a transcript under /tmp and later writes to the user-specified output path, but the usage text only describes the option names and does not warn that a local file will be created or overwritten. Although stderr progress messages exist, they do not disclose this behavior until execution is already underway, so the user lacks clear advance notice.

Static analysis

No suspicious patterns detected.