Back to skill

Security audit

exchange_rate_assistant

Security checks for vulnerabilities and agentic risk

Overview

This exchange-rate skill needs review because it acts as a broad remote service client that fetches, stores, and forwards API secrets beyond what a simple rate lookup clearly requires.

Install only after the publisher narrows and clearly documents the remote execution boundary, removes or separates purchase-history access, disables plaintext secret persistence by default, restricts the API base URL to trusted HTTPS origins, and pins dependencies. Treat any stored PRANA_SKILL keys as sensitive credentials.

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 DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:76
Finding

Remote Service Is Given Unrestricted Control Over User-Facing Output

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prana_skill_client.py:491
Finding

API Credentials Can Be Sent to an Arbitrary User-Selected Host

Content
View full analysis
dict[str, str]: return { "Content-Type": "application/json", "x-api-key": f"{public_key}:{secret_key}", } ``` The header is attached to requests derived from the caller-controlled base URL: ```python url = base_url.rstrip("/") + "/api/claw/agent-run" body: dict = { "skill_key": skill_key, "question": content, "thread_id": thread_id or "", "request_id": request_id, } data = json.dumps(body, ensure_ascii=False).encode("utf-8") req = urllib.request.Request( url, data=data, method="POST", headers=_headers_x_api_key(public_key, secret_key), ) ``` The CLI exposes the base URL directly: ```python parser.add_argument( "--base-url", "-b", default=DEFAULT_PRANA_BASE, help="Prana API root URL", ) ``` The JavaScript client has the same behavior: ```javascript function headersXApiKey(publicKey, secretKey) { return { 'Content-Type': 'application/json', 'x-api-key': `${publicKey}:${secretKey}`, }; } ``` ```javascript const url = `${baseUrl.replace(/\/+$/, '')}/api/claw/agent-result`; const res = await fetch(url, { method: 'POST', headers: headersXApiKey(publicKey, secretKey), body: JSON.stringify(body), signal: AbortSignal.timeout(HTTP_TIMEOUT_MS), }); ``` ### Technical Analysis Both clients accept an unrestricted `--base-url` value and use it as the destination for authenticated requests. There is no hostname allowlist, certificate pinning, scheme validation, or check that the selected origin is the expected Prana service. The `x-api-key` header conta ...[truncated 1483 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prana_skill_client.py:229
Finding

Fetched Secret Keys Are Persisted in a Plaintext Project File by Default

Content
View full analysis
None: CONFIG_DIR.mkdir(parents=True, exist_ok=True) lines = [ "# Auto-saved by prana_skill_client after GET /api/v1/api-keys; do not commit to public repos.", f"{public_key}:{secret_key}", "", ] API_KEY_FILE.write_text("\n".join(lines), encoding="utf-8") ``` Persistence is enabled unless the user explicitly opts out: ```python if fetched: pub, sec = fetched if not _skip_write_fetched_api_key(): try: _persist_fetched_api_key_txt(pub, sec) except OSError as e: print(f"Warning: unable to write config/api_key.txt: {e}", file=sys.stderr) ``` JavaScript implements equivalent persistence: ```javascript function persistFetchedApiKeyTxt(publicKey, secretKey) { fs.mkdirSync(CONFIG_DIR, { recursive: true }); const lines = [ '# Auto-saved by prana_skill_client after GET /api/v1/api-keys; do not commit to public repos.', `${publicKey}:${secretKey}`, '', ]; fs.writeFileSync(API_KEY_FILE, lines.join('\n'), 'utf8'); } ``` ### Technical Analysis The secret is stored as `public_key:secret_key` in `config/api_key.txt` inside the Skill package. Neither implementation explicitly creates the file with owner-only permissions. Security therefore depends on the process umask and filesystem defaults. Placing credentials under the project root increases the likelihood that they will be copied into source-control commits, shared archives, backups, bug reports, container images, or artifact uploads. A warning comment does not prevent automated collection or accidental disclosure. The safer b ...[truncated 811 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
SKILL.md:12
Finding

Unauthenticated Credential Provisioning Is Combined with Persistent Global Agent Configuration

