T09 · Insecure Skill Coding Practices
- Location
SKILL.md:360- Finding
Browser authentication cookies can be exported to a persistent plaintext file
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This media skill has a coherent video-subtitling purpose, but it asks agents to use Chrome YouTube login cookies by default and can leave exported cookies in a local file.
Review before installing. Use this only if you are comfortable with local tools accessing your Chrome YouTube session. Prefer unauthenticated yt-dlp downloads first, use a dedicated browser profile or temporary cookie file only when needed, delete any `cookies.txt`, and review the update-check and dependency-install commands before running them.
SKILL.md:360Browser authentication cookies can be exported to a persistent plaintext file
SKILL.md:117Unpinned Python dependency installation exposes the workflow to supply-chain changes
The declared description is about a user-facing video repurposing/subtitling skill for YouTube-to-Bilibili workflows, including bilingual and hardcoded subtitle creation. The supplied code does not process user videos, create bilingual subtitles, or target Chinese platform publishing. Instead, it is a developer-facing helper script for regenerating documentation preview images of subtitle presets from a sample SRT file. This is a materially different primary purpose, so it is a clear mismatch.
The browser-cookie access pattern strongly overlaps with credential-harvesting behavior because it reads authenticated session material from Chrome. Although the stated purpose is downloading restricted videos, this context only partially justifies the capability; defaulting to this behavior in a media skill materially raises the risk of credential misuse or accidental exposure.
r provides a YouTube URL (single video or playlist) and wants a Bilibili-ready version
- User needs bilingual EN-ZH subtitles burned into video
- User wants to repurpose English video content for Chinese audience
## Quick Reference
| Step | Tool | Command | Output |
|------|------|---------|--------|
| 0. Update | `git` | Auto-check for skill updates | — |
| 1. Download | `yt-dlp` | `yt-dlp --cookies-from-browser chrome -f ... -o ...` | `{slug}.mp4` |
| 2. Transcribe | `whisper`* | `srt_utils.py check-whisper` then transcribe | `{slug}_{lang}.srt` |
| 2.5 Validate | `srt_utils.py` | `srt_utils.py validate / fix` | `{slug}_{lang}.srt` (fixed) |
| 3. Translate | AI | SRT-aware batch translation | `{slug}_zh.srt` |
| 4. Merge | `srt_utils.py` | `srt_utils.py merge ...` | `{slug}_bilingual.srt` |
| 4.5 Style | `srt_utils.py` | `srt_utils.py to_ass --preset netflix\|clean\|glow` | `{slug}_bilingual.ass` |
| 5. Burn | `ffmpeg` | `ffmpeg -c:v libx264 -vf ass=...` | `{slug}_bilingual.mp4`
The find command enumerates multiple agent configuration and skill directories under the user's home directory, which is broader access than needed for normal media processing. Access to agent config locations can reveal installed skills, local environment structure, and potentially sensitive metadata; in adversarial hands, this becomes reconnaissance for further compromise.
# Find skill directory (works across Claude Code, OpenClaw, Hermes, Pi)
SKILL_DIR="$(find ~/.claude/skills ~/.openclaw/skills ~/.hermes/skills ~/.pi/agent/skills ~/.agents/skills ~/myagents/myskills -maxdepth 2 -name 'yt2bb' -type d 2>/dev/null | head -1)"
echo "yt2bb: SKILL_DIR=$SKILL_DIR"
if [ -n "$SKILL_DIR" ] && [ -d "$SKILL_DIR/.git" ]; then
git -C "$SKILL_DIR" fetch --quiet origin main 2>/dev/null
The file has undeclared shell/process-execution capability through subprocess-based environment probing, but the skill metadata does not appear to declare that permission. In an agent setting, hidden execution capability increases trust-boundary risk because reviewers and orchestrators may assume the skill is purely local text/file processing while it can also launch system binaries.
The README states that a logged-in YouTube account via Chrome is a prerequisite and that yt-dlp extracts cookies automatically, but it does not clearly warn users that the skill will access browser session cookies. This can surprise users into granting access to authenticated YouTube data or account-bound content, increasing privacy and account misuse risk if the skill or its tooling is modified or misused.
The README explicitly states that yt-dlp will automatically extract cookies from a logged-in Chrome profile, but it does not warn users that browser cookies may grant authenticated access and may expose account/session data to local tooling or logs. In an agent/skill context, normalizing automatic credential reuse increases the chance that users run the workflow without understanding the privacy and account-security implications.
Using yt-dlp --cookies-from-browser chrome accesses sensitive browser session data and can expose authenticated tokens far beyond what users expect from a subtitling skill. In this context the capability may be functionally useful for age-gated/private videos, but it is high-risk because it reaches into browser storage and could be repurposed to access or exfiltrate credentials.
Enumerating ~/.claude/skills, ~/.openclaw/skills, and similar paths discloses what agent frameworks and skills are installed. This reconnaissance is unnecessary for the business purpose and can help an attacker tailor follow-on attacks or identify other valuable targets in the environment.
# Find skill directory (works across Claude Code, OpenClaw, Hermes, Pi)
SKILL_DIR="$(find ~/.claude/skills ~/.openclaw/skills ~/.hermes/skills ~/.pi/agent/skills ~/.agents/skills ~/myagents/myskills -maxdepth 2 -name 'yt2bb' -type d 2>/dev/null | head -1)"
echo "yt2bb: SKILL_DIR=$SKILL_DIR"
if [ -n "$SKILL_DIR" ] && [ -d "$SKILL_DIR/.git" ]; then
git -C "$SKILL_DIR" fetch --quiet origin main 2>/dev/null
The instruction 'All output in this file must be in Chinese' forces a specific language for generated content. The policy allows locale constraints only when the skill offers user choice or clearly documents a justified region-specific requirement; here it is presented as a blanket requirement for output generation.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
| Platform | Font | Install |
|----------|------|---------|
| macOS | `PingFang SC` | pre-installed |
| Linux | `Noto Sans CJK SC` | `sudo apt install fonts-noto-cjk` |
| Linux (alt) | `WenQuanYi Micro Hei` | `sudo apt install fonts-wqy-microhei` |
| Windows | `Microsoft YaHei` | pre-installed |
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
| Platform | Font | Install |
|----------|------|---------|
| macOS | `PingFang SC` | pre-installed |
| Linux | `Noto Sans CJK SC` | `sudo apt install fonts-noto-cjk` |
| Linux (alt) | `WenQuanYi Micro Hei` | `sudo apt install fonts-wqy-microhei` |
| Windows | `Microsoft YaHei` | pre-installed |
The skill enables implicit invocation without any stated trigger constraints, which can cause it to activate on loosely related user requests and begin workflows involving YouTube downloads, cookie-based authentication context, transcription, and publication metadata generation without clear user intent. In this skill, that is more dangerous than a generic helper because it touches external content acquisition and references Chrome cookie auth, increasing the chance of unintended privacy-impacting or policy-sensitive actions.
The capability text hard-codes translation to Simplified Chinese and Bilibili-oriented output without indicating user choice, which can lead the agent to transform content into a specific target language and platform format even when the user did not explicitly request that outcome. In context, this is primarily a consent and workflow-scope issue rather than direct code execution, but it can still cause unintended content processing or publication-preparation behavior.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
def write_srt(entries, path):
"""Write list of entry dicts to SRT file."""
lines = []
for i, e in enumerate(entries, 1):
lines.extend([str(i), f"{e['start']} --> {e['end']}", e['text'], ''])
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _run_quiet(cmd):
"""Run a command and return stdout, or None on failure."""
try:
r = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
return r.stdout.strip() if r.returncode == 0 else None
except (FileNotFoundError, subprocess.TimeoutExpired):
return None
The workflow table describes many generated artifacts such as downloaded videos, subtitle files, merged subtitles, burned-in videos, and publish metadata, but the README does not clearly warn that multiple files will be written and that repackaged media for republication will be produced. This can lead to unanticipated disk writes, accidental disclosure of derivative content, or unintended publishing workflow side effects.
The skill performs a remote git fetch update check unrelated to the core media-processing task. While it does not auto-pull or execute code, it introduces unexpected network activity and trust in a remote repository, which expands the attack surface and can leak usage metadata.
Embedding git network update instructions in a subtitle-processing skill is unnecessary for task completion and conditions users to perform remote interactions from within the skill. Even without auto-execution of remote code, this can normalize unsafe trust in upstream changes and create room for supply-chain abuse if users later follow the suggested pull command.
Step 4.5 explicitly instructs the agent to ask the user for a subtitle style and 'Do NOT silently pick a default,' only using clean if the user has no preference. Later, the utility section documents to_ass input.srt output.ass as using a default clean, which contradicts the earlier intent guidance and could encourage silent defaulting.
No suspicious patterns detected.