Back to skill

Security audit

Douyin Search

Security checks across malware telemetry and agentic risk

Overview

The skill appears to perform Douyin scraping as advertised, but it asks for and stores live Douyin login cookies/session state and has avoidable command-execution handling risks that merit review before installation.

Install only if you are comfortable giving the skill access to a logged-in Douyin session. Prefer placing cookies in the local file yourself instead of pasting them into chat, protect and delete cookie/state files when done, expect local exports and optional video downloads to consume disk space, and review the shell=True agent-browser eval implementation before using it on a shared or sensitive machine.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (18)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""通过临时文件执行 ab eval(避免 shell 转义)"""
    js_file = str(TMP_DIR / "_ab_eval_tmp.js")
    Path(js_file).write_text(js)
    return subprocess.run(
        f"agent-browser eval \"$(cat {js_file})\"",
        shell=True, capture_output=True, text=True, timeout=timeout,
    )
Confidence
98% confidence
Finding
return subprocess.run( f"agent-browser eval \"$(cat {js_file})\"", shell=True, capture_output=True, text=True, timeout=timeout, )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
js = HARVEST_JS_AGGRESSIVE
        js_file = str(TMP_DIR / "_harvest_oneshot.js")
        Path(js_file).write_text(js)
        r = subprocess.run(
            f"agent-browser eval \"$(cat {js_file})\"",
            shell=True, capture_output=True, text=True, timeout=120,
        )
Confidence
98% confidence
Finding
r = subprocess.run( f"agent-browser eval \"$(cat {js_file})\"", shell=True, capture_output=True, text=True, timeout=120, )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
js_file = str(TMP_DIR / "_harvest_step.js")
        Path(js_file).write_text(js)
        # 初始化(第一次 eval 走初始化分支)
        r = subprocess.run(
            f"agent-browser eval \"$(cat {js_file})\"",
            shell=True, capture_output=True, text=True, timeout=15,
        )
Confidence
98% confidence
Finding
r = subprocess.run( f"agent-browser eval \"$(cat {js_file})\"", shell=True, capture_output=True, text=True, timeout=15, )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
})()
        """
        Path(str(TMP_DIR / "_harvest_flush.js")).write_text(flush_js)
        r = subprocess.run(
            f"agent-browser eval \"$(cat {TMP_DIR}/_harvest_flush.js)\"",
            shell=True, capture_output=True, text=True, timeout=10,
        )
Confidence
97% confidence
Finding
r = subprocess.run( f"agent-browser eval \"$(cat {TMP_DIR}/_harvest_flush.js)\"", shell=True, capture_output=True, text=True, timeout=10, )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
stalled = 0
        total_added = 0
        for rnd in range(max_rounds):
            r = subprocess.run(
                f"agent-browser eval \"$(cat {js_file})\"",
                shell=True, capture_output=True, text=True, timeout=20,
            )
Confidence
98% confidence
Finding
r = subprocess.run( f"agent-browser eval \"$(cat {js_file})\"", shell=True, capture_output=True, text=True, timeout=20, )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
err(f"  round {rnd+1} eval 失败: {r.stderr[:100]}")
                # 重试 1 次
                time.sleep(2)
                r = subprocess.run(
                    f"agent-browser eval \"$(cat {js_file})\"",
                    shell=True, capture_output=True, text=True, timeout=20,
                )
Confidence
98% confidence
Finding
r = subprocess.run( f"agent-browser eval \"$(cat {js_file})\"", shell=True, capture_output=True, text=True, timeout=20, )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
}})
    .filter(Boolean)
)""")
    r = subprocess.run(
        f"agent-browser eval \"$(cat {eval_file})\"",
        capture_output=True, text=True, timeout=15, shell=True,
    )
Confidence
98% confidence
Finding
r = subprocess.run( f"agent-browser eval \"$(cat {eval_file})\"", capture_output=True, text=True, timeout=15, shell=True, )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
const avatar = !!document.querySelector('[class*="avatar"]:not([class*="placeholder"])');
        return JSON.stringify({title, captcha, loginPanel, userInfo, avatar});
    })()"""
    r = subprocess.run(
        f"agent-browser eval \"$(cat <<'EOF'\n{check_js}\nEOF\n)\"",
        capture_output=True, text=True, timeout=10, shell=True,
    )
Confidence
97% confidence
Finding
r = subprocess.run( f"agent-browser eval \"$(cat <<'EOF'\n{check_js}\nEOF\n)\"", capture_output=True, text=True, timeout=10, shell=True, )

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The README describes the skill as "只读" (read-only), but the documented commands explicitly download Douyin video files and persist them under local output directories. This is a security-relevant integrity issue because users and calling agents may grant trust or choose execution paths based on the claim that the skill only reads metadata/comments, while it actually writes copyrighted content and sensitive session-derived artifacts to disk.

Description-Behavior Mismatch

Medium
Confidence
84% confidence
Finding
The read-only claim is materially incomplete because the skill stores downloaded video files and records local paths in aggregated outputs. While this does not modify Douyin state, it does expand local data collection and persistence, which is security-relevant and may surprise users or downstream systems expecting metadata-only handling.

Description-Behavior Mismatch

High
Confidence
90% confidence
Finding
The script is presented as a comment-harvesting/read-only tool, but it also optionally downloads full videos. That mismatch expands data collection and persistence beyond the stated purpose, increasing privacy/compliance risk and making operator misuse more likely.

Intent-Code Divergence

Medium
Confidence
86% confidence
Finding
The top-level documentation says the skill is read-only and focused on comment harvesting, yet the code also performs feed navigation/warmup behavior and can download videos. Security-relevant documentation mismatches are dangerous because they cause users and reviewers to underestimate the scope of browser automation and data collection.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The changelog states that the skill now downloads videos and writes artifacts under $SKILL/data/, but it does not indicate any explicit user-facing warning or consent flow about local file creation and storage usage. In an agent setting, undocumented writes can mislead operators, create disk consumption surprises, and violate expectations for a supposedly read-only skill, even if the writes stay confined to the skill directory.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The contributor guide explicitly instructs maintainers to test with real cookies and a persisted browser state/session, but it does not pair that advice with handling requirements for these authentication artifacts. In this skill’s context, those files can grant access to logged-in Douyin sessions, so accidental logging, committing, sharing, or reuse across machines could expose user accounts or authenticated scraping capability.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill explicitly tells the agent to ask the user to send raw Douyin cookies and then save them to disk, but provides no strong warning about credential sensitivity, storage protection, or least-retention handling. Session cookies are effectively authentication secrets; exposing them to the agent or persisting them insecurely can enable account takeover or unauthorized access to private account functionality.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
The code reads browser cookie material from disk and converts it into a request header for download functionality, but provides little explicit warning about handling sensitive authentication data. In a scraping skill that already automates a logged-in browser, silent cookie reuse increases the chance of credential exposure or unreviewed session use.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script writes Douyin authentication cookies to disk in a reusable Netscape cookie jar without a nearby explicit warning that this file contains live session credentials. In a skill specifically designed to automate authenticated scraping, these cookies are equivalent to account access tokens, so accidental disclosure, backup sync, or local compromise can lead to account hijacking.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
Persisting agent-browser state to disk can capture authenticated browser session material beyond plain cookies, yet the code saves it without an explicit warning about sensitivity. In this skill context, that state is intentionally used to retain a logged-in Douyin session for comment harvesting, so theft of the file could allow unauthorized reuse of the session.

VirusTotal

64/64 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
comments-harvest.py:317