Back to skill

Security audit

Toutiao Publisher

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Toutiao publishing helper, but it needs Review because it can post to a live account using stored login sessions and includes under-scoped automation and setup behavior.

Review this before installing. Use it only if you are comfortable letting an agent operate a real Toutiao account, upload the specified local files, and publish externally. Prefer visible, non-headless runs with --dry-run until content is reviewed, keep data/browser_state private, clear saved sessions when done, and run the skill in an isolated environment because setup downloads executable dependencies and Chrome.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:61
Finding
Arbitrary Python File Execution Through Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:61-89`; `scripts/setup_environment.py:122-139` **Vulnerability Type**: Unrestricted script path traversal and arbitrary local Python execution **Risk Level**: High ### Vulnerable Code `scripts/run.py:61-89`: ```python script_name = sys.argv[1] script_args = sys.argv[2:] # Handle both "scripts/script.py" and "script.py" formats if script_name.startswith("scripts/"): # Remove the scripts/ prefix if provided script_name = script_name[8:] # len('scripts/') = 8 # Ensure .py extension if not script_name.endswith(".py"): script_name += ".py" # Get script path skill_dir = Path(__file__).parent.parent script_path = skill_dir / "scripts" / script_name if not script_path.exists(): print(f"❌ Script not found: {script_name}") print(f" Working directory: {Path.cwd()}") print(f" Skill directory: {skill_dir}") print(f" Looked for: {script_path}") sys.exit(1) # Ensure venv exists and get Python executable venv_python = ensure_venv() # Build command cmd = [str(venv_python), str(script_path)] + script_args # Run the script try: result = subprocess.run(cmd) ``` `scripts/setup_environment.py:122-139`: ```python def run_script(self, script_name: str, args: list = None) -> int: """Run a script with the virtual environment""" script_path = self.skill_dir / "scripts" / script_name if not script_path.exists(): print(f"❌ Script not found: {script_path}") return 1 # Ensure venv is set up if not self.ensure_venv(): print("❌ Failed to set up environment") return 1 # Build command cmd = [str(self.venv_python), str(script_path)] if args: cmd.extend(args) print(f"🚀 Running: {script_name} with venv Python") try: # Run the script with venv Python result = subprocess.run(cmd) ``` ### Technical Analysis Both entry points accept a caller-controlled script name and append it ...[truncated 1691 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace free-form script selection with a strict allowlist: ```python ALLOWED_SCRIPTS = { "auth_manager.py": scripts_dir / "auth_manager.py", "publisher.py": scripts_dir / "publisher.py", } ``` 2. Reject absolute paths, path separators, `..` components, and unexpected extensions before resolving the path. 3. Canonicalize both paths and verify containment: ```python scripts_dir = (skill_dir / "scripts").resolve() candidate = (scripts_dir / script_name).resolve(strict=True) if candidate.parent != scripts_dir: raise ValueError("Script must be a direct child of the scripts directory") ``` 4. Reject symbolic links unless they are explicitly required and their final targets remain inside the trusted directory. 5. Apply the same validation in both `run.py` and `SkillEnvironment.run_script`. 6. Add tests covering absolute paths, `../` traversal, nested traversal, symbolic-link escapes, and valid allowlisted names. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/setup_environment.py:52
Finding
Automatic Execution of Unverified Third-Party Dependencies and Browser Installer<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_environment.py:52-82`; `requirements.txt:1-2` **Vulnerability Type**: Unverified supply-chain installation and automatic installer execution **Risk Level**: Medium ### Vulnerable Code `scripts/setup_environment.py:52-82`: ```python if self.requirements_file.exists(): print("📦 Installing dependencies...") try: # Upgrade pip first subprocess.run( [str(self.venv_pip), "install", "--upgrade", "pip"], check=True, capture_output=True, text=True, ) # Install requirements result = subprocess.run( [str(self.venv_pip), "install", "-r", str(self.requirements_file)], check=True, capture_output=True, text=True, ) print("✅ Dependencies installed") # Install Chrome for Patchright (not Chromium!) print("🌐 Installing Google Chrome for Patchright...") try: subprocess.run( [ str(self.venv_python), "-m", "patchright", "install", "chrome", ], check=True, capture_output=True, text=True, ) ``` `requirements.txt:1-2`: ```text patchright==1.55.2 python-dotenv==1.0.0 ``` ### Technical Analysis On first execution, the Skill automatically upgrades `pip`, installs packages, imports and executes package-provided code, and invokes Patchright’s browser installer. The Python package versions are pinned, but their artifacts are not protected with cryptographic hashes. The package index and browser download source are not explicitly constrained or verified by project-controlled integrity metadata. Version pinning prevents ordinary version drift but does not protect against a compromised package index, a replaced artifact under the same ...[truncated 1271 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a reviewed lockfile containing exact artifact hashes and install with `pip --require-hashes`. 2. Constrain installation to an explicitly trusted HTTPS package index and, where practical, an internal package mirror. 3. Avoid automatically upgrading `pip` during normal Skill execution. Pin and provision the installer separately. 4. Separate environment provisioning from article publication and require explicit user or administrator approval before downloading executable components. 5. Pin and verify the Chrome artifact by expected version and cryptographic digest, or install it through a trusted system package-management process. 6. Build and scan dependencies in CI, generate a software bill of materials, and review dependency updates before release. 7. Run setup under a dedicated unprivileged account or isolated build container without access to stored browser sessions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/auth_manager.py:176
Finding
Toutiao Authentication State Stored Without Explicit Access Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/auth_manager.py:176-181`; `scripts/publisher.py:113-119`; `scripts/config.py:12-14` **Vulnerability Type**: Insecure storage of reusable session credentials **Risk Level**: Medium ### Vulnerable Code `scripts/auth_manager.py:176-181`: ```python def _save_browser_state(self, context: BrowserContext): """Save browser state to disk""" try: # Save storage state (cookies, localStorage) context.storage_state(path=str(self.state_file)) print(f" 💾 Saved browser state to: {self.state_file}") ``` `scripts/publisher.py:113-119`: ```python # Save state for future use try: state_path = Path("data/browser_state/state.json") state_path.parent.mkdir(parents=True, exist_ok=True) context.storage_state(path=str(state_path)) print(" State saved.") except Exception as e: print(f" Warning: Could not save state: {e}") ``` `scripts/config.py:12-14`: ```python BROWSER_PROFILE_DIR = BROWSER_STATE_DIR / "browser_profile" STATE_FILE = BROWSER_STATE_DIR / "state.json" AUTH_INFO_FILE = DATA_DIR / "auth_info.json" ``` ### Technical Analysis Playwright storage-state files can contain reusable authentication cookies and local-storage values. The project writes this material to plaintext JSON without explicitly creating an owner-only directory or setting an owner-only file mode. The normal authentication manager uses the configured project-relative state location. The publisher independently constructs `Path("data/browser_state/state.json")`, which is relative to the current working directory rather than the Skill root. When the publisher is started from another directory, this can produce an additional credential copy in an unexpected location. The effective exposure depends on operating-system defaults and the process umask. On shared systems, permissive inherited permissions, workspace sharing, backups, or accidental source-control inclusion can expose the sessio ...[truncated 1191 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system credential store or encrypted secret storage for reusable authentication material. 2. Create the data and browser-state directories with owner-only permissions (`0700` on Unix-like systems). 3. Enforce owner-read/write permissions (`0600`) immediately after creating the storage-state file. 4. Write to a temporary owner-only file and atomically rename it into place to avoid partially written or briefly exposed state. 5. Replace the publisher’s working-directory-relative path with the canonical configured `STATE_FILE`. 6. Add `data/`, browser profiles, storage-state files, screenshots, and other runtime artifacts to `.gitignore` and backup-exclusion policies. 7. Detect and reject state paths located in shared or world-writable directories. 8. Document session revocation procedures and clear both the profile and all storage-state copies on logout. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/config.py:23
Finding
Remote Browser Content Processed With the Chrome Sandbox Disabled<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py:23-29`; `scripts/browser_utils.py:25-34` **Vulnerability Type**: Browser isolation and defense-in-depth disabled **Risk Level**: High ### Vulnerable Code `scripts/config.py:23-29`: ```python BROWSER_ARGS = [ "--disable-blink-features=AutomationControlled", # Patches navigator.webdriver "--disable-dev-shm-usage", "--no-sandbox", "--no-first-run", "--no-default-browser-check", ] ``` `scripts/browser_utils.py:25-34`: ```python context = playwright.chromium.launch_persistent_context( user_data_dir=user_data_dir, channel="chrome", # Use real Chrome headless=headless, no_viewport=True, ignore_default_args=["--enable-automation"], user_agent=USER_AGENT, args=BROWSER_ARGS, ) ``` ### Technical Analysis The `--no-sandbox` argument disables a primary Chrome security boundary intended to isolate renderer processes that parse and execute untrusted remote web content. The browser visits Toutiao pages and may process advertisements, embedded resources, uploaded article content, and other remotely controlled material. Disabling the sandbox is not itself a complete exploit: an attacker must still trigger a browser vulnerability or another renderer-level code-execution flaw. However, it substantially weakens containment and can turn a renderer compromise into direct execution with the browser process’s user privileges. The option is applied universally rather than only in a separately isolated deployment environment. ### Attack Path 1. The Skill launches Chrome with `--no-sandbox`. 2. Chrome loads Toutiao or another resource reached through the publishing workflow. 3. Malicious or compromised remote content triggers a browser renderer vulnerability. 4. Attacker-controlled code executes in the renderer. 5. Because the Chrome sandbox is disabled, the normal renderer containment boundary is absent. 6. The exploit can operate with the permissions avail ...[truncated 629 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--no-sandbox` from `BROWSER_ARGS` for supported desktop and server environments. 2. Refuse to run the browser as root rather than disabling its sandbox to accommodate privileged execution. 3. If an exceptional environment cannot support Chrome’s sandbox, run the entire Skill in a dedicated unprivileged container or virtual machine with: - A read-only project filesystem where possible - No host home-directory mount - Restricted network egress - Dropped Linux capabilities - `no-new-privileges` - Resource limits and a restrictive seccomp profile 4. Keep Chrome and Patchright updated through a reviewed, controlled update process. 5. Add a startup check that warns or fails when sandboxing is disabled outside an explicitly approved isolated environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (21)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
*   **首次使用**:直接运行发布命令(不带 `--headless`)。脚本会自动弹出浏览器,请扫码登录。登录后脚本会自动保存状态。
*   **状态失效**:如果遇到 `No valid authentication` 且自动重试无效,可手动清理状态:
    ```bash
    rm -rf data/browser_state
    ```
    然后重新运行发布命令。
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill documentation frames the capability as a focused publishing helper, but it also indicates environment creation, dependency installation, browser installation, and a generic script runner. Hidden or under-emphasized execution, installation, and environment-management behavior expands the attack surface substantially because running the skill can modify the host, fetch code, and execute scripts beyond what a user may expect from a simple publisher.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill documentation frames the capability as a focused publishing helper, but it also indicates environment creation, dependency installation, browser installation, and a generic script runner. Hidden or under-emphasized execution, installation, and environment-management behavior expands the attack surface substantially because running the skill can modify the host, fetch code, and execute scripts beyond what a user may expect from a simple publisher.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README encourages natural-language driven publishing, including generating content and directly posting it to a live Toutiao account, but it does not clearly warn that these actions modify external account state and may cause irreversible publication. In an agent setting, this increases the risk of users invoking real-world side effects without informed consent, especially when login state is persisted and reused.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill advertises commands that read and write local files, persist browser session state, and execute shell commands, but it declares no explicit tool scope or permission boundaries. That makes it harder for users or orchestrators to understand what capabilities are being granted and increases the chance of over-privileged execution or unintended local side effects.

Vague Triggers

Medium
Confidence
96% confidence
Finding
Triggering the skill merely when 'toutiao' or '头条号' is mentioned is overly broad and can cause the agent to invoke browser automation, session handling, or publication-related actions outside a clear user request. In this context, the skill has side effects and external network interaction, so loose activation criteria materially increase the risk of unintended execution.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill persists authentication state and browser data locally, but the user-facing instructions do not clearly warn that cookies and session material will remain on disk. Persisted session artifacts are sensitive because compromise of the local machine or workspace could allow reuse of the authenticated Toutiao account.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The documentation presents the workflow as manual and interactive, but it also exposes a headless fully automated publishing mode. That discrepancy is dangerous because users may authorize the skill expecting human review in the browser, while the automation path can submit content and files directly to an external service without the same visibility or confirmation.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The automated publishing mode accepts a local content file and cover image, but the documentation does not clearly warn that those local files will be uploaded to an external Toutiao service. Without that disclosure, users may unintentionally transmit sensitive local data, especially if file paths are supplied by an agent or reused from prior context.

Known Vulnerable Dependency: python-dotenv==1.0.0 — 2 advisory(ies): CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)

