Back to skill

Security audit

Amap Poi Fetch

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it advertises, but it should be reviewed because it ships a reusable Amap API key and can create Excel files from third-party data without formula safeguards.

Install only after replacing the bundled Amap key with your own restricted key, or treating the bundled key as already exposed. Prefer a pinned dependency in a virtual environment. Generated Excel files contain third-party POI text, so open them with external links and active content disabled, or use `--skip-excel` when XLSX output is not needed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/poi_fetch.py:10
Finding
Hard-Coded Amap API Credential Exposed in Source Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/poi_fetch.py:10-11` **Vulnerability Type**: Hard-coded API credential **Risk Level**: High ### Vulnerable Code ```python DEFAULT_KEY = "0c166a2bf61c1e4e6c96e3b645233e54" # 默认key(主人已创建) KEY = os.environ.get("AMAP_KEY", DEFAULT_KEY) ``` The credential is also disclosed in `SKILL.md:59`: ```markdown | KEY | 默认使用主人已申请的KEY(`0c166a2bf61c1e4e6c96e3b645233e54`) | ``` It is subsequently included in Amap request URLs: ```python url = f"https://restapi.amap.com/v3/config/district?keywords={urllib.parse.quote(city_name)}&subdistrict=1&key={KEY}" ``` ```python url_base = (f"https://restapi.amap.com/v3/place/text?key={KEY}" f"&keywords={kw_enc}&city={adcode}&citylimit=true&offset=20&extensions=all") ``` ### Technical Analysis An owner-created API key is embedded directly in the distributed source code and repeated in the documentation. Environment-variable support does not protect the embedded credential because the hard-coded value is automatically used whenever `AMAP_KEY` is absent. The script also transmits the key as a URL query parameter. HTTPS protects the request in transit, but query strings may still be retained by application telemetry, proxy logs, exception reporting, browser-like tooling, or API-provider access logs. Embedding the key in the package gives every recipient the same credential and prevents effective per-user attribution or revocation. Network access to `restapi.amap.com` is consistent with the declared POI-fetching functionality. The security issue is not the use of the Amap API itself, but distributing a reusable owner credential rather than requiring callers to supply their own. ### Attack Path 1. An attacker downloads, receives, or otherwise reads the Skill package. 2. The attacker extracts the API key from `scripts/poi_fetch.py` or `SKILL.md`. 3. The attacker submits independent requests to supported Amap API endpoints using that key. 4. Requests consume the ...[truncated 677 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed API key immediately. 2. Remove the key from source code, documentation, release archives, and version-control history. 3. Require a credential through `AMAP_KEY` or `--key` and terminate safely if neither is supplied: ```python KEY = os.environ.get("AMAP_KEY") if not KEY: raise SystemExit( "An Amap API key is required. Set AMAP_KEY or provide --key." ) ``` 4. Avoid including secrets in diagnostic output and sanitize request URLs before logging. 5. Apply provider-side restrictions where available, including allowed API products, source IP ranges, referrers, quotas, and usage alerts. 6. Prefer separate, revocable credentials for each user or deployment rather than a shared package-wide credential. 7. Add automated secret scanning to the release and source-control workflow. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/poi_fetch.py:83
Finding
Excel Formula Injection Through Untrusted POI Fields<![CDATA[ ## Vulnerability Details **File Location**: `scripts/poi_fetch.py:83-86, 175-191` **Vulnerability Type**: Spreadsheet formula injection **Risk Level**: High ### Vulnerable Code The sanitization function converts values to strings but does not neutralize spreadsheet formulas: ```python def safe(v): if v is None: return "" if isinstance(v, (list, dict)): return str(v) if v else "" return str(v) ``` Remote POI fields are then written directly into workbook cells: ```python row_data = [ district, safe(poi.get("name","")), safe(poi.get("keytag","")), safe(poi.get("tel","")), safe(poi.get("address","")), safe_float(biz_ext.get("rating","")), safe_float(biz_ext.get("cost","")), safe(biz_ext.get("opentime2","")), safe(poi.get("business_area","")), lng, lat ] for col, val in enumerate(row_data, 1): c = ws2.cell(row=row, column=col, value=val) c.font = Font(name="微软雅黑", size=9) c.border = border c.alignment = left_align if col in [2,5,8] else center ``` ### Technical Analysis Values such as the POI name, tags, telephone number, address, operating hours, and business area originate from a remote data source. The application treats these fields as trusted spreadsheet content. When `openpyxl` receives a string beginning with `=`, it may serialize it as a formula cell rather than literal text. Other spreadsheet software and import paths may also treat leading `+`, `-`, or `@` characters as formula indicators. The existing `safe()` function only normalizes data types and therefore does not provide formula-injection protection. An attacker would need to control or influence a POI record returned for the selected search query. A malicious value could then be incorporated into the generated workbook and evaluated when an analyst opens it. The result depends on the spreadsheet application's formula support, external-content protections, and user security settings. ### Attack Path 1. An attacke ...[truncated 1328 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply spreadsheet-specific neutralization to every remotely sourced textual field. 2. Prefix formula-indicating values with an apostrophe and force the destination cell to use a text format: ```python def safe_excel_text(v): if v is None: return "" if isinstance(v, (list, dict)): value = str(v) if v else "" else: value = str(v) if value.startswith(("=", "+", "-", "@")): value = "'" + value return value ``` 3. Use this function for POI names, tags, telephone numbers, addresses, operating hours, business areas, district names, and any future external fields. 4. Explicitly assign text formatting where appropriate: ```python c = ws2.cell(row=row, column=col) c.number_format = "@" c.value = safe_excel_text(val) ``` 5. Keep numeric fields under strict numeric conversion rather than passing arbitrary strings. 6. Add regression tests using payloads such as `=1+1`, `=HYPERLINK(...)`, `+SUM(1,1)`, `-1+2`, and `@SUM(1,1)`. 7. Document that generated workbooks contain third-party POI data and should be opened with external links and active content disabled. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:69
Finding
Unpinned Runtime Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:69-73` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```markdown ## 依赖 ```bash pip install openpyxl ``` ``` ### Technical Analysis The installation instruction resolves the latest available `openpyxl` release and its applicable transitive dependencies at installation time. No reviewed version constraint, lockfile, package hash, controlled index, or isolated environment is specified. Consequently, installations are not reproducible. A future compromised, malicious, or incompatible upstream release could be selected without any change to this Skill's reviewed source. Python packages may execute package-controlled logic during installation or later when imported by `export_excel()`. No evidence was found that `openpyxl` is currently malicious or that the Skill intentionally uses dependency confusion or an untrusted package index. The finding concerns the uncontrolled supply-chain boundary created by the documented installation procedure. ### Attack Path 1. A user follows the documented `pip install openpyxl` instruction. 2. `pip` resolves whichever release is current and compatible at that time. 3. If the selected upstream release, transitive dependency, package index, or distribution artifact has been compromised, malicious package code is installed. 4. Package-controlled code may execute during installation or when the script imports `openpyxl`. 5. Execution occurs with the permissions of the user running `pip` or the Skill. ### Impact Assessment A compromised dependency could obtain the same local privileges as the installing or executing user, including access to that user's readable files, environment variables, network connectivity, and writable directories. Installing globally or with elevated privileges would increase the scope. Under normal non-elevated installation, this issue does not independently provide administrative privil ...[truncated 163 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `openpyxl` to a reviewed version in a dependency manifest. 2. Generate and verify cryptographic hashes, for example through a locked requirements file: ```text openpyxl==<reviewed-version> \ --hash=sha256:<verified-distribution-hash> ``` 3. Install with hash enforcement: ```bash python3 -m pip install --require-hashes -r requirements.txt ``` 4. Review and lock applicable transitive dependencies as well. 5. Use a dedicated virtual environment rather than a global or privileged Python installation. 6. Document the intended trusted package index and disable unintended extra indexes. 7. Periodically update pinned versions through a controlled review process that includes vulnerability and provenance checks. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

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

Critical
Category
Data Flow
Content
SKIP_EXCEL = "--skip-excel" in sys.argv

def fetch_json(url):
    with urllib.request.urlopen(url, timeout=10) as r:
        return json.loads(r.read().decode("utf-8"))

def get_districts(city_name):
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Ssd 3

High
Confidence
99% confidence
Finding
The documentation exposes a default owner API key in plaintext, which is effectively a credential disclosure. Anyone reading the skill can reuse the key for unauthorized API calls, leading to quota exhaustion, billing abuse, attribution problems, and possible service suspension for the legitimate owner.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill documentation describes capabilities to access the network, read environment-provided data such as API keys, and write files locally, but it does not declare any explicit tool scope or permission boundary. That increases the risk of overbroad execution and makes it harder for users or the platform to understand and constrain what the skill is allowed to do.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The trigger phrase “抓取POI” is very broad and overlaps with ordinary map, search, and data-query requests, which can cause accidental activation of a networked scraping and file-writing workflow. Unintended execution is dangerous here because the skill performs external collection and persists output locally, potentially without the user realizing a more invasive action was invoked.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill performs remote data collection and writes JSON and Excel artifacts to the local workspace, but the documentation does not present this as an explicit consent or disclosure step to the user. This can lead to silent data acquisition and local persistence, which is risky in agent environments where users may expect a simple lookup rather than scraping plus file creation.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The file's natural-language interface and usage text are entirely in Chinese, and the script prints Chinese-only runtime messages. Under the stated policy, forcing a specific language without user opt-in is a locale-policy issue unless the regional restriction is explicitly documented and justified.

Natural-Language Policy Violations

Low
Confidence
71% confidence
Finding
SQP-3 适用于所有文件。文档中的描述、触发词和示例均仅以中文给出,未说明是否要求中文输入,也未提供其他语言选项或用户可选择的 locale 行为。

Missing User Warnings

Low
Confidence
89% confidence
Finding
This code creates a directory under ~/.openclaw/workspace/data and persists fetched POI data there, later also saving an Excel export. Although there are progress prints, the initial user-facing description does not clearly warn that local files will be created and populated, which is the kind of disclosure this rule asks for on code files.

Static analysis

No suspicious patterns detected.