Back to skill

Security audit

Douyin Video Analysis

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real Douyin analysis workflow, but it reuses live browser cookies for media downloads and writes persistent local notes without enough scoping or consent controls.

Review before installing. Only use it with Douyin links you trust, preferably from an isolated Chrome profile with no important logged-in sessions. Expect local audio files and Obsidian notes to be created, and consider changing the helper to validate Douyin/CDN destinations, avoid cookie replay, use private per-run temp files, and ask before writing to the vault.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
helpers/douyin_grab.py:31
Finding
Browser session cookies may be disclosed to an untrusted media endpoint## Vulnerability Details **File Location**: `helpers/douyin_grab.py`, lines 31–64 **Vulnerability Type**: Unvalidated destination combined with sensitive cookie forwarding **Risk Level**: High ### Vulnerable Code ```python def fetch_page_snapshot(): js = r'''JSON.stringify({ url: location.href, title: document.title, bodyText: document.body ? document.body.innerText.slice(0,12000) : "", metas: Array.from(document.querySelectorAll("meta")).map(m=>({name:m.getAttribute("name"), property:m.getAttribute("property"), content:m.getAttribute("content")})).filter(x=>x.content).slice(0,80), resources: performance.getEntriesByType("resource").map(r=>r.name).filter(n => /douyinvod|media-audio|media-video|aweme\/detail/.test(n)).slice(0,400), cookie: document.cookie, ua: navigator.userAgent })''' return json.loads(chrome_eval(js)) def pick_audio_url(resources): for u in resources: if "media-audio" in u: return u return None def sanitize_cookie(raw_cookie: str) -> str: # keep it simple; curl can take the raw cookie string return raw_cookie.strip() def download_audio(audio_url: str, page_url: str, cookie: str, ua: str, out_path: Path): cmd = [ "curl", "-L", audio_url, "-H", f"User-Agent: {ua}", "-H", f"Referer: {page_url}", "-H", "Origin: https://www.douyin.com", "-H", f"Cookie: {cookie}", "-H", "Accept: */*", "-o", str(out_path), "-sS", ] ``` ### Technical Analysis The script extracts the complete JavaScript-accessible cookie string from the currently loaded browser page. It then selects a media URL solely by checking whether the resource URL contains the substring `media-audio`. There is no validation of the selected URL's scheme, hostname, port, or relationship to Douyin. The cookie is subsequently supplied as an explicit HTTP ...[truncated 1929 chars]
Remediation
## Remediation Suggestions 1. Validate the initial user URL before opening it: - Require HTTPS. - Allow only documented Douyin domains. - Normalize internationalized domain names and reject deceptive suffix matches. 2. Validate every candidate media URL: - Parse it with `urllib.parse.urlsplit`. - Require an exact allowlisted hostname or a carefully defined trusted CDN suffix. - Reject embedded credentials, unexpected ports, non-HTTPS schemes, and malformed URLs. - Do not rely on substring matching. 3. Do not forward the complete page cookie string: - Prefer unauthenticated media downloads where possible. - If authentication is required, select only the minimum necessary cookie names. - Use an isolated browser profile with no unrelated authenticated sessions. 4. Handle redirects explicitly: - Disable unrestricted `curl -L`. - Inspect and validate each redirect destination before following it. - Never forward a manually supplied `Cookie` header when the destination origin changes. - Set a small maximum redirect count. 5. Add tests covering: - Attacker-controlled URLs containing `media-audio`. - Cross-origin redirects. - Subdomain confusion such as `douyin.com.attacker.example`. - Non-HTTPS media URLs.

T09 · Insecure Skill Coding Practices

