Back to skill

Security audit

金融市场数据采集工具

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches financial data collection, but it needs review because it runs scripts with broad network and file access while importing code/config from hard-coded external user paths and disabling the browser sandbox.

Review before installing. Use it only in a contained environment, verify the external config.py and output/log paths, expect web-search queries and market symbols to be sent to third-party providers, and remove or constrain the hard-coded import paths and Chromium --no-sandbox setting before production use.

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

Warning
Location
scripts/collect_market_data.py:228
Finding

Chromium Sandbox Disabled for Untrusted External Content

Content
View full analysis

Vulnerability Details

File Location: scripts/collect_market_data.py, lines 228–250
Vulnerability Type: Browser sandbox weakening
Risk Level: Medium

Vulnerable Code

python
_selenium_driver = None

def get_selenium_driver():
    """Lazy-load Selenium WebDriver only when dynamic rendering is needed."""
    global _selenium_driver
    if _selenium_driver is None:
        try:
            from selenium import webdriver
            from selenium.webdriver.chrome.options import Options
            from selenium.webdriver.chrome.service import Service
            from selenium.webdriver.support.ui import WebDriverWait
            from selenium.webdriver.support import expected_conditions as EC
            from selenium.webdriver.common.by import By

            opts = Options()
            opts.add_argument("--headless=new")
            opts.add_argument("--disable-images")
            opts.add_argument("--no-sandbox")
            opts.add_argument("--disable-dev-shm-usage")
            opts.add_argument("--disable-gpu")
            opts.add_argument("--log-level=3")
            prefs = {"profile.managed_default_content_settings.images": 2}
            opts.add_experimental_option("prefs", prefs)

            _selenium_driver = webdriver.Chrome(options=opts)

Technical Analysis

The collector uses Selenium to render external financial websites while passing Chromium's --no-sandbox option. The browser sandbox is a defense-in-depth boundary that restricts compromised renderer processes from accessing the host environment. Disabling it is not necessary for the Skill's declared market-data collection function and exceeds the minimum privileges required for browser rendering.