Content
View full analysis
Optional[Tuple[str, str]]: url = _build_api_keys_fetch_url(base_url) req = urllib.request.Request(url, method="GET") try: with urllib.request.urlopen(req, timeout=API_KEYS_FETCH_TIMEOUT_SEC) as resp: text = resp.read().decode("utf-8") ``` The JavaScript client does the same: ```javascript async function fetchPranaApiKeysViaGet(baseUrl) { const url = buildApiKeysFetchUrl(baseUrl); try { const res = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(API_KEYS_FETCH_TIMEOUT_MS), }); const text = await res.text(); ``` ### Technical Analysis The endpoint is described and implemented as not requiring prior authentication. It returns both a public key and a secret key. The documentation then instructs users to place those credentials in global OpenClaw environment configuration. An exchange-rate lookup does not inherently require persistent, gateway-wide secrets or a gateway restart. Global configuration expands the credential's lifetime and makes it potentially available to unrelated Agent runs and subprocesses. Restarting the gateway also affects availability outside the scope of the individual Skill ...[truncated 996 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:59
Finding

Setup Instructions Install Unpinned Third-Party Dependencies

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This second mismatch finding is substantively the same issue: the skill markets itself as a currency assistant but operationally behaves as a generic client for remote Prana/Claw calls with credential handling and thread management. Such deception or poor disclosure materially increases the chance of unsafe approval and secret exposure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This second mismatch finding is substantively the same issue: the skill markets itself as a currency assistant but operationally behaves as a generic client for remote Prana/Claw calls with credential handling and thread management. Such deception or poor disclosure materially increases the chance of unsafe approval and secret exposure.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The document describes an exchange-rate assistant, but nearly the entire runbook is about retrieving platform API keys, invoking a generic agent endpoint, and polling results, with no exchange-rate-specific logic. In context, this makes the skill more dangerous because the business purpose provides cover for unrelated privileged operations.

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 77)May include surrounding context.

md
NODE_OPTIONS=--experimental-default-type=module node scripts/prana_skill_client.js -m "…" [-t thread_id] [-n]

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**Python 脚本 pip 依赖**(用于解析 `SKILL.md` 的 YAML frontmatter):

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill includes an unrelated capability to retrieve purchase-history links using combined public and secret keys. This is especially risky because billing/payment-history access is unrelated to exchange-rate queries and could expose sensitive account activity under the guise of a harmless finance helper.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code automatically retrieves platform API credentials from a remote endpoint and writes them to config/api_key.txt on disk. For a skill that should only perform exchange-rate lookups, acquiring and persisting reusable platform credentials is excessive and expands compromise impact if the filesystem, repo, logs, or package contents are exposed.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This skill advertised as an exchange-rate assistant is actually a generic remote wrapper that forwards user input and locally available credentials to a remote Prana/OpenClaw service. That mismatch is dangerous because it conceals the true trust boundary and enables arbitrary remote behavior under an innocuous financial-assistant label, increasing phishing, data exfiltration, and deceptive execution risk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script is not a local exchange-rate tool; it forwards arbitrary user messages to a remote agent-run endpoint using a server-selected skill key. That makes the package a generic remote capability launcher under a misleading finance-themed label, which can bypass user expectations, local review, and scope restrictions. In the skill context, this mismatch is more dangerous because users may trust it as a narrow utility while it can invoke broader remote behavior.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill requires access to environment variables, network endpoints, and persistent configuration, but it declares no explicit tool scope or permissions boundary. That makes the trust model opaque and increases the risk of over-privileged execution, especially because the document instructs fetching and storing API keys and restarting the gateway.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill tells operators to retrieve and configure secret/public API keys, persist them in the environment, and restart infrastructure without any security warning, consent prompt, or guidance on least-privilege handling. That omission raises the risk of accidental secret exposure, unsafe persistence, and misuse by users who believe they are only enabling exchange-rate lookup.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The document instructs external network calls that obtain and later transmit credentials, but it does not clearly warn users that secrets and request data will be sent to remote services. In this context, the lack of disclosure is more dangerous because the advertised function sounds like a simple informational tool rather than a credentialed remote-execution wrapper.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The instructions explicitly combine public and secret keys into an authentication header for a payment-history endpoint, which is disproportionate to the stated exchange-rate use case. Handling high-value credentials in an unrelated skill expands the blast radius if the skill is misused, logged, or executed in a broader-trust environment.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The top-level doc comment describes this file as a 'Prana encapsulation skill' thin client whose behavior matches another wrapper implementation. That documentation frames the file as generic transport/wrapper infrastructure, which conflicts with the skill manifest’s portrayal of a specialized exchange-rate assistant.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script's natural-language comments, help text, prompts, and error messages are presented in Chinese only, including usage guidance and operational notices. This imposes a specific language on users without any visible opt-in or alternative locale support, matching the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill persists fetched API credentials locally without an explicit opt-in or prominent warning to the user. Silent secret storage creates avoidable exposure through accidental commits, local compromise, or reuse by other processes, especially because the stored material appears to be platform access keys rather than task-specific exchange-rate tokens.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

顶部文档将脚本描述为纯通信薄客户端,但实际实现包含读取 SKILL.md frontmatter、解析环境变量与配置文件、自动获取 API key、写入 config/api_key.txt,以及设置进程环境变量等本地状态管理行为。虽然这些不一定是核心业务逻辑,但已超出“仅负责通信”的字面描述。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file contains natural-language descriptions and CLI help text that assume Chinese as the only interaction language. The policy explicitly flags language or locale constraints when the skill forces a specific language without user opt-in, and no alternative language option or justification is provided here.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The code can automatically fetch API credentials from a remote service and persist them to config/api_key.txt, then export them into process environment variables. Automatic credential acquisition and local storage materially expand the trust boundary and create credential exposure risk through disk leakage, repo inclusion, overly broad reuse, or downstream tool access. For a simple exchange-rate assistant, this behavior is disproportionate and increases suspicion.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The argparse description, help strings, and runtime error messages are all presented only in Chinese, which imposes a language choice on users. Because no opt-in, locale selection, or documented regional justification appears in the file, this matches the language-policy violation category.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

All user-facing instructions and naming are presented only in Chinese, and the document does not offer any user language or locale choice. Per SQP-3, forcing a specific language without opt-in can be a natural-language policy violation unless the locale constraint is explicitly justified.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/prana_skill_client.js:26

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/prana_skill_client.py:583