T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_company_data.py:70- Finding
TLS Certificate Verification Disabled for Authenticated iFinD Requests
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill appears to generate the promised stock report, but it handles financial-service credentials and cross-skill code in ways that need review before installation.
Install only if you are comfortable granting this skill access to your Tushare and iFinD credentials and letting it contact external financial/search services. Review or fix the disabled TLS verification, cross-skill Python import, output path validation, and Markdown/PDF sanitization before using it with valuable credentials or sensitive research targets.
scripts/fetch_company_data.py:70TLS Certificate Verification Disabled for Authenticated iFinD Requests
scripts/generate_markdown_v6.py:31Untrusted External Content Is Rendered Without HTML or Resource Sanitization
scripts/generate_markdown_v6.py:73Runtime Execution of an Unverified Module from Another Mutable Skill Directory
scripts/onepager.py:22Unvalidated Stock Code Can Influence Output Paths
SKILL.md:143Third-Party Dependencies Are Installed Without Version or Integrity Pinning
该描述与代码存在实质不一致。描述强调这是一个‘生成一页纸调研简报’的技能,并承诺整合后输出 Markdown + PDF,还声明了 iFinD → Tushare → AkShare + Web Search 的数据优先级。实际代码是一个数据采集脚本,负责拉取多类股票/公司数据并保存为 JSON 文件,没有看到任何报告排版、Markdown 生成、PDF 导出或章节标注输出逻辑。数据来源方面,代码仅使用 Tushare 与 iFinD,没有 AkShare 或 Web Search 的实现;而且基本信息、市场数据、财务、K线、股东等主体内容主要都来自 Tushare,不符合‘iFinD 优先’的声明。另有一个额外能力是抓取公司近一年资本市场活动,这在描述中没有明确列为简报章节。综合来看,主功能被描述为“生成报告”,但代码实际只是“抓取并落盘数据”,因此应判定为不匹配。
This code chunk implements only a narrow sub-function: generating a 10-year monthly K-line style chart with turnover and Zigzag trend lines from already-supplied JSON data. The declared description presents a much broader end-to-end equity research brief generator with multi-source data acquisition, report composition, multiple content sections, and Markdown/PDF export. While the charting behavior is one component mentioned in the description, the actual code shown does not accurately represent the declared overall capability on its own. Therefore this chunk materially under-implements the declared purpose and has a different primary behavior.
The skill explicitly references another skill's MCP config file path and instructs reading an auth_token from it. Accessing tokens from shared configuration locations is sensitive because it expands the blast radius across skills and can expose or misuse credentials intended for a different component.
| 凭证 | 类型 | 配置方式 | 获取途径 |
|------|------|----------|----------|
| **TUSHARE_TOKEN** | 环境变量 | `export TUSHARE_TOKEN="your_token"` 或写入 `~/.bashrc` | https://tushare.pro |
| **iFinD auth_token** | 配置文件 | `~/.openclaw/workspace/skills/ifind-finance-data/mcp_config.json` 中的 `auth_token` 字段 | iFinD终端 → 工具 → 常用工具 → 数据MCP |
**配置示例:**
Referenced artifact was not completely inspected
| `fetch_company_data.py` | 数据获取(iFinD→Tushare→AkShare降级) |
The skill advertises and instructs use of environment variables, local config files, shell execution, network access, and file output, but it does not declare any explicit tool/permission scope. This creates an over-privileged execution model where the agent may grant broader capabilities than users expect, increasing the risk of unintended secret access, network exfiltration, or filesystem modification.
该 markdown 文件整体以中文编写并将数据源规则、字段映射和调用示例固定为中文表述,但未说明这是仅面向中文用户或中国市场合规场景的限定,也未提供语言/locale 选择。根据规则,强制特定语言而无用户 opt-in 属于自然语言政策违规。
The script's natural-language description and all user-facing console messages are fixed in Chinese, with no indication that language selection is optional or that the tool is intentionally limited to a Chinese-only audience. The policy calls for flagging language or locale constraints when they are forced without user opt-in or clear justification.
The script reads an iFinD auth token from a local configuration file and transmits it in Authorization headers to an external service, while treating all content and network paths as trusted. If the configured endpoint is intercepted, misconfigured, or contacted over a weakened TLS setup elsewhere in the script, the credential can be exposed and reused to access the third-party account or associated data.
This code sends stock queries and an authorization token to the external iFinD service via HTTP requests, but the script provides no disclosure before transmission beyond generic progress messages. For code files, network calls that transmit user or system data should have some user-facing warning, confirmation, or clearly documented notice.
The request disables TLS certificate verification with verify=False during session initialization to the external iFinD MCP endpoint. This makes the connection vulnerable to man-in-the-middle interception, allowing an attacker on the network path to spoof the server, capture the Authorization token, and tamper with returned data used in downstream report generation.
}
}
resp = session.post(server_url, json=init_payload, headers=headers, verify=False, timeout=30)
session_id = resp.headers.get("Mcp-Session-Id")
if session_id:
The tool call to fetch company introduction data also disables TLS verification, exposing both the auth header and the returned content to interception or modification. Because the response content is incorporated into generated reports, an attacker could silently poison research output in addition to stealing credentials.
}
}
resp = session.post(server_url, json=call_payload, headers=headers, verify=False, timeout=60)
data = resp.json()
if "result" in data:
The manifest describes a one-pager that integrates basic company info, market data, 10-year financial tables, 10-year monthly K-line charts, shareholder structure, and recent news. This file additionally retrieves '近一年资本市场活动(并购、投融资、回购、股权激励等)', which is a substantive extra content category not mentioned in the stated report scope or source-priority description.
This capital-activities initialization request disables certificate verification, creating the same MITM exposure for session establishment and token transmission. An attacker could hijack the session setup and influence or observe all subsequent interactions with the external service.
}
}
resp = session.post(server_url, json=init_payload, headers=headers, verify=False, timeout=30)
session_id = resp.headers.get("Mcp-Session-Id")
if session_id:
The capital-activities data request runs over a connection that does not verify server certificates, so returned activity data can be manipulated in transit. In a financial-research context, integrity of the fetched content matters; poisoned event data could mislead users and degrade trust while also exposing bearer credentials.
}
}
resp = session.post(server_url, json=call_payload, headers=headers, verify=False, timeout=60)
data = resp.json()
if "result" in data:
The news-session initialization request also disables certificate validation, again exposing authentication material and session metadata to network attackers. Since the skill aggregates third-party news into saved output, tampering at this stage can undermine both confidentiality and integrity.
}
}
resp = session.post(news_url, json=init_payload, headers=headers, verify=False, timeout=30)
session_id = resp.headers.get("Mcp-Session-Id")
if session_id:
The news search request transmits queries and receives content over a connection with verify=False, allowing interception and manipulation of both request and response. This is especially risky because the fetched content is later stored locally and may feed user-visible summaries or reports, making silent data poisoning plausible.
}
}
resp = session.post(news_url, json=call_payload, headers=headers, verify=False, timeout=60)
data = resp.json()
if "result" in data:
The script's natural-language descriptions, headings, messages, and generated report content are consistently fixed in Chinese, indicating the skill forces a specific language/locale. There is no apparent user opt-in or configurable language selection to accommodate different locale preferences.
The documentation says the function gets company information via the OpenClaw system web_search tool, implying actual search execution. In practice, the code modifies sys.path to import a local brave_search_api module, and if that import fails it only logs a message and returns an empty list rather than calling any system tool.
The skill sends company-derived queries to an external search provider without explicit user consent or a clear notice that report generation causes outbound network transmission. In environments where queried entities, watchlists, or internal research targets are sensitive, this can leak confidential business interest or workflow context to third parties.
The function documentation promises actual web search execution through either a system tool or Brave API. However, the except branch only prints that it will use a system tool and immediately returns an empty list, so the documented behavior contradicts what the code really does.
The module docstring and subsequent user-facing messages are written in Chinese, which imposes a specific language on users without any opt-in or indication that the tool is intentionally limited to a Chinese-speaking context. The policy explicitly calls for flagging language or locale constraints when they are forced rather than optional or justified.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def generate_pdf_pypdf(html_path: str, pdf_path: str) -> bool:
"""使用 wkhtmltopdf 作为备选"""
try:
result = subprocess.run(
['wkhtmltopdf', '--encoding', 'utf-8', html_path, pdf_path],
capture_output=True, text=True
)
This code file contains natural-language strings and documentation that force a specific language for descriptions, CLI help, and status output. Under the policy, a skill should not impose a language or locale without user opt-in or clear justification that the tool is region-specific.
The docstring at L448 states the function gets recent news '用于模型汇总摘要', implying summarization-oriented behavior. In practice, the function only queries iFinD, parses results, and returns raw news entries; no model call or summarization occurs in this code path.
The script creates a directory and writes aggregated company data to ~/.openclaw/workspace/temp/.../data.json, but there is no prior warning or explanatory comment describing that local files will be created. Although it prints the saved path afterward, the operation lacks advance disclosure about file creation and persistence.
No suspicious patterns detected.