Medium
Category
Supply Chain
Confidence
93% confidence
Finding
The dependency is pinned to python-dotenv 1.0.0, which is reported as affected by a symlink-following/arbitrary file overwrite issue in set_key. Even if this requirements file alone does not prove the vulnerable API is used, shipping a version with known advisories is a real supply-chain risk because the skill handles persistent authentication and may process local environment files or secrets during setup.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code reads persisted browser state from state.json and restores cookies into a live browser session, which affects authentication state and may involve sensitive session data. While there is a failure log on exception, there is no confirmation prompt, user-facing disclosure, or explanatory comment warning users that stored session cookies will be reused.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script performs a real publication by default whenever --dry-run is not set, and it does so without an explicit confirmation prompt immediately before the irreversible action. In an agent/automation context, this creates a meaningful risk of unintended external side effects such as accidental posting, publication of draft or attacker-supplied content, and reputational damage to the authenticated account owner.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("   This may take a minute...")

        # Run setup with system Python
        result = subprocess.run([sys.executable, str(setup_script)])
        if result.returncode != 0:
            print("❌ Failed to set up environment")
            sys.exit(1)
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
# Run the script
    try:
        result = subprocess.run(cmd)
        sys.exit(result.returncode)
    except KeyboardInterrupt:
        print("\n⚠️ Interrupted by user")
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
# Run the script
    try:
        result = subprocess.run(cmd)
        sys.exit(result.returncode)
    except KeyboardInterrupt:
        print("\n⚠️ Interrupted by user")
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
print("📦 Installing dependencies...")
            try:
                # Upgrade pip first
                subprocess.run(
                    [str(self.venv_pip), "install", "--upgrade", "pip"],
                    check=True,
                    capture_output=True,
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
)

                # Install requirements
                result = subprocess.run(
                    [str(self.venv_pip), "install", "-r", str(self.requirements_file)],
                    check=True,
                    capture_output=True,
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
# Install Chrome for Patchright (not Chromium!)
                print("🌐 Installing Google Chrome for Patchright...")
                try:
                    subprocess.run(
                        [
                            str(self.venv_python),
                            "-m",
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Low
Confidence
72% confidence
Finding
SQP-3 applies to all file types and covers language or locale policy violations. The document consistently instructs usage in Chinese and provides only Chinese natural-language examples, which can amount to a de facto language constraint without an explicit opt-in or justification.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The configuration defines persistent storage locations for browser state and authentication information, including `state.json` and `auth_info.json`. In this file, there is no user-facing disclosure, prompt, or warning comment indicating that login/session data may be written locally.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The automation relies on Chinese UI text such as 标题, 添加封面, 本地上传, 无封面, 发布成功, and related labels, which effectively forces operation in a Chinese-language interface. There is no opt-in, language selection, or explicit documentation in this file that the skill is region- or locale-specific.

Static analysis

No suspicious patterns detected.