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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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"
  1. Ensure the temporary directory is owned by the current user and has mode 0700.

  2. 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.
  3. 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.
  4. Avoid using audio_latest.mp4 as shared state. Return the unique generated path to the next pipeline stage.

  5. 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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

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 · helpers/douyin_grab.py (reported line 82)May include surrounding context.

python
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", []))

Missing User Warnings

Medium
Category
Not specified by scanner
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.

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

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

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 · helpers/douyin_grab.py (reported line 66)May include surrounding context.

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

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 · helpers/run_douyin_pipeline.py (reported line 14)May include surrounding context.

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

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

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 · helpers/transcribe_and_note.py (reported line 30)May include surrounding context.

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