This option does not itself create arbitrary code execution. Exploitation would require an additional browser or renderer vulnerability, or another method of compromising the browser process. However, if such a compromise occurs while an external page is being rende ...[truncated 1262 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove the sandbox-disabling argument:
python
opts = Options()
opts.add_argument("--headless=new")
opts.add_argument("--disable-images")
opts.add_argument("--disable-dev-shm-usage")
opts.add_argument("--disable-gpu")
  1. Run Chrome under a dedicated, non-administrative operating-system account with access restricted to only the required output and temporary directories.

  2. If the deployment environment cannot support Chromium sandboxing, run Selenium inside a hardened container or virtual machine with:

    • No host filesystem mounts except a narrowly scoped output directory.
    • A read-only root filesystem where practical.
    • Dropped Linux capabilities.
    • no-new-privileges enabled.
    • Resource limits for memory, CPU, processes, and execution time.
    • Network egress restricted to documented market-data domains.
  3. Keep Chromium and the matching WebDriver fully patched and version-compatible.

  4. Prefer direct HTTP APIs over browser rendering. If safe sandboxed rendering cannot be provided, disable the Selenium fallback rather than removing the browser security boundary.

  5. Do not execute this Skill as root or an administrator.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (34)

Tainted flow: 'headers' from os.getenv (line 395, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/collect_market_data.py (reported line 397)May include surrounding context.

python
return None
            headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
            payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
            resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
            if resp.status_code != 200:
                return None
            data = resp.json()

Tainted flow: 'headers' from os.getenv (line 71, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/collect_news_websearch.py (reported line 73)May include surrounding context.

python
try:
        headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
        payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
        resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
        if resp.status_code == 401:
            log.warning(f"  Bocha API Key 无效(401)")
            return None

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

声明描述的是一个用于金融日报准备的综合数据采集能力,包含五大内容分类:市场表现、政策、企业、经济数据、汇总。实际代码确实大体围绕金融市场数据采集展开,地区覆盖也基本一致,因此主方向是相符的。但从实现看,代码真正完成的主要是“市场表现”和“经济数据”两大类,并扩展了“美国就业市场”和“休市情况”等专项内容;而声明中明确列出的“政策动态”“企业动态”“环球市场速览”仅出现在初始化数据结构中,没有对应的实际采集实现或汇总生成逻辑。这属于能力描述与实际实现不一致,且是实质性缺项。另有本地 checkpoint 持久化、断点续采等行为,虽可视为支持性实现,但属于未声明的数据落盘能力。综合判断为存在描述与行为不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

声明描述的是一个完整的全球金融市场数据采集技能,涵盖市场表现、政策、企业、经济数据、汇总五大类;但当前代码块实际只是一个“Step 2”补充脚本。它不会独立完成完整采集,而是先检查并读取已有基础文件,缺失时直接退出,说明其真实职责依赖前置步骤。其次,代码不仅采集数据,还基于已有市场表现和经济数据自动生成多句总结性市场速览,这超出了单纯“采集数据”的表述。再次,企业类内容在实现上被明显收窄为科技企业相关新闻,而不是一般企业动态。最后,地区与分类覆盖也不完全按声明落实,尤其政策部分没有单列中国香港,且市场表现类并非本脚本采集。因此,声明与该代码块的实际行为存在实质性不一致。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

声明描述的核心能力是“采集全球金融市场数据”,即面向美国、中国、香港、欧洲、亚太市场的数据获取与日报前置收集。但提供的代码块没有任何网络请求、搜索调用、API访问、行情查询或按地区/分类采集金融数据的逻辑。相反,它是一个内容清洗模块:清理搜索结果噪音、检测乱码、判断文本是否截断、补句号、繁体转简体、过滤低质量内容、清洗政策/企业/经济日历条目并批量去重。这些功能更像是采集后的后处理步骤,而不是声明中的主功能本身。根据评估标准,这属于主目的与实际行为存在实质差异,因此应判定为不匹配。

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| `collect_news_websearch.py` | 政策类 + 企业类 + 汇总类 | Web Search(Tavily AI搜索) |

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares no explicit tool scope or permission boundaries, yet its documented behavior includes shell execution, filesystem writes, environment/config access, and outbound network/web-search activity. In an agent setting, missing scope declarations increase the chance of over-privileged execution or accidental misuse beyond what a user would reasonably expect.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrases are broad and likely to match ordinary user requests such as asking for market data or a daily summary, which can cause the skill to run unintentionally. Because this skill performs shell commands, local file writes, and third-party network access, accidental activation can create unanticipated side effects and unnecessary data exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill documents writing outputs and logs to local filesystem paths but does not warn users about these side effects. Unannounced persistence can leak sensitive prompts, market research context, or operational metadata onto disk, especially in shared or managed environments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill performs external network requests and web searches to multiple third-party services without a prominent user-facing warning. This can transmit user intent, query contents, or derived sensitive business context to external providers, creating privacy, compliance, and supply-chain risk.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script prepends a hard-coded user-specific local workspace path to sys.path, causing Python imports to resolve from an uncontrolled local directory before standard locations. In a skill context, this weakens code integrity: a malicious or unexpected file in that workspace (such as config.py or another imported module) could be imported and executed implicitly. The market-data purpose does not justify modifying import precedence to a personal filesystem path, which makes this more suspicious and dangerous.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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/collect_market_data.py (reported line 499)May include surrounding context.

python
log.warning(f"  ⚠️ TickDB 查询跳过: apiKey 为空 (symbols={symbols})")
        return {}
    try:
        url = f'https://api.tickdb.ai/v1/market/ticker?symbols={symbols}'
        r = http.get(url, headers={'X-API-Key': key}, timeout=timeout, retry=2, delay_range=(0.5, 1.5))
        if r is None:
            log.warning(f"  ⚠️ TickDB 查询返回 None: symbols={symbols}")

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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/collect_market_data.py (reported line 524)May include surrounding context.

python
log.warning(f"  ⚠️ TickDB 查询跳过: apiKey 为空 (symbols={symbols})")
        return {}
    try:
        url = f'https://api.tickdb.ai/v1/market/ticker?symbols={symbols}'
        r = http.get(url, headers={'X-API-Key': key}, timeout=timeout, retry=2, delay_range=(0.5, 1.5))
        if r is None:
            log.warning(f"  ⚠️ TickDB 查询返回 None: symbols={symbols}")

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest describes collecting global financial market data for a daily market brief, covering market performance, policy, corporate, economic data, and summaries. This section goes further by building a dedicated U.S. employment-analysis module that extracts consensus expectations, revision narratives, layoff-cause breakdowns, and nonfarm-payroll outlooks from search results, which is closer to analytical forecasting content than straightforward market-data collection.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script prepends user-specific filesystem paths to sys.path, causing imports like config and content_cleaner to resolve from external directories outside the skill package. In a hostile or shared environment, an attacker who can write to those directories could plant a malicious module that executes arbitrary code at import time, making this a realistic local code-execution/supply-chain risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script sends search queries to Tavily via client.search(...), and similar outbound requests are also made to Bocha and DuckDuckGo elsewhere in the file. Although the file contains logging and function comments, there is no clear user disclosure that query contents will be transmitted to third-party search providers, which is relevant because queries may encode user/system context or internal reporting scope.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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/collect_market_data.py (reported line 397)May include surrounding context.

python
try:
        headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
        payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
        resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
        if resp.status_code == 401:
            log.warning(f"  Bocha API Key 无效(401)")
            return None

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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/collect_news_websearch.py (reported line 73)May include surrounding context.

python
try:
        headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
        payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
        resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
        if resp.status_code == 401:
            log.warning(f"  Bocha API Key 无效(401)")
            return None

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/collect_market_data.py (reported line 397)May include surrounding context.

python
try:
        headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
        payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
        resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
        if resp.status_code == 401:
            log.warning(f"  Bocha API Key 无效(401)")
            return None

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/collect_news_websearch.py (reported line 73)May include surrounding context.

python
try:
        headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
        payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
        resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
        if resp.status_code == 401:
            log.warning(f"  Bocha API Key 无效(401)")
            return None

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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/collect_market_data.py (reported line 397)May include surrounding context.

python
try:
        headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
        payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
        resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
        if resp.status_code == 401:
            log.warning(f"  Bocha API Key 无效(401)")
            return None

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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/collect_news_websearch.py (reported line 73)May include surrounding context.

python
try:
        headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
        payload = {'query': query, 'freshness': 'noLimit', 'summary': True, 'count': max_results}
        resp = requests.post('https://api.bocha.cn/v1/web-search', headers=headers, json=payload, timeout=15)
        if resp.status_code == 401:
            log.warning(f"  Bocha API Key 无效(401)")
            return None

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The comment states '仅采集原始数据条目,不做解读,不做分析', but the implementation extracts inferred release times into '发布时间点', labels missing times as '待确认', deduplicates items, and truncates text into summaries. Those steps go beyond merely storing raw entries and contradict the stated intent of no interpretation/analysis.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest describes this skill as collecting market data for a daily financial report, but this function synthesizes qualitative conclusions such as market sentiment, policy impact, and macro outlook rather than just collecting raw inputs. That is a semantic expansion from data gathering/preparation into report-style analysis generation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file contains natural-language instructions and behavior that always convert text to Simplified Chinese (zh-cn), reinforced by Chinese-language comments describing this as the standard behavior. This forces a specific language/locale transformation without any visible opt-in, configurability, or stated region-specific justification, which matches the policy-violation criteria.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.