Back to skill

Security audit

SkillForge API 服务发现

Security checks for vulnerabilities and agentic risk

Overview

This skill has a clear paid-service discovery purpose, but it can send user data and API credentials to configurable external endpoints and invoke paid services without enforcing the documented confirmation gate.

Review this before installing if you plan to connect a real SkillForge account. Use only a trusted HTTPS platform URL, a narrowly scoped/revocable API key, and external spending limits. Do not allow automatic invocation of paid services unless the host enforces explicit per-call confirmation showing the service, destination, data being sent, and authoritative price.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
handler.js:145
Finding
API Credentials and User Data Can Be Sent to an Unrestricted or Insecure Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `handler.js:145-163`, `handler.js:186-200`, `package.json:45-52`, `skill.yaml:25` **Vulnerability Type**: Unrestricted sensitive-data transmission **Risk Level**: High ### Vulnerable Code ```javascript async function discoverServices(capability, limit = 3) { if (!config.platform_url || !config.api_key) { throw new Error('[SkillForge] 未配置 platform_url 或 api_key'); } const category = CAPABILITY_CATEGORY_MAP[capability]; const url = new URL(`${config.platform_url}/v1/discover`); url.searchParams.set('capability', capability); if (category) { url.searchParams.set('category', category); } url.searchParams.set('limit', limit.toString()); try { const response = await fetch(url.toString(), { method: 'GET', headers: { 'Authorization': `Bearer ${config.api_key}`, 'Content-Type': 'application/json' } }); ``` ```javascript async function invokeService(serviceId, input, options = {}) { if (!config.platform_url || !config.api_key) { throw new Error('[SkillForge] 未配置 platform_url 或 api_key'); } const url = `${config.platform_url}/v1/services/${serviceId}/invoke`; try { const response = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${config.api_key}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ input, options }) }); ``` The manifest also defines a plaintext default under a different configuration key: ```yaml config: apiUrl: ${SKILLFORGE_API_URL:-http://localhost:3000} timeout: 30000 ``` The runtime schema only requires a generic URI: ```json "platform_url": { "type": "string", "description": "SkillForge 平台地址", "format": "uri" } ``` ### Technical Analysis External network access and transmission of invocation input are necessary for the declared API-discovery functionality. However, the implementation does not constrain that access to ...[truncated 2069 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https:` for all non-development endpoints and reject plaintext HTTP before initialization succeeds. 2. Maintain an explicit allowlist of approved SkillForge origins rather than accepting an arbitrary URI. 3. Normalize the configured URL and reject embedded credentials, unexpected ports, fragments, and ambiguous hostnames. 4. Disable automatic redirects or validate every redirect target before forwarding authorization headers. 5. Bind the API key to the expected platform audience and use a narrowly scoped, revocable token. 6. Add explicit user disclosure before transmitting prompts, files, or other potentially sensitive input. 7. Apply field-level filtering so internal metadata and secrets are not included in invocation payloads. 8. Standardize the configuration key across `skill.yaml`, `package.json`, documentation, and `handler.js`. 9. Add tests proving that HTTP URLs, non-allowlisted hosts, and cross-origin redirects are rejected. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
handler.js:371
Finding
Paid Service Invocation Does Not Enforce Documented User Confirmation<![CDATA[ ## Vulnerability Details **File Location**: `handler.js:371-405`, `README.md:9`, `SKILL.md:16` **Vulnerability Type**: Missing authorization and consent enforcement for billable operations **Risk Level**: High ### Vulnerable Code The documentation promises confirmation before execution: ```markdown - 💰 **费用透明** - 调用前展示价格,用户确认后执行 ``` ```markdown 3. **调用服务** - 用户确认后调用服务并返回结果 ``` The handler does not verify such confirmation: ```javascript if (action === 'invoke') { if (!serviceId) { return { success: false, action: 'invoke', error: '缺少服务 ID (serviceId)' }; } try { // 检查费用限制 if (config.max_cost_per_call && input?._estimatedCost > config.max_cost_per_call) { return { success: false, action: 'invoke', error: `预估费用 $${input._estimatedCost} 超过限制 $${config.max_cost_per_call}` }; } const result = await invokeService(serviceId, input); return { success: result.success, action: 'invoke', serviceId, data: result.data, billing: result.billing, meta: result.meta, formatted: formatInvocationResult(result), error: result.error }; } catch (error) { return { success: false, action: 'invoke', serviceId, error: error.message }; } } ``` ### Technical Analysis The only authorization conditions for invocation are the presence of `serviceId` and the optional client-supplied cost comparison. There is no confirmation flag, trusted approval record, confirmation token, session binding, or verification that the user accepted a displayed service and price. The configuration includes `auto_confirm_free`, but the invocation path does not use it. Therefore, the handler does not distinguish between free and paid services when determining whether approval is required. Although a hosting Agent could implement confirmation outside this module, the Skill itself claims that it performs invocation only af ...[truncated 1171 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require a short-lived confirmation token before every paid invocation. 2. Bind the token to the user or session, service ID, exact input hash, displayed price, currency, and expiration time. 3. Obtain confirmation only after showing the authoritative price and external data-sharing notice. 4. Consume confirmation tokens once to prevent replay. 5. Enforce `auto_confirm_free` only after the platform cryptographically or authoritatively confirms that the service is free. 6. Reject direct `invoke` actions that do not carry a valid approval record. 7. Keep an auditable record of the displayed terms and the user's confirmation. 8. Add tests demonstrating that paid calls fail without approval and that approval cannot be reused for another service, input, or price. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
handler.js:385
Finding
Maximum Cost Limit Can Be Bypassed Through Caller-Controlled Price Data<![CDATA[ ## Vulnerability Details **File Location**: `handler.js:385-394` **Vulnerability Type**: Client-controlled billing-policy enforcement **Risk Level**: Medium ### Vulnerable Code ```javascript // 检查费用限制 if (config.max_cost_per_call && input?._estimatedCost > config.max_cost_per_call) { return { success: false, action: 'invoke', error: `预估费用 $${input._estimatedCost} 超过限制 $${config.max_cost_per_call}` }; } const result = await invokeService(serviceId, input); ``` ### Technical Analysis The configured `max_cost_per_call` is enforced by comparing it with `input._estimatedCost`. The entire `input` object is supplied by the caller, and `_estimatedCost` is optional. A caller can bypass the comparison by omitting `_estimatedCost`, setting it to zero, supplying a value lower than the real service price, or providing a value whose type does not produce the intended numeric comparison. The handler does not retrieve authoritative pricing for the selected service immediately before invocation and does not bind the price to a discovery result. As a result, `max_cost_per_call` appears to be a security control but does not reliably restrict actual charges. ### Attack Path 1. The victim configures `max_cost_per_call` to limit spending. 2. A caller selects a service whose real price exceeds that limit. 3. The caller omits `input._estimatedCost` or supplies a false low value. 4. The comparison evaluates to false. 5. The handler invokes the service. 6. The platform charges the authoritative service price, potentially exceeding the configured maximum. ### Impact Assessment An attacker or faulty integration can bypass the local per-call spending policy and cause charges greater than the configured limit. The total impact is bounded by the platform account balance and any platform-side spending controls. The issue does not grant operating-system access or code execution, but it undermines a declared financial safety boundary. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never trust `_estimatedCost` supplied inside invocation input. 2. Retrieve authoritative service pricing from the platform immediately before invocation. 3. Bind the service ID and price to a signed discovery quote with a short expiration. 4. Enforce the maximum charge on the platform side as part of the same transaction that performs billing. 5. Send `maxAllowedCharge` as a server-enforced constraint, and require the platform to reject charges above it. 6. Validate all monetary values as finite, non-negative numbers in a fixed currency and use integer minor units or decimal arithmetic. 7. Reconfirm with the user if the current price differs from the previously displayed price. 8. Add tests for missing, negative, string, `NaN`, zero, and falsified estimated-cost values. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
handler.js:257
Finding
Untrusted Marketplace Metadata Is Embedded in Agent-Facing Output<![CDATA[ ## Vulnerability Details **File Location**: `handler.js:257-282`, `handler.js:349-357` **Vulnerability Type**: Indirect prompt injection through remote service metadata **Risk Level**: Medium ### Vulnerable Code ```javascript function formatServiceList(services) { if (!services || services.length === 0) { return '未找到匹配的服务'; } const lines = ['发现以下可用服务:\n']; services.forEach((service, index) => { const priceStr = service.priceUnit === 'free' ? '免费' : `$${service.price.toFixed(4)}/次`; const ratingStr = service.rating ? ` ⭐${service.rating.toFixed(1)}` : ''; const callsStr = service.calls ? ` (${(service.calls / 1000).toFixed(1)}k次调用)` : ''; lines.push(`${index + 1}. **${service.name}** - ${priceStr}${ratingStr}${callsStr}`); lines.push(` ${service.description}`); lines.push(` 开发者: ${service.developer || '匿名'}\n`); }); return lines.join('\n'); } ``` The formatted text is returned directly to the Agent: ```javascript const result = await discoverServices(targetCapability, config.discover_limit); return { success: true, action: 'discover', capability: targetCapability, services: result.data || [], formatted: formatServiceList(result.data), suggestion: '请让用户选择服务后调用 invoke 动作' }; ``` ### Technical Analysis Service names, descriptions, and developer names originate from a remote marketplace response. The Skill inserts these values directly into Markdown-like text intended for presentation in the Agent session. No escaping, length restriction, control-character removal, URL policy, trust labeling, or separation between remote data and operational instructions is applied. A malicious service provider can therefore place instruction-like text in marketplace metadata. If the host Agent processes the returned `formatted` field as conversational context rather than inert display data, the metadata may alter its subsequent behavior. The direct invocation capabili ...[truncated 1291 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all marketplace metadata as untrusted data. 2. Return service records as typed structured fields instead of combining them into instruction-like conversational text. 3. Clearly label remote fields as untrusted and instruct the host not to follow commands contained in them. 4. Escape Markdown and HTML metacharacters, remove control characters, and enforce strict length limits. 5. Restrict or neutralize URLs in service descriptions and developer metadata. 6. Display only platform-validated fields needed for service selection. 7. Require explicit user selection and confirmation independently of all remote metadata. 8. Ensure the host Agent cannot interpret service descriptions as privileged Skill instructions. 9. Add adversarial tests containing instruction injection, deceptive Markdown, oversized fields, and malicious links. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是一个面向运行时的服务发现与调用技能;但给出的代码块只是 test.js 测试文件,其主要作用是验证 detectCapabilityGap、formatServiceList、formatInvocationResult 和关键词映射等函数是否按预期工作。代码中没有网络请求、没有服务查询、没有付费服务调用、没有自动推荐逻辑的实际执行,仅有测试数据构造、字符串检查、日志输出和 process.exit。因此该代码块的实际用途与声明的主要目的存在明显不一致。

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
The skill documentation and all user interaction examples are presented in Chinese, including example prompts and agent responses, with no indication that other languages are supported or that the language choice is optional. This can amount to a language/locale policy issue because the skill appears to assume a fixed language for invocation and usage without user opt-in.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares behavior involving external API discovery and invocation, but does not specify any tool scope such as permissions or allowed-tools. For a network-capable skill that can trigger paid remote calls, missing explicit scope weakens policy enforcement and makes unintended or overly broad network access more likely.

Natural-Language Policy Violations

Medium
Confidence
85% confidence
Finding
The natural-language content of the skill is entirely presented in Chinese, and there is no indication that users may select another language or that the skill is intentionally limited to a Chinese-speaking context. Under the stated policy, forcing a specific language without opt-in is a locale-policy concern.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases are broad and generic, covering common language such as finding APIs or missing capabilities. In an agent environment, this can cause the skill to activate in situations the user did not explicitly intend, increasing the chance of unsolicited external discovery or progression toward paid service usage.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The auto-activation criteria are vague, such as when the agent believes a task needs an external API or a capability is missing. Because this skill is designed to discover and potentially call paid third-party services, ambiguous activation can lead to unreviewed data being sent off-platform, accidental charges, or untrusted service selection without meaningful user consent.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
Comments and returned messages such as initialization errors, discovery results, and invocation output are written exclusively in Chinese. This can violate language/locale policy when the skill forces a specific language without offering user selection or documenting a justified locale constraint.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
auto_discover: true,
  discover_limit: 3,
  max_cost_per_call: 1.00,
  auto_confirm_free: false
};

/**
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill sends arbitrary user-provided input to an external paid service endpoint, but this file does not enforce any user consent, destination disclosure, or data-sensitivity checks before transmission. In an agent setting, this can leak sensitive prompts, files, or derived secrets to third-party providers and may also trigger unintended charges.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The manifest explicitly advertises 'automatic discovery and invocation' of external AI services, but it does not define clear trigger constraints, approval gates, or scope limits in the package metadata. In a skill that can invoke paid third-party services, vague autonomy increases the risk of unexpected outbound requests, unintended data sharing, and surprise billing when the host agent decides a local capability is missing.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The manifest description states the skill will 'automatically discover and call paid AI services,' yet there is no explicit warning in the package metadata about billing, external data transfer, or user consent requirements. Because this skill's purpose is service discovery and invocation of paid services, omission of a strong warning materially increases the chance of unauthorized spend or unreviewed transmission of prompts/data to third parties.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
"default": 1.0,
          "minimum": 0
        },
        "auto_confirm_free": {
          "type": "boolean",
          "description": "是否自动确认免费服务",
          "default": false
Confidence
85% confidence
Finding
The presence of an 'auto_confirm_free' option enables autonomous approval behavior for at least a subset of external service invocations. Even though the default is false, this kind of autonomous decision-making is risky in a service-discovery skill because 'free' services can still exfiltrate sensitive data, perform unreviewed actions, or establish unsafe trust in third-party providers without human review.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The description states that the skill will automatically discover and call paid AI services, but it does not prominently warn users that external services may be contacted, that costs may be incurred, or that data may leave the local environment. This weakens informed consent and makes accidental disclosure of prompts or billable usage more likely when the skill is triggered.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger keywords are very broad and include generic terms like '服务', 'API', '工具', and '调用', which can cause the skill to activate in many unrelated conversations. Because this skill is designed to discover and call external paid services, over-triggering increases the chance of unintended network access, cost-incurring actions, or recommendations being made without sufficiently clear user intent.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The automatic 'capability_missing' trigger allows the skill to activate whenever the agent detects a capability gap, but the metadata does not define clear boundaries, approval requirements, or safe fallback behavior. In the context of a discovery skill that may contact external paid services, this ambiguity can lead to silent escalation from a local failure into unsolicited third-party service discovery or invocation.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This JavaScript file contains user-facing console output and descriptive strings entirely in Chinese, including the skill title and test result messages. Under the policy rule, forcing a specific language without opt-in is a locale-policy issue unless the constraint is explicitly justified, which is not present here.

Missing User Warnings

Low
Confidence
88% confidence
Finding
Service discovery sends task/capability-derived information plus a bearer API credential to an external platform without any user-facing notice or consent flow in this code. Even if discovery sends less data than invocation, task descriptions can still reveal sensitive intent, and the automatic outbound call expands the privacy and trust boundary.

Natural-Language Policy Violations

Low
Confidence
71% confidence
Finding
The primary user-facing description is in Chinese only, with no indication of language choice or localization support. This can violate a language/locale policy when a skill forces a specific language without user opt-in or documented regional justification.

Unverifiable Dependency: openclaw has 16 known advisory(ies) (CVE-2026-53846 (OpenClaw: Workspace .env npm_execpath could influence bundled runtime dependency); CVE-2026-32064 (OpenClaw's andbox browser noVNC observer lacked VNC authentication); CVE-2026-32006 (OpenClaw has a BlueBubbles group allowlist mismatch via DM pairing-store fallbac) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The skill name and description are written only in Chinese, with no indication that language is configurable or intentionally limited to a Chinese-speaking context. This can constitute a locale/language policy issue because the skill appears to impose one language without user opt-in or documented justification.

Static analysis

No suspicious patterns detected.