Back to skill

Security audit

投票鸭制作助手

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent voting-service integration, but it can send saved authentication tokens to caller-controlled API endpoints and perform account-changing actions without strong scoping.

Review before installing. Only use this skill with trusted Toupiaoya endpoints, avoid custom --base-url or related environment overrides, do not upload sensitive local files, and treat the saved token as account authority that can create or modify remote voting projects.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/toupiaoya/cli.py:15
Finding

Authenticated API requests can be redirected to arbitrary endpoints

Content
View full analysis

Vulnerability Details

File Location: scripts/toupiaoya/cli.py:15-23, scripts/toupiaoya/commands/project_cmd.py:218-242, scripts/toupiaoya/create.py:23-24, 62-63, scripts/toupiaoya/http.py:24-36, scripts/toupiaoya/project_api.py:101-111, 125-140, 151-166, 237-245, 256-266, 277-292, and scripts/toupiaoya/constants.py:16-18
Vulnerability Type: Credential disclosure through an unvalidated authenticated endpoint override
Risk Level: High

Vulnerable Code

The top-level CLI permits an unrestricted API base URL:

python
parser.add_argument(
    "--base-url",
    default=DEFAULT_BASE_URL,
    help=(
        "API root address (environment variable TOUPIAOYA_API_BASE or EQXIU_AIGC_API_BASE; "
        f"default is {DEFAULT_BASE_URL!r})"
    ),
)

The project creation handler automatically reads the persistent token and combines it with the caller-controlled base URL:

python
def _run_create(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
    _ = parser
    cfg = load_config()
    token_cli = getattr(args, "access_token", None)
    token = (token_cli or "").strip() or token_from_config(cfg)
    if not token:
        print(
            "Notice: X-Openclaw-Token is not configured; run `login` or provide --access-token.",
            file=sys.stderr,
        )
    creator = ToupiaoyaCreator(base_url=args.base_url)
    group_list = parse_group_list(args.groupList)
    single_vote = not getattr(args, "multi", False)
    result = creator.execute(
        brief_title=args.briefTitle,
        brief_desc=args.briefDesc,
        detail_title=args.detailTitle,
        detail_desc=args.detailDesc,
        vote_type=args.voteType,
        time_start=args.timeStart,
        time_end=args.timeEnd,
        group_list=group_list,
        template_id=args.templateId,
        single_vote=single_vote,
        sponsor=args.sponsor,
        access_to
...[truncated 5196 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove public endpoint overrides from production-facing CLI commands unless they are essential.
  2. Parse every authenticated destination with urllib.parse.urlparse and require:
    • The https scheme.
    • No embedded username or password.
    • An exact approved hostname.
    • An expected port, normally 443.
  3. Maintain an explicit allowlist of authenticated service origins, such as the required Toupiaoya API hosts. Do not use suffix-only checks that could accept names such as toupiaoya.com.attacker.example.
  4. Implement destination validation centrally in scripts/toupiaoya/http.py before adding X-Openclaw-Token.
  5. Disable redirects for authenticated requests or manually follow only same-origin HTTPS redirects.
  6. If custom development endpoints must remain available:
    • Require an explicit opt-in flag.
    • Never automatically load the persistent production token for a custom endpoint.
    • Require a separately supplied development token.
    • Display the exact destination and request explicit user confirmation.
  7. Validate environment-derived endpoints during startup and fail closed if they are not approved.
  8. Add tests proving that credentials are not sent to HTTP URLs, unapproved hosts, deceptive subdomains, alternate ports, or cross-origin redirects.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:203
Finding

Runtime dependencies are installed without version or integrity pinning

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:203 and scripts/toupiaoya/cos_upload.py:76-80
Vulnerability Type: Unpinned third-party dependency installation
Risk Level: Medium

Vulnerable Code

The Skill documentation instructs users to install mutable latest versions:

text
Dependencies: the repository-root requirements.txt contains cos-python-sdk-v5 and requests;
when using the Skill directory independently, run:
pip install cos-python-sdk-v5 requests

The runtime error repeats the same unpinned installation instruction:

python
try:
    from qcloud_cos import CosConfig, CosS3Client
except ImportError as e:
    raise ImportError(
        "Install the Tencent Cloud COS SDK first: pip install cos-python-sdk-v5"
    ) from e

The supplied project directory structure contains no requirements.txt, despite the documentation referring to one.

Technical Analysis

The installation commands specify package names but no reviewed versions or integrity hashes. Consequently, installation resolves whatever releases are current on the configured Python package index at execution time.

This makes the installed code materially different from the code reviewed during this audit. A compromised future package release, compromised package index, malicious mirror, or unsafe local pip configuration could introduce code that runs with the privileges of the user invoking the Skill.

The risk is particularly relevant because these dependencies execute in a process that handles:

  • The persistent X-Openclaw-Token.
  • Temporary Tencent COS secret IDs, secret keys, and session tokens.
  • Local files selected for upload.
  • Authenticated project and material data.

No evidence was found that the named packages are typosquatted or currently malicious. The confirmed issue is the absence of reproducible dependency constraints and integrity verification.

Attack Path

  1. A user follo ...[truncated 1400 chars]
Remediation
View remediation

Remediation Suggestions

  1. Add the dependency manifest referenced by the documentation.

  2. Pin each direct and transitive dependency to a reviewed version.

  3. Generate and verify cryptographic hashes, for example with a requirements file used through:

    bash
    python -m pip install --require-hashes -r requirements.txt
    
  4. Use a lock-file workflow that records all transitive dependencies for the supported Python versions and platforms.

  5. Document the expected official package index and warn against untrusted mirrors.

  6. Replace inline unpinned installation instructions with installation from the audited lock file.

  7. Run automated dependency vulnerability and provenance checks when updating pins.

  8. Review updates before changing the lock file, especially for packages that process credentials, network traffic, or uploaded file content.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

根据提供的代码片段,这部分功能专注于认证:交互式登录、从配置文件读取 token、接收命令行 token 参数、检查认证状态。声明描述则强调投票模板、投票作品、素材、作品数据及选项/分组管理等业务能力。两者在主要功能上明显不一致。虽然认证可能是整个系统的配套实现细节,但该代码片段本身并未体现声明中的任何核心投票功能,反而实现了描述未列出的认证命令,因此应判定为描述与行为不匹配。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README instructs users to create vote projects by sending titles, descriptions, scheduling, and option/group content to a third-party HTTPS API, but it does not clearly warn that this content leaves the local environment and is transmitted to an external service. In an agent skill context, that omission can cause users or calling agents to unknowingly disclose sensitive or proprietary content through the skill.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill describes and demonstrates capabilities that require network, shell, file read/write, and access to locally stored tokens, but it declares no explicit tool scope or permissions boundaries. In an agent environment, that omission can let the skill be invoked with broader powers than users expect, increasing the chance of unintended command execution, credential use, local file access, or external data exfiltration.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrases and read_when rules are very broad and overlap with ordinary conversation about voting, rankings, or polls. In an agent setting, that can cause accidental activation of a skill that has network, shell, file, and token-using capabilities, leading to unintended external requests or changes to user resources without sufficiently specific intent.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
91% confidence
Finding

The skill instructs the system to persist and automatically reuse an access token from ~/.toupiaoya/config.json for subsequent requests. Automatic session persistence is risky in agent contexts because a later invocation may silently act under previously stored credentials, enabling unintended account actions, cross-task confusion, or disclosure through local file access.

Content

Scanner excerpt · SKILL.md (reported line 116)May include surrounding context.

md
- `detailDesc` : 详细的描述,例如规则,投票时间等介绍
- `voteType`:`imageVote` | `textVote`(默认) | `videoVote`
- `timeStart`、`timeEnd`:格式 `yyyy-MM-dd HH:mm`
- `groupList`
- `templateId`(默认 `19980321`代表空白模版),可以使用搜索出的templateId
- 单双选控制:默认单选;传 `--multi` 表示多选。
- `--access-token`:可显式传 token;不传则默认读取 `~/.toupiaoya/config.json`。

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

This example shows a state-changing creation command that can proceed using a locally persisted token rather than requiring fresh user authorization. In an agent environment, that makes it easier for an unintended or ambiguously triggered invocation to create external resources under the user's account.

Content

Scanner excerpt · SKILL.md (reported line 135)May include surrounding context.

--voteType "textVote"
--templateId 19980321
--multi
--groupList '[{"groupName":"默认组","choices":[{"name":"作品1"},{"name":"作品2"}]}]'

text

图片投票:

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The image-vote creation flow similarly relies on automatic header injection from --access-token or local configuration, which can cause authenticated write actions to occur without deliberate credential selection. Because this action creates externally hosted content, accidental execution can have persistent user-visible effects.

Content

Scanner excerpt · SKILL.md (reported line 147)May include surrounding context.

--detailDesc "投票规则:每人每天可投一次。"
--voteType "imageVote"
--templateId 19980321
--groupList '[{"groupName":"主推","choices":[{"name":"作品1","cover":"https://example.com/p1.png"},{"name":"作品2","cover":"https://example.com/p2.png"}]}]'

text

说明:`project create` 会自动在请求头中带 `X-Openclaw-Token`(来源:`--access-token` 或本地配置)。

Session Persistence

Medium
Category
Rogue Agent
Confidence
87% confidence
Finding

Even for read operations like fetching all choices, reuse of a persisted token means the skill may access account-scoped data from local state without a fresh authorization decision. In shared or long-lived agent sessions, this can expose project contents across tasks or users if the environment is not carefully isolated.

Content

Scanner excerpt · SKILL.md (reported line 251)May include surrounding context.

获取作品全部选项(get_choices)

  • 语义:按作品 id 读取当前投票组件中的所有选项,不再是“根据 groupList 生成选项”。
  • CLI 命令:
bash

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code sends the provided access token in the X-Openclaw-Token header to a remote endpoint using requests.get. Although the file documents local token storage during login, there is no user-facing disclosure, log, comment, or prompt near this network transmission explaining that the credential will be sent to an external service.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

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.

Content

Scanner excerpt · scripts/toupiaoya/commands/project_cmd.py (reported line 12)May include surrounding context.

python
from toupiaoya.auth import resolve_token
from toupiaoya.config import load_config, token_from_config
from toupiaoya.create import ToupiaoyaCreator, parse_group_list
from toupiaoya.project_api import (
    add_project_group,
    add_project_choice,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This function opens a local file and uploads its contents to Tencent COS using temporary credentials, which is a safety-relevant file read plus external network transmission. The code contains no confirmation prompt, no user-facing log/print, and no inline warning explaining that local data will be sent to a remote storage service.

Content

No source excerpt is available for this finding.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · scripts/toupiaoya/cos_upload.py (reported line 99)May include surrounding context.

python
key = object_key.lstrip("/")
    with local_path.open("rb") as fp:
        resp = client.put_object(Bucket=bucket, Body=fp, Key=key, EnableMD5=False)

    etag = (resp.get("ETag") or resp.get("Etag") or "").strip('"')
    out: dict[str, Any] = {

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · README.md (reported line 104)May include surrounding context.

md
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · README.md (reported line 123)May include surrounding context.

md
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · README.md (reported line 136)May include surrounding context.

md
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · README.md (reported line 139)May include surrounding context.

md
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · SKILL.md (reported line 260)May include surrounding context.

md
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · SKILL.md (reported line 271)May include surrounding context.

md
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · scripts/toupiaoya/commands/project_cmd.py (reported line 47)May include surrounding context.

python
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · scripts/toupiaoya/commands/project_cmd.py (reported line 228)May include surrounding context.

python
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · scripts/toupiaoya/create.py (reported line 15)May include surrounding context.

python
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

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.

Content

Scanner excerpt · scripts/toupiaoya/create.py (reported line 57)May include surrounding context.

python
return None
    parsed = json.loads(group_list_text)
    if not isinstance(parsed, list):
        raise ValueError("--groupList 必须是 JSON 数组")
    return parsed

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code constructs a remote URL and posts user-supplied voting data to it via post_json, with an optional access_token included for authentication. There is no confirmation prompt, user-facing log message, or inline warning in this file disclosing that data will be transmitted to an external service.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/toupiaoya/http.py (reported line 36)May include surrounding context.

python
headers: dict[str, str] = dict(extra_headers or {})
    if access_token:
        headers["X-Openclaw-Token"] = access_token
    return requests.post(url, json=dict(json_body), headers=headers or None, timeout=timeout)


def post_multipart(

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This Python file contains multiple POST-based update operations such as updating choices, adding choices, adding groups, and updating groups, which can modify remote project data. The code provides no confirmation prompt, warning message, or explanatory comment/docstring disclosing that these functions make state-changing requests affecting user-managed content.

Content

No source excerpt is available for this finding.