Back to skill

Security audit

Tapd

Security checks for vulnerabilities and agentic risk

Overview

This TAPD skill mostly matches its stated purpose, but it needs review because it can send TAPD credentials to a configurable API host and provides broad generic read/write API access.

Install only if you trust the publisher and can control the environment. Use a short-lived, least-privilege TAPD token, keep TAPD_API_BASE_URL at the official TAPD API or a vetted internal host, avoid plaintext HTTP, and require explicit confirmation before any POST or batch operation that changes project data or sends content to a WeCom webhook.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/tapd_client_stdlib.py:42
Finding

Authentication Credentials Sent to an Unrestricted Configurable API Destination

Content
View full analysis
str: base = os.environ.get("TAPD_API_BASE_URL", DEFAULT_TAPD_API_BASE_URL) return base.rstrip("/") def _get_headers() -> dict: token = os.environ.get("TAPD_ACCESS_TOKEN") if token: return { "Authorization": f"Bearer {token}", "Content-Type": "application/json", "Via": "mcp", } user = os.environ.get("TAPD_API_USER") password = os.environ.get("TAPD_API_PASSWORD") if not user or not password: raise ValueError( "请设置 TAPD_ACCESS_TOKEN 或 TAPD_API_USER + TAPD_API_PASSWORD" ) auth = b64encode(f"{user}:{password}".encode()).decode() return { "Authorization": f"Basic {auth}", "Content-Type": "application/json", "Via": "mcp", } def request( method: str, endpoint: str, params: Optional[dict] = None, data: Optional[dict] = None, timeout: int = 30, ) -> dict: """ 发送 TAPD API 请求。URL 自动追加 ?s=mcp 或 &s=mcp。 :param method: GET 或 POST :param endpoint: 路径,如 "stories", "workspaces/get_workspace_info" :param params: GET 查询参数或 POST 时也可拼在 URL(部分接口习惯用 query) :param data: POST 请求体(JSON) :param timeout: 超时秒数 :return: 响应 JSON(dict) """ base = _get_base_url() url = f"{base}/{endpoint.lstrip('/')}" sep = "&" if "?" in url else "?" url = f"{url}{sep}s=mcp" if params: encoded = urllib.parse.urlencode(params, doseq=True) url = f"{url}&{encoded}" if "?" in url else f"{url}?{encoded}" headers = _get_headers() req_body = None if method.upper() == "POST" and data is not None: req_body = json.dumps(data).encode("utf-8") req = urllib.request.Request( url, data=req_body, ...[truncated 3312 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Tainted flow: 'req' from os.environ.get (line 111, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The HTTP destination is derived from the environment variable TAPD_API_BASE_URL without validation, and the request carries TAPD credentials in Authorization headers. If an attacker can influence the runtime environment, they can redirect requests to an arbitrary host and exfiltrate bearer tokens or Basic credentials, making this an SSRF-plus-secret-leak issue rather than a harmless configuration feature.

Content

Scanner excerpt · scripts/tapd_client_stdlib.py (reported line 112)May include surrounding context.

python
req_body = json.dumps(data).encode("utf-8")

    req = urllib.request.Request(url, data=req_body, headers=headers, method=method.upper())
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode("utf-8"))

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

主要用途大体仍是 TAPD API 客户端,且确实只用了 Python 标准库,没有第三方 HTTP 库;但描述与实现并不准确一致。最明显的是,声明的企业微信通知能力在代码中完全不存在。其次,描述称“不依赖 MCP”,而代码虽然不调用 MCP SDK/服务,却明确在每个请求中附加 mcp 标记(请求头 Via: mcp、URL 参数 s=mcp),这与声明存在表述冲突。再者,声明列举了大量具体 TAPD 资源与操作(评论、工作流、测试用例、Wiki、工时等)的查询/创建/更新,但实现中只有项目、需求/任务、缺陷、迭代、发布计划等专门封装,以及通用 GET/POST;这说明描述比实际实现更宽泛。综合看,应判定为描述与行为不完全匹配。

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Allowing a caller to override TAPD_API_BASE_URL and pair it with generic HTTP request capability turns this TAPD integration into a general outbound HTTP client. If an attacker can influence environment configuration or invoke the generic commands, the skill could send authenticated requests to attacker-controlled infrastructure, enabling SSRF-like behavior, credential leakage in Authorization headers, or unintended access to internal services.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation explicitly instructs users to invoke arbitrary endpoints through generic get/post commands, expanding the skill from a TAPD-specific integration into a broad HTTP API wrapper. In context, this is more dangerous because the skill also handles sensitive credentials via environment variables; broad endpoint freedom increases the chance of misuse for data exfiltration, access to unintended TAPD endpoints, or requests to untrusted services when combined with configurable base URLs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

这是 markdown 文件,适用缺少用户警告检查。L030 明确说明 stories/tasks 的 POST 可用于创建/更新,但文档整体未提示这些写操作会直接影响项目数据、应谨慎在生产项目中使用。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

L106 的 tcases/batch_save 会一次性创建最多 200 条记录,属于明显的数据改动操作。文档没有任何关于批量操作影响范围、误创建风险或执行前确认的提示。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The document includes an external enterprise WeChat webhook destination, which expands the skill from TAPD operations into outbound message delivery to a separate service. Without clear warnings about external transmission, users or downstream agents may send sensitive TAPD-derived content outside the primary system boundary, creating data leakage and unintended notification risks.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Manifest 明确说明该 Skill 还可“发送企业微信通知”,且模块文档甚至提到 BOT_URL 环境变量,但代码中没有任何针对企业微信 webhook 或通知发送的实现。技能声明的能力与实际代码行为不一致,属于描述与实现不匹配。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file contains natural-language documentation, CLI descriptions, and error/help text entirely in Chinese, including usage instructions and runtime messages. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly justified, which it is not here.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The generic get/post subcommands allow callers to supply any endpoint string, bypassing the narrower resource-specific interface described by the skill. In an agent context, this expands capability to arbitrary TAPD API reads and writes, increasing the blast radius for prompt injection, misuse, or accidental destructive operations against data the skill was not expected to touch.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

L054 说明 comments POST 会添加/更新评论,属于会影响用户数据的行为。作为技能说明文档,未对该写入行为提供任何显式提醒,用户可能把它当作只读查询参考。

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

模块顶部文档把 BOT_URL 列为可选环境变量,暗示脚本会使用它实现某种通知或机器人调用能力。实际代码中既未读取也未使用该变量,文档对代码能力形成了误导。

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.