Warning
Location
helpers/douyin_grab.py:98
Finding
Predictable shared temporary audio path permits symlink-based file clobbering## Vulnerability Details **File Location**: `helpers/douyin_grab.py`, lines 12–13 and 98–101 **Vulnerability Type**: Unsafe temporary file handling **Risk Level**: Medium ### Vulnerable Code ```python TMP_DIR = Path("/tmp/douyin_transcribe") TMP_DIR.mkdir(parents=True, exist_ok=True) ``` ```python if audio_url: cookie = sanitize_cookie(data.get("cookie", "")) ua = data.get("ua", "Mozilla/5.0") out_path = TMP_DIR / "audio_latest.mp4" try: download_audio(audio_url, data.get("url") or url, cookie, ua, out_path) ``` The destination is passed directly to `curl`: ```python cmd = [ "curl", "-L", audio_url, "-H", f"User-Agent: {ua}", "-H", f"Referer: {page_url}", "-H", "Origin: https://www.douyin.com", "-H", f"Cookie: {cookie}", "-H", "Accept: */*", "-o", str(out_path), "-sS", ] ``` ### Technical Analysis The script uses the fixed directory `/tmp/douyin_transcribe` and the predictable filename `audio_latest.mp4`. It does not verify directory ownership or permissions and does not reject symbolic links before writing. If an attacker can prepare the directory or manipulate the destination path, `curl -o` may follow a symbolic link and overwrite its target. The target is limited to files writable by the account running the Skill, but this can still corrupt user data or application configuration. Reusing one global filename also creates race conditions between concurrent executions. One run can replace another run's media, causing the wrong audio to be transcribed or exposing media between users or jobs sharing the same account or temporary environment. ### Attack Path 1. A local attacker or another process predicts the fixed path `/tmp/douyin_transcribe/audio_latest.mp4`. 2. Before the Skill writes the file, the attacker creates the directory with permissive access or replaces the output with a symbolic link. 3. The symbolic link point ...[truncated 1088 chars]
Remediation
## Remediation Suggestions 1. Create a private, randomized directory for each run: ```python import tempfile from pathlib import Path run_dir = Path(tempfile.mkdtemp(prefix="douyin_transcribe_")) out_path = run_dir / "audio.mp4" ``` 2. Ensure the temporary directory is owned by the current user and has mode `0700`. 3. Create destination files atomically: - Use exclusive creation semantics such as `os.open` with `O_CREAT | O_EXCL`. - Add `O_NOFOLLOW` where supported. - Pass an already secured file descriptor to the downloader where practical. 4. If the persistent directory must be retained: - Verify that it is a real directory rather than a symbolic link. - Verify its owner matches the effective user. - Reject group-writable or world-writable permissions. - Generate a cryptographically unpredictable filename for every run. 5. Avoid using `audio_latest.mp4` as shared state. Return the unique generated path to the next pipeline stage. 6. Delete temporary media after processing unless retention is explicitly requested, and add concurrency tests to ensure separate runs cannot overwrite or consume one another's files.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill claims end-to-end URL ingestion, metadata extraction, transcription, analysis, and note generation, but the described helper flow depends on preexisting audio input and writes directly into a fixed local Obsidian path while not actually performing much of the promised analysis. This mismatch can mislead users about what data will be processed and where outputs will be stored, increasing the risk of unintended local writes and overtrust in incomplete results.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill claims end-to-end URL ingestion, metadata extraction, transcription, analysis, and note generation, but the described helper flow depends on preexisting audio input and writes directly into a fixed local Obsidian path while not actually performing much of the promised analysis. This mismatch can mislead users about what data will be processed and where outputs will be stored, increasing the risk of unintended local writes and overtrust in incomplete results.

Missing User Warnings

High
Confidence
95% confidence
Finding
The workflow explicitly instructs use of browser-derived cookies plus referer/origin/user-agent to bypass a 403 when downloading media. That can expose authenticated session material to downstream tooling, expand access beyond publicly available content, and create risk of credential leakage or unauthorized access if those headers/cookies are logged, stored, or reused improperly.

Ssd 3

High
Confidence
98% confidence
Finding
By extracting cookies, user agent, page metadata, and resource URLs from the browser and reusing them in a separate request flow, the script creates a data-exposure channel from an authenticated browser session into script-controlled networking. That expands the trust boundary and can leak session-linked information through local output, logs, downstream tooling, or future modifications to the destination URL selection.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The script reads document.cookie from the browser context and forwards it in a separate curl request, effectively exporting live browser session material outside the browser's normal protections. In the context of a video-analysis skill, this creates unnecessary credential exposure and could enable unauthorized reuse of authenticated state, especially if logs, errors, or modified endpoints capture those cookies.

Missing User Warnings

