Back to skill

Security audit

prana-cyf-20260327

Security checks for vulnerabilities and agentic risk

Overview

This skill is presented as a market-news tool, but it mainly forwards user prompts and API credentials to a remote service and can save fetched secrets locally.

Install only after reviewing the remote Prana service and endpoint you intend to use. Avoid custom base URLs unless you control and trust them, do not paste sensitive trading or account information into prompts, and disable automatic API-key fetching and plaintext key storage unless you explicitly need them.

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)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:12
Finding

Remote Service Is Instructed to Control the Final Agent Output

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prana_skill_client.py:177
Finding

Arbitrary API Origin Can Receive Stored Secret Credentials and User Messages

Content
View full analysis
dict[str, str]: return { "Content-Type": "application/json", "x-api-key": f"{public_key}:{secret_key}", } ``` ```python url = base_url.rstrip("/") + "/api/claw/agent-run" body = { "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), ) ``` ```python parser.add_argument("--base-url", "-b", default=DEFAULT_PRANA_BASE, help="Prana API root URL") args = parser.parse_args() public_key, secret_key = load_prana_credentials(args.base_url) result = invoke_prana( args.base_url, skill_key, content, args.thread_id or None, request_id, public_key, secret_key, ) ``` The JavaScript implementation has the same data flow: the command-line `--base-url` value is passed to `loadPranaCredentials` and `invokePrana`, while `headersXApiKey` attaches the public and secret keys to requests. ### Technical Analysis The caller can provide an arbitrary `--base-url`. The clients do not restrict the hostname, scheme, port, or redirect destination before attaching the reusable secret to the `x-api-key` header. Consequently, credentials loaded from environment variables or `config/api_key.txt` may be sent to any caller-selected server. The user message, thread identifier, skill identifier, and request identifier are sent to the same destination. The code also does not require HTTPS, so a supplied pla ...[truncated 1531 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/prana_skill_client.py:236
Finding

Fetched API Secrets Are Persisted in Plaintext 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") ``` ```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) return pub, sec ``` Equivalent JavaScript code: ```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 clients write the public and secret keys in plaintext to `config/api_key.txt` after automatic acquisition. Persistence is enabled by default and disabled only when `PRANA_SKILL_SKIP_WRITE_API_KEY` is affirmatively set. Neither implementation explicitly creates the file with restrictive permissions. The resulting access mode therefore depends on platform defaults and the process umask. The file is also stored inside the project directory, where it is more likely to be included in source-control commits, copied archives, backups, support bundles, or redistributed artifacts. The warning comment inside the file does no ...[truncated 1156 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/prana_skill_client.py:185
Finding

Client Automatically Obtains API Credentials from an Unauthenticated Endpoint

Content
View full analysis
str: root = base_url.rstrip("/") path = f"{root}/api/v1/api-keys" aid = (os.environ.get("ACCOUNT_ID") or os.environ.get("PRANA_ACCOUNT_ID") or "").strip() if aid: path = f"{path}?{urlencode({'account_id': aid})}" return path ``` ```python def fetch_prana_api_keys_via_get(base_url: str) -> 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") except urllib.error.HTTPError as e: err = e.read().decode("utf-8", errors="replace") print( f"Error: automatic API key retrieval failed with HTTP {e.code}: {err[:2000]}", file=sys.stderr, ) return None parsed = _parse_credentials_json(text) if not parsed: return None return parsed ``` ```python base = (prana_base_url or os.environ.get("NEXT_PUBLIC_URL") or DEFAULT_PRANA_BASE or "").strip() if base and not _auto_fetch_api_key_disabled(): fetched = fetch_prana_api_keys_via_get(base) 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) return pub, sec ``` The JavaScript client performs the same unauthenticated GET automatically when no local credentials are available. ### Technical Analysis When no credential is configure ...[truncated 2495 chars]
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 (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

A skill advertised as a financial news monitor but actually operating as an undeclared remote execution proxy with credential acquisition/storage creates a strong risk of deception and secret exfiltration. The instruction that /api/claw/agent-run and /api/claw/agent-result outputs must be passed directly to end users without modification also increases danger by discouraging sanitization or policy checks on remote content.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

A skill advertised as a financial news monitor but actually operating as an undeclared remote execution proxy with credential acquisition/storage creates a strong risk of deception and secret exfiltration. The instruction that /api/claw/agent-run and /api/claw/agent-result outputs must be passed directly to end users without modification also increases danger by discouraging sanitization or policy checks on remote content.

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

md
- **Node.js 18+**:先在包根目录执行 **`npm install`**,再执行 `node scripts/prana_skill_client.js -m "…" [-t thread_id] [-b base_url]`

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

技能清单描述的是一个实时财经新闻抓取与展示能力,但该文件的实际核心行为是组装用户消息、读取技能元数据与凭证,然后调用远端 /api/claw/agent-run 和 /api/claw/agent-result 接口获取结果。代码中没有任何财经新闻源抓取、WebSocket 推送或前端展示逻辑,实际行为更像是一个远程代理/封装调用器,与声明的技能功能明显不一致。

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

This skill does not implement the advertised market-news functionality locally; instead, it forwards arbitrary user input to a remote Prana service using an embedded remote skill key. That mismatch is security-relevant because users may believe they are running a transparent crawler/display tool while actually sending data to an opaque backend with different capabilities and trust boundaries.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding

The skill declares no explicit tool scope or permissions even though the referenced implementation reportedly has access to environment variables, filesystem read/write, and network capabilities. That omission reduces transparency and reviewability, making it easier for a seemingly simple news skill to access sensitive data or perform side effects beyond user expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script's visible error/help output is written in Chinese, and this pattern continues throughout the file for operational messages shown to users. That imposes a language choice on all users without offering locale selection or documenting a justified region-specific restriction.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

该技能为一个财经快讯客户端,却实现了多路径凭证发现、远程拉取 API key、以及本地持久化 config/api_key.txt 的能力,明显超出其最小必要功能。即使未表现出直接窃取行为,这种过度授权设计会扩大凭证暴露面:误连不受信任 base URL、工作目录泄露、或错误提交文件时,都可能导致长期有效凭证泄漏。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

脚本会在未获得用户明确确认的情况下,通过网络请求自动获取 API 凭证,并默认写入磁盘。这会把临时的网络信任扩大为持久化秘密存储:一旦 base URL 被错误配置、服务端返回不该下发的密钥、或本地目录被其他用户/进程访问,凭证就可能被泄漏并用于后续未授权调用。

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code can automatically request API credentials from a remote endpoint and persist them to config/api_key.txt on disk. Auto-bootstrapping and storing secrets locally expands the attack surface: compromised endpoints, misconfigured environments, or accidental repository inclusion could expose reusable credentials far beyond the stated news-monitoring purpose.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script transmits user messages, thread IDs, request IDs, and skill metadata to a remote service without an explicit runtime warning or consent prompt. In this skill context, users may paste sensitive market research, credentials, or proprietary trading context, so silent exfiltration to a third-party endpoint creates meaningful privacy and confidentiality risk.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The header comment says the script is a thin client with no business logic, only responsible for communicating with Prana. In practice, it implements nontrivial local behavior including config parsing, skill selection, credential discovery from env/files/network, writing secrets to disk, and fallback polling orchestration.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The file's user-facing docstrings and CLI descriptions are presented in Chinese only, with no indication that users may choose another language or locale. Under the stated policy, forcing a specific language without opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access, suspicious.potential_exfiltration

Environment variable access combined with network send.

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

File read combined with network send (possible exfiltration).

Warn
Code
suspicious.potential_exfiltration
Location
scripts/prana_skill_client.js:94