Back to skill

Security audit

apihz-cn

Security checks for vulnerabilities and agentic risk

Overview

The skill is a mostly coherent ApiHz API client, but its credential handling can expose user API keys through HTTP fallback paths and unvalidated catalog URLs.

Review before installing with real credentials. Use a low-privilege ApiHz account, rotate any exposed/test key, force HTTPS-only APIHZ_BASE_URL and APIHZ_LIST_URL, avoid HTTP fallback nodes, and do not use remotely supplied catalog URLs unless the destination is validated against trusted ApiHz hosts. Verify that .credentials/apihz.txt is ignored and owner-readable only before committing or sharing the workspace.

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

T09 · Insecure Skill Coding Practices

Error
Location
TEST-REPORT-FINAL.md:5
Finding
Live API Credential Published in the Test Report<![CDATA[ ## Vulnerability Details **File Location**: `TEST-REPORT-FINAL.md:5` **Vulnerability Type**: Hardcoded and publicly exposed secret **Risk Level**: High ### Vulnerable Code ```markdown **Test Date:** 2026-03-09 21:58 **Version:** v1.0.6 **Test Environment:** WSL2 (Ubuntu 24.04.4 LTS) **KEY:** 3d616fff11599a5cf52fcacb4c76f9f5 (encrypted storage) ``` The same report indicates that the credential was successfully decrypted and used during testing: ```markdown ✅ readConfig(): Passed - ID: 10013679 - KEY: Decrypted - Encrypted: Yes ``` ### Technical Analysis A complete 32-character value explicitly identified as the API communication key is committed to the repository. Describing the key as being stored in encrypted form does not protect it when its plaintext value is separately included in project documentation. The report also discloses the associated developer ID and states that the credential was successfully used for account verification, check-in, and API requests. This provides strong evidence that the value was an operational credential rather than an obvious placeholder. Anyone with access to the repository or its history can extract the credential without needing to defeat the project's AES-GCM storage mechanism. ### Attack Path 1. An attacker obtains the repository or reads the published test report. 2. The attacker extracts the plaintext API key from line 5. 3. The attacker obtains the associated developer ID from the same test report or other project documentation. 4. The attacker submits the ID and key to the documented ApiHz endpoints. 5. If the credential remains active, the attacker can act as the account, consume quotas, retrieve account information, or invoke paid APIs. ### Impact Assessment Successful exploitation can provide unauthorized use of the affected ApiHz account. The practical scope includes: - Consumption or exhaustion of API quotas. - Unauthorized access to APIs enabled for the account. - Retrieva ...[truncated 340 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed API key immediately. 2. Review service logs for unauthorized calls made with the exposed credential. 3. Remove the key from the current file and all repository history using a history-rewriting tool. 4. Replace operational values in test reports with unmistakably synthetic placeholders. 5. Add automated secret scanning to local pre-commit hooks and CI pipelines. 6. Store test credentials in a dedicated secret manager or protected CI secret rather than documentation. 7. Use a low-privilege, quota-limited test account for future integration testing. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/auth.js:481
Finding
Reusable Credentials Transmitted Through Plaintext HTTP Fallback Nodes<![CDATA[ ## Vulnerability Details **File Location**: `src/auth.js:481-493` **Additional Locations**: `src/auth.js:359,390,438,727`; `scripts/auto-checkin.js:134-145,194,223`; `src/client-enhanced.js:40,261` **Vulnerability Type**: Plaintext transmission of authentication credentials **Risk Level**: High ### Vulnerable Code The bulk API synchronization function intentionally bypasses the HTTPS-first request wrapper and sends credentials directly to an HTTP server: ```javascript const allApis = []; const categories = categoriesResult.categories; const baseUrl = this.backupServers[0]; // directly use HTTP backup node console.log(` Using node: ${baseUrl}`); console.log(''); for (const category of categories) { const categoryId = categories.indexOf(category) + 1; let page = 1; let maxPage = 1; do { const url = `${baseUrl}/api/xitong/apilist.php?id=${id}&key=${key}&type=2&cid=${categoryId}&page=${page}`; try { // Directly use HTTP request instead of request method const result = await this.httpRequest(url, 5000); ``` The configured fallback servers use unencrypted HTTP: ```javascript this.backupServers = options.backupServers || [ 'http://101.35.2.25', 'http://124.222.204.22', 'http://81.68.149.132' ]; ``` The daily check-in path also constructs credential-bearing URLs from an HTTP endpoint and falls back to those HTTP nodes when the primary HTTPS request fails: ```javascript async function checkIn(id, key) { const url = `${BACKUP_SERVERS[0]}/api/xitong/function.php?id=${id}&key=${key}&type=1`; try { const result = await request(url); ``` ```javascript async function request(url, timeout = 10000) { const primaryUrl = url.replace(BACKUP_SERVERS[0], PRIMARY_SERVER); try { return await httpRequest(primaryUrl, timeout); } catch (error) { console.log(`Primary server unavailable, trying backup node...`); } for (const server of BACKUP_SERVERS) { try { return await httpRequest(url.repla ...[truncated 2400 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove every plaintext HTTP endpoint from the code and default configuration. 2. Require `https:` for primary, backup, catalog, check-in, account-information, and dynamic-parameter endpoints. 3. Fail closed if HTTPS is unavailable; do not silently downgrade to HTTP. 4. Reject `APIHZ_BASE_URL` and `APIHZ_LIST_URL` values that do not use HTTPS. 5. Prefer authenticated POST bodies over URL query strings for credentials when supported by the service. 6. Do not include reusable secrets in error messages, logs, cache files, or telemetry. 7. Pin requests to an explicit allowlist of trusted ApiHz hostnames. 8. If IP-based fallback is operationally required, use an HTTPS endpoint with valid certificate verification for the intended hostname. 9. Rotate credentials that have previously been sent through the HTTP paths. 10. Update the documentation so its transport-security claims accurately reflect the implementation. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/call-api.js:154
Finding
Untrusted Remote Catalog URLs Can Receive Automatically Attached Credentials<![CDATA[ ## Vulnerability Details **File Location**: `scripts/call-api.js:154-167` **Additional Locations**: `src/client-enhanced.js:62-79`; `src/client.js:31-33` **Vulnerability Type**: Credential forwarding and server-side request forgery through unvalidated absolute URLs **Risk Level**: Critical ### Vulnerable Code The interactive caller uses an endpoint obtained from remotely supplied API catalog data: ```javascript if (methodName && client[methodName]) { return await client[methodName](params); } // Generic invocation const endpoint = api.apiurl; return await client.request(endpoint, params); ``` The enhanced client accepts that endpoint as either a relative or absolute URL and automatically attaches credentials: ```javascript async request(endpoint, params = {}, method = 'POST') { const url = new URL(endpoint, this.baseUrl); if (url.protocol === 'http:' && !this._httpWarningShown) { console.warn('Warning: HTTP is in use and credentials may be transmitted in plaintext.'); this._httpWarningShown = true; } const defaultParams = { id: this.id, key: this.key }; const allParams = { ...defaultParams, ...params }; ``` The resulting hostname and protocol are taken directly from the resolved URL: ```javascript const options = { hostname: url.hostname, port: url.port || (url.protocol === 'https:' ? 443 : 80), path: url.pathname + (method === 'GET' ? '?' + querystring.stringify(allParams) : ''), method: method, timeout: this.timeout, agent: false, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Mozilla/5.0', 'Connection': 'close' } }; const req = (url.protocol === 'https:' ? https : http).request(options, (res) => { ``` For POST requests, the same credentials are written to the request body: ```javascript if (method === 'POST') { req.write(querystring.stringify(allParams)); } ``` ### Technical Analysis `api.apiurl` comes from an externally retrieved catalog. JavaScript ...[truncated 2487 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every catalog field, including `apiurl`, as untrusted input. 2. Permit only relative API paths beginning with an approved prefix such as `/api/`. 3. Reject endpoints containing a scheme, hostname, credentials, backslashes, protocol-relative syntax, or path traversal. 4. After URL resolution, enforce: - `url.protocol === 'https:'` - `url.hostname` belongs to an explicit ApiHz hostname allowlist - the port is approved - the normalized path is within an approved API namespace 5. Do not attach credentials in a generic request function until the destination origin has been validated. 6. Fetch catalog data only through HTTPS and consider signing or pinning catalog content. 7. Block loopback, private, link-local, and metadata-service address ranges if arbitrary destination support is intentionally retained. 8. Display the final validated destination to the user before invoking dynamically cataloged endpoints. 9. Add tests proving that absolute URLs, protocol-relative URLs, HTTP URLs, and internal IP addresses are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
.gitignore:1
Finding
Credential Files Are Not Ignored and Are Written Without Explicit Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `.gitignore:1-8` **Additional Location**: `src/auth.js:326-350` **Vulnerability Type**: Insecure local credential storage and ineffective version-control exclusion **Risk Level**: Medium ### Vulnerable Code The `.gitignore` file is a credential-style configuration template rather than a list of paths to ignore: ```text # ApiHz API authentication information # Official site: https://www.apihz.cn # This file contains sensitive information; never commit it to Git APIHZ_ID= APIHZ_KEY= APIHZ_BASE_URL=https://cn.apihz.cn APIHZ_LIST_URL=http://101.35.2.25/api/xitong/apilist.php ``` These entries do not exclude `.credentials/apihz.txt`, `.credentials/`, or other generated secret files. The credential file is then written without an explicit file mode: ```javascript const content = `# ApiHz API authentication information # Official site: https://www.apihz.cn # Registration: ${this.registerUrl} # # Security notes: # - KEY and DMSG are stored using AES-256-GCM # - The encryption key is based on a machine fingerprint # - Do not copy this file to another machine APIHZ_ID=${id} APIHZ_KEY_ENC=${encryptedKey} APIHZ_DMSG_ENC=${encryptedDmsg} APIHZ_BASE_URL=https://cn.apihz.cn APIHZ_LIST_URL=http://101.35.2.25/api/xitong/apilist.php `; fs.writeFileSync(this.credentialsFile, content, 'utf8'); ``` The encryption key is deterministically derived from readily discoverable host properties: ```javascript getMachineFingerprint() { const hostname = require('os').hostname(); const username = require('os').userInfo().username; const workspace = this.workspace; const fingerprint = `${hostname}:${username}:${workspace}`; const hash = crypto.createHash('sha256').update(fingerprint).digest('hex'); return hash; } ``` ### Technical Analysis Git does not interpret environment-variable assignments as ignore patterns. Therefore, the generated `.credentials/apihz.txt` file remains eligible for accidental staging and ...[truncated 2072 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the current `.gitignore` content with actual ignore patterns, including: ```gitignore .credentials/ **/.credentials/ apihz.txt *.env ``` 2. Move sample configuration assignments into a separate file such as `.env.example`, using only fake placeholder values. 3. Create the credential directory with mode `0700`. 4. Write the credential file with mode `0600`, for example: ```javascript fs.writeFileSync(this.credentialsFile, content, { encoding: 'utf8', mode: 0o600, flag: 'w' }); fs.chmodSync(this.credentialsFile, 0o600); ``` 5. Prefer an operating-system keychain, credential vault, or user-supplied encryption secret over a deterministic host fingerprint. 6. Remove legacy plaintext credential support after a controlled migration. 7. Add a pre-commit check that rejects `.credentials` files and recognized ApiHz secret patterns. 8. Document how users can verify that secret files are not tracked with `git status` and `git check-ignore`. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (34)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documentation frames the skill as API consumption, but it also performs initialization, workspace configuration management, and credential setup. Hidden or under-disclosed setup behavior is risky because it expands the trust boundary from simple API calls to local state management and secret lifecycle operations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documentation frames the skill as API consumption, but it also performs initialization, workspace configuration management, and credential setup. Hidden or under-disclosed setup behavior is risky because it expands the trust boundary from simple API calls to local state management and secret lifecycle operations.

Missing User Warnings

High
Confidence
99% confidence
Finding
The report includes what appears to be a full API key value directly in markdown and later repeats a visible prefix of the key. Even if the author claims it is "encrypted storage," publishing credential material in a report can enable unauthorized API use, account abuse, and downstream compromise if the secret is still valid or reused elsewhere.

Missing User Warnings

High
Confidence
98% confidence
Finding
The backup servers use plain HTTP, and the script sends `id` and `key` in URL query parameters to those endpoints. That exposes credentials to interception by network attackers and to leakage via logs, proxies, or monitoring systems; because the same credentials are used for account actions, compromise can enable unauthorized access and account abuse.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The code comments claim backup nodes do not transmit sensitive credentials, but multiple call paths build URLs containing id and key and then send them to HTTP backup servers during failover. This exposes API credentials to interception or modification by any network attacker on the path and is made worse by the misleading security documentation, which may cause maintainers to underestimate the risk.

Missing User Warnings

High
Confidence
98% confidence
Finding
At this call site, the code fetches API data using a backup HTTP endpoint while embedding id and key in the query string. Cleartext transmission of credentials allows passive capture and active tampering, and because this skill is specifically an API client handling authentication material, the context makes the exposure more serious.

Missing User Warnings

High
Confidence
98% confidence
Finding
The phoneLookup and idcardLookup methods send highly sensitive personal data to a third-party API without any visible warning, consent flow, or safeguards in this code. Because phone numbers and national ID values are regulated identifiers in many jurisdictions, unauthorized transmission can cause serious privacy, compliance, and abuse risks.

Lp3

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding
The skill documents use of environment variables and credential handling but does not declare any tool scope or permissions boundaries. In an agent ecosystem, this omission weakens reviewability and can allow the skill to access local environment-derived secrets or configuration without explicit user awareness.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The natural-language content throughout the file forces a specific language experience for users, and the document does not provide an opt-in, alternative language, or a justification that this skill is intended only for a Chinese-speaking or region-specific context. This matches the policy category for language or locale constraints without user choice.

Session Persistence

Medium
Category
Rogue Agent
Content
**自动签到脚本:**
```bash
# 添加到 crontab (每天 00:02 执行)
crontab -e

# 添加以下行 (替换路径)
2 0 * * * OPENCLAW_WORKSPACE=/your/path node /your/path/skills/apihz/scripts/auto-checkin.js
Confidence
85% confidence
Finding
The skill encourages persistence via cron for daily automated check-in. Persistence is not inherently malicious here, but scheduled execution increases risk because secrets are reused automatically over time, network actions continue without active user review, and compromise of the script or workspace would yield recurring unauthorized activity.

External Transmission

Medium
Category
Data Exfiltration
Content
| 集群 IP | `http://101.35.2.25/api/...` | 速度快,CC 防火墙严格 | ⭐⭐ 备用 |
| VIP 线路 | `https://vip.apihz.cn/api/...` | 超高稳定,CC 防火墙宽松 | ⭐⭐⭐⭐ 企业 |

**获取最优 IP:** 访问 `https://api.apihz.cn/getapi.php` 获取当前最优 IP 地址

**示例 (天气 API):**
```bash
Confidence
84% confidence
Finding
The skill explicitly supports fetching service endpoints and using a cluster IP over plain HTTP. Even if the documentation says credentials are not sent when querying the list, relying on externally supplied endpoints and advertising non-TLS transport increases the risk of tampering, traffic interception, or redirection to malicious infrastructure.

Intent-Code Divergence

Medium
Confidence
79% confidence
Finding
The skill claims credentials are stored with AES-256-GCM encryption, yet the same file documents a plaintext-style credential file layout containing ID, KEY, and DMSG values. Security documentation that overstates protection can lead users to store sensitive secrets under false assumptions, increasing exposure if the workspace is read by other tools or users.

External Transmission

Medium
Category
Data Exfiltration
Content
- 🟢 **域名接口** (默认): `https://cn.apihz.cn` - 自动分发,CC 防火墙适中
   - 🟢 **集群 IP**: `http://101.35.2.25` 等 - 速度快,CC 防火墙严格,定期更新
   - 🟡 **VIP 线路**: `https://vip.apihz.cn` - 超高稳定,CC 防火墙宽松 (需购买)
   - 📖 **获取最优 IP**: 访问 `https://api.apihz.cn/getapi.php` 获取当前最优 IP

4. **网络传输:** 
   - ✅ 主 API 使用 HTTPS 加密 (`https://cn.apihz.cn`)
Confidence
84% confidence
Finding
This section again documents retrieval of the 'best IP' from an external service and use of plain HTTP cluster IPs. Dynamic endpoint selection without authenticated integrity checks can expose users to man-in-the-middle manipulation or unsafe failover behavior, especially because the skill handles account credentials and automated requests.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The report recommends using an HTTP fallback node for synchronization without clearly warning that HTTP lacks transport encryption and strong integrity protection. In an API aggregation skill handling credentials and fetched API metadata, this could expose requests to interception, tampering, replay, or malicious content injection by a network attacker.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
Comments and console messages throughout the file are presented only in Chinese, including the primary user-visible execution output. This creates a language/locale restriction without indicating that the skill is region-specific or offering an alternative language selection.

Description-Behavior Mismatch

Medium
Confidence
84% confidence
Finding
Manifest 将技能描述为提供“天气/地震/IP 归属地/临时邮箱/翻译等”企业级 API 接口集合,但该脚本实际执行的是读取本地认证文件、解密凭据、自动签到以及查询账户会员/调用量信息。这不是对外提供 API 接口的显然实现细节,而是一个账户自动化维护能力,和清单描述的能力范围存在语义偏移。

Context-Inappropriate Capability

Medium
Confidence
76% confidence
Finding
代码读取主机名、当前用户名和工作目录生成机器指纹,并将其作为解密密钥的一部分,用于访问认证信息。对于一个宣称提供 API 接口的技能而言,本机身份采集与设备绑定并非其公开目的所直接要求的能力,属于与 stated purpose 不完全相称的附加能力。

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The file's user-facing description and prompts are written exclusively in Chinese, presenting the skill as an interactive assistant without any indication that language selection is optional. Under the policy, forcing a specific language without user opt-in is a natural-language policy concern unless the locale restriction is explicitly justified.

External Transmission

Medium
Category
Data Exfiltration
Content
'http://81.68.149.132'    // 接口地址 3 - 备用
    ];
    // 最优 IP 获取接口 (定期调用更新备用服务器列表)
    this.bestIpUrl = options.bestIpUrl || 'https://api.apihz.cn/getapi.php';
    
    // 官方注册链接
    this.registerUrl = 'https://www.apihz.cn/?shareid=你的 ID';
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
`saveConfig` persists API ID and encrypted secret material to `.credentials/apihz.txt`, which is a safety-relevant file write involving credentials. The file contains comments and success logs, but there is no confirmation prompt before overwriting or creating the credentials file, and the initialization flow presents this as automatic setup behavior.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
注释写明“这里调用 cron 工具创建任务”,且 initialize() 会向用户报告“自动签到任务已创建”,但 createCheckInTask() 只是直接返回成功对象,没有进行任何调度、持久化或外部调用。该文档与用户提示共同造成了代码意图和实际行为的明显背离。

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code constructs a GET URL for dcan that includes id and key in the query string, and the default apiUrl uses plain HTTP. This can expose credentials through browser/proxy/server logs, monitoring systems, referer leakage, and network interception, especially because the request is sent to a configurable external endpoint rather than the main HTTPS base URL.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The generic request helper automatically sends API credentials together with all user-supplied query parameters to an external service, but this file provides no disclosure, consent, or data-minimization controls. That creates a broad data-exfiltration surface because any input passed to the client is transmitted off-system to a third party.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The client exposes ping, port scanning, WHOIS, ICP, and SSL probing methods that enable network reconnaissance against arbitrary targets. While these may be legitimate API features, they expand the skill from general information retrieval into infrastructure-enumeration capability, which can be abused for target discovery and reconnaissance in environments where users may not expect offensive-adjacent functionality.

Static analysis

No suspicious patterns detected.