High
Confidence
97% confidence
Finding
Collecting browser cookies without an explicit warning or consent step is a serious privacy and security issue because users are unlikely to expect their active web session to be harvested and replayed. The skill context makes this more dangerous, not less, because its stated purpose is media analysis, not credential handling, so the behavior is disproportionate to user expectations.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The changelog repeatedly states that documentation and product-page content were changed to be 'Chinese-first' and prioritized for Chinese users. This is natural-language evidence of a locale preference being enforced, and the file does not indicate any user opt-in or a documented region-specific justification.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The listing highlights metadata extraction, audio capture, transcription, and note creation, but it does not clearly warn users that the skill downloads audio from a third-party platform and writes derived content into Obsidian. That omission can mislead users about data flow, storage, and side effects, increasing the risk of privacy, copyright, or unintended local data modification issues.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The listing explicitly states that the skill will write transcripts and analysis results into Obsidian, but it does not warn users that local notes or files will be created or modified. This can lead to unexpected filesystem changes, accidental overwrites, privacy issues, or insertion of untrusted/transcribed content into a user's knowledge base without informed consent.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill explicitly describes inspecting browser-loaded resources, attempting authenticated audio download, writing files to /tmp, and saving notes into Obsidian, but it does not warn users that it may leverage an already-authenticated browser session or persist potentially sensitive content locally. In this context, the danger is unauthorized or unexpected use of session context and local data persistence, which can expose private account context, copyrighted/private media, or sensitive transcript content without informed user consent.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill instructs use of shell execution, local file reads/writes, browser probing, and writing into Obsidian, yet declares no explicit tool or permission scope. That creates an authorization and audit gap: an agent may invoke broader capabilities than users expect, including filesystem changes and browser-assisted collection, without clear guardrails.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill instructs retrying downloads with browser-derived headers/cookies but does not clearly warn users that authenticated session data may be reused. Replaying cookies outside the browser context can expose account/session material, expand the blast radius of compromise, and perform actions or access media in ways users did not knowingly authorize.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill writes notes into Obsidian and stores temporary media/transcription artifacts under /tmp, but this side effect is not surfaced as a clear privacy and storage warning. Users may unknowingly persist potentially sensitive content locally, where it can be indexed, synced, or later accessed by other software or users on the machine.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The file title is entirely in Chinese and the whole template is written only in Chinese, which effectively mandates a specific language for using this skill output. There is no indication that users can choose another language or that the Chinese-only requirement is justified by a documented region-specific purpose.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The trigger says the skill activates when a user sends a Douyin URL and wants content analysis, but it does not define specific invocation phrases, exclusions, or context boundaries. This can cause ambiguous activation for many ordinary requests involving Douyin links, with no negative examples clarifying when the workflow should not run.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The workflow saves downloaded media into `/tmp/douyin_transcribe/` and later writes transcript/analysis notes, but does not warn the user that source content and derived text will persist on disk. This creates privacy and data-retention risk, especially if the content contains personal information, copyrighted material, or sensitive browsing-derived artifacts tied to the user's activity.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
The script writes downloaded media to a predictable location under /tmp without clearly warning the user, which can expose sensitive or copyrighted content to other local processes or users depending on system configuration and file permissions. The risk is amplified by the skill's automated workflow, because users may not realize local artifacts are being retained.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
url = sys.argv[1]
    chrome_open(url)
    print("Opened in Chrome. Waiting for page to settle...", file=sys.stderr)
    subprocess.run(["sleep", "5"])

    data = fetch_page_snapshot()
    audio_url = pick_audio_url(data.get("resources", []))
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This script writes a new Markdown note directly into the user's Obsidian vault without any interactive confirmation, dry-run mode, or explicit safety gate. Because multiple note fields are populated from command-line input, invoking the helper can cause persistent modification of a sensitive personal knowledge base, and in this skill context that means unreviewed external content from Douyin may be stored automatically in the user's vault.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_json(cmd):
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise RuntimeError(p.stderr.strip() or p.stdout.strip() or f'command failed: {cmd}')
    txt = p.stdout.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_json(cmd):
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise RuntimeError(p.stderr.strip() or p.stdout.strip() or f'command failed: {cmd}')
    txt = p.stdout.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_json(cmd):
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise RuntimeError(p.stderr.strip() or p.stdout.strip() or f'command failed: {cmd}')
    txt = p.stdout.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The transcription call sets `language='zh'`, which forces a specific language/locale behavior for all inputs. This is a natural-language policy issue because the script does not offer a user opt-in or selection mechanism, and no region-specific justification is documented in the file.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
result = transcribe({audio_file!r}, path_or_hf_repo={model!r}, language='zh', task='transcribe')
print(result['text'])
"""
    p = subprocess.run([VENV_PY, '-c', code], capture_output=True, text=True)
    if p.returncode != 0:
        raise RuntimeError(p.stderr.strip() or p.stdout.strip() or 'transcription failed')
    return p.stdout.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.