Back to skill

Security audit

TradingView技术指标分析助手

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed Prana remote-service wrapper, but it handles API credentials and identity data with overly broad endpoint control and plaintext credential persistence.

Install only if you are comfortable sending your prompts and related market-analysis content to Prana. Use a trusted HTTPS Prana endpoint, avoid overriding the base URL from untrusted environments, prefer scoped PRANA_* environment variables, and avoid storing secret keys in the skill directory unless file permissions and backup/source-control exposure are controlled.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prana_skill_client.py:218
Finding
Caller-Controlled API Endpoint Can Receive Credentials, Identity Data, and User Messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/prana_skill_client.py:218-264`, `scripts/prana_skill_client.py:455-480`, and `scripts/prana_skill_client.py:502` **Vulnerability Type**: Unrestricted transmission of sensitive information to a configurable network endpoint **Risk Level**: High ### Complete Code Snippet ```python def _build_api_keys_fetch_url(base_url: str) -> str: """ 组装 GET /api/v1/api-keys 完整 URL。 查询参数(与 Prana 服务端一致):account_id、email、phone_number;均可从环境变量注入。 若全无则服务端会为随机新用户签发 key(见 api_keys_api.get_api_keys)。 """ root = base_url.rstrip("/") path = f"{root}/api/v1/api-keys" q: Dict[str, str] = {} aid = (os.environ.get("ACCOUNT_ID") or os.environ.get("PRANA_ACCOUNT_ID") or "").strip() if aid: q["account_id"] = aid email = (os.environ.get("PRANA_API_KEYS_EMAIL") or os.environ.get("EMAIL") or "").strip() if email: q["email"] = email phone = ( os.environ.get("PHONE_NUMBER") or os.environ.get("PRANA_PHONE") or os.environ.get("phone_number") or "" ).strip() if phone: q["phone_number"] = phone if q: path = f"{path}?{urlencode(q)}" return path def fetch_prana_api_keys_via_get(base_url: str) -> Optional[Tuple[str, str]]: """ 调用 Prana GET /api/v1/api-keys(无需 JWT),解析 data.api_key 的 public_key、secret_key。 """ 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") ``` ```python def invoke_prana( base_url: str, skill_key: str, content: str, thread_id: str | None, request_id: str, public_key: str, secret_key: str, ) -> dict: """ 调用 Prana 技能执行接口。 body: skill_key, question, thread_id, request_id(不含 api_key) Header: x-api-key: public_key:secret_key 若 HTTP 超时、连接失败,或网关类错误(5xx / ...[truncated 4284 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce HTTPS for all non-mock network requests and reject URLs using `http://`, unsupported schemes, embedded credentials, or malformed hosts. 2. Pin the production endpoint to the expected Prana hostname or implement a narrow, explicit allowlist of trusted Prana hosts. 3. Disable arbitrary `--base-url` and `NEXT_PUBLIC_URL` overrides in production builds. If overrides are needed for development, require an explicit development mode and display the destination before sending secrets. 4. Remove fallback reads from generic variables such as `EMAIL`, `PHONE_NUMBER`, `phone_number`, and `ACCOUNT_ID`. 5. Only use explicitly scoped names such as `PRANA_ACCOUNT_ID`, `PRANA_API_KEYS_EMAIL`, and `PRANA_PHONE`. 6. Require explicit user consent before transmitting identity attributes during automatic credential creation. 7. Consider disabling automatic key acquisition by default. Require the user to opt in or configure credentials through a trusted provisioning workflow. 8. Validate redirects or disable cross-origin redirects so a trusted initial endpoint cannot redirect credential-bearing traffic to another host. 9. Separate credential acquisition and authenticated API destinations, validating both against the trusted-host policy. 10. Clearly notify users that their full message is sent to a remote service and advise them not to include unnecessary secrets. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/prana_skill_client.py:276
Finding
Automatically Fetched Secret Keys Are Stored in Plaintext Without Enforced Access Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/prana_skill_client.py:276-299` and `scripts/prana_skill_client.py:355-367` **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: Medium ### Complete Code Snippet ```python def _persist_fetched_api_key_txt(public_key: str, secret_key: str) -> None: """将 public_key:secret_key 写入 config/api_key.txt(首行为注释,与现有读取逻辑兼容)。""" 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") def _persist_fetched_api_key_json(public_key: str, secret_key: str) -> None: """PRANA_SKILL_PERSIST_FETCHED_KEY=1 时额外写入 config/api_key.json(完整 API 响应形状)。""" CONFIG_DIR.mkdir(parents=True, exist_ok=True) payload = { "code": 200, "message": "success", "data": {"api_key": {"public_key": public_key, "secret_key": secret_key}}, } API_KEY_JSON_FILE.write_text( json.dumps(payload, ensure_ascii=False, indent=2) + "\n", 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"警告: 无法写入 config/api_key.txt:{e}", file=sys.stderr) also_json = os.environ.get("PRANA_SKILL_PERSIST_FETCHED_KEY", "").strip().lower() if also_json in ("1", "true", "yes", "on"): try: _persist_fetched_api_key_json(pub, sec) except OSError as e: print(f"警告: 无法写入 config/api_key.json:{e}", file=sys.stderr) return pub, sec ``` ### Technical Analysis After automatically retrieving an API key, the client writes the public and secret components into `config/api_key.txt` by default. Optional JSON persistence stores the same secret in `conf ...[truncated 2319 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not persist automatically fetched credentials by default. Make storage an explicit opt-in operation. 2. Prefer an operating-system credential manager, secret service, protected keychain, or orchestrator-managed secret store. 3. If file storage is unavoidable, create the file atomically with owner-only mode `0600`, rather than relying on `Path.write_text()` and the process umask. 4. Verify that the destination is a regular file owned by the current user and reject symbolic links or unexpected hard links. 5. Set restrictive permissions on the `config` directory, such as `0700`, where platform semantics permit. 6. Refuse to use credential files that are group-readable or world-readable, and provide a clear remediation message. 7. Add both `config/api_key.txt` and `config/api_key.json` to a distributed `.gitignore`. 8. Store only one credential copy and avoid optional duplication into JSON. 9. Use atomic replacement with a securely created temporary file in the same protected directory. 10. Support credential rotation and revocation so exposed keys can be invalidated promptly. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (12)

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 declared purpose is a local technical-analysis toolkit, but the detected behavior indicates remote API brokering, credential persistence, automatic API key acquisition, and dynamic routing via frontmatter fields rather than implementing the described analytics. This mismatch is dangerous because it can mislead users and reviewers into granting trust to a skill that actually exfiltrates data, obtains secrets, or proxies requests to an external service.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
This package is presented as a local Python technical-analysis toolkit, but the code forwards user input to a remote Prana service for the actual capability. That creates a material supply-chain and data-exfiltration risk because users may disclose prompts, market data, strategies, or secrets to an undeclared third party while believing computation occurs locally.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README explicitly instructs users to persist a `public_key` and `secret_key` in local files, but provides only a narrow warning not to commit one filename to a public repo. It does not clearly emphasize that the secret key is a sensitive credential, recommend safer storage mechanisms, or warn about leakage through plaintext files, backups, logs, screenshots, or broader source-control mistakes; this can lead to credential exposure and unauthorized API use.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill advertises no explicit tool restrictions, yet the surrounding package behavior reportedly includes environment access, file read/write, and network capabilities. In an agent setting, missing scope declarations weaken least-privilege guarantees and can allow a seemingly simple analysis skill to perform sensitive side effects beyond user expectations.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The file presents the skill name, description, and usage instructions exclusively in Chinese. Under the policy rule for natural-language violations, forcing a specific language without user opt-in or a documented justification is reportable.

Intent-Code Divergence

Medium
Confidence
88% confidence
Finding
顶部文档把脚本描述成单纯的“薄客户端”,暗示职责仅是转发请求;然而代码实际还执行本地配置解析、环境变量身份参数收集、自动调用 API key 接口,以及将凭证写入磁盘等状态管理和副作用操作。这些行为超出了“仅负责通信”的表述,构成文档与实现意图的不一致。

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
This code file contains natural-language strings that effectively force a specific language/locale for users. Under the policy, language restrictions should either be optional for the user or clearly justified as region-specific; neither is present here.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The script can automatically request API keys from a remote service and collects account identifiers, email, and phone-related values from environment variables to do so. For a trading-indicator skill, this is unrelated privilege expansion and increases the chance of silent account creation, unexpected identity linkage, and unauthorized outbound communication.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The code persists fetched remote credentials to local disk by default, including plaintext storage in config/api_key.txt. Storing API credentials automatically expands the blast radius of compromise on shared systems, makes accidental repo inclusion more likely, and is disproportionate to the advertised purpose of a technical-analysis helper.

Natural-Language Policy Violations

Low
Confidence
72% confidence
Finding
The entire skill documentation is written in Chinese and does not indicate that other languages are supported or that Chinese is a required locale for a region-specific tool. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation when no choice or justification is provided.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The manifest presents the skill name and description entirely in Chinese, with no indication that users can choose another language or that the skill is intended only for a Chinese-speaking or region-specific audience. This can violate language/locale policy requirements when a skill implicitly forces one language without opt-in or justification.

Static analysis

No suspicious patterns detected.