Back to skill

Security audit

unisound-abnormal-items

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims overall, but it can send complete sensitive medical reports to a remote API despite promising de-identification that the code does not perform.

Review before installing or using this skill with real patient data. Only submit pre-redacted reports, verify the API endpoint you intend to use, avoid custom base URLs unless you fully trust them, and treat the app key as a credential that could be exposed to the configured endpoint.

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/run.py:137
Finding
Medical reports are transmitted without the promised local de-identification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:137-143`; related privacy claim at `SKILL.md:27-29` **Vulnerability Type**: Sensitive data disclosure caused by missing de-identification **Risk Level**: High ### Vulnerable Code ```python def run_abnormal_items(report_text: str, llm, output_path: str = "") -> int: prompt = f"""请对以下体检报告中的异常指标进行专项深度解读。 【体检报告/异常指标信息】 {report_text.strip()} 请严格按照要求输出 JSON + 专项解读。""" print("正在进行异常指标专项解读...") result = llm([sys_msg(SYSTEM_PROMPT), user_msg(prompt)]) ``` The corresponding documentation states: ```markdown - **最小必要原则**:仅处理指标解读所必需的检查数据;不要求包含直接身份标识。 - **严格脱敏**:发送前对可识别身份信息进行脱敏处理。 - **不做本地持久化**:仅在内存中短暂处理;**本次调用结束即销毁**。 ``` ### Technical Analysis The skill accepts complete medical reports and directly interpolates the unmodified `report_text` into the prompt sent to the external model API. No local routine identifies, removes, masks, or validates names, patient identifiers, addresses, telephone numbers, dates of birth, or other identifying fields. This behavior conflicts with the documented promise that identifiable information is strictly de-identified before transmission. Because the documented input format permits a complete examination report, the transmitted content may combine direct identifiers with sensitive medical findings. The disclosure is not limited to fields needed for interpretation: the entire loaded text is transmitted. The default destination is the external endpoint `https://maas-api.hivoice.cn/v1/chat/completions`. ### Attack Path 1. A user supplies a complete health examination report containing identifying and medical information through `--input`. 2. `load_input` reads the report without redacting sensitive fields. 3. `run_abnormal_items` inserts the complete report into `prompt`. 4. `llm(...)` passes that prompt to `_http_post`. 5. The external API receives the identifiable medical report despite the skill's de-identification claim. ### Impact Assessment T ...[truncated 528 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Implement deterministic local de-identification before constructing the prompt. At minimum, detect and mask names, patient and examination identifiers, telephone numbers, email addresses, street addresses, government identifiers, and exact dates of birth. 2. Prefer an allowlist-based parser that extracts only medically necessary fields instead of sending the complete input document. 3. Display a clear warning and require explicit confirmation if likely identifiers remain after redaction. 4. Provide users with a preview of the exact redacted content that will be transmitted. 5. Add automated tests containing representative identifiers and verify that none appear in outgoing request bodies. 6. Document the external data transfer, destination, retention assumptions, and limitations of automated redaction. 7. If reliable de-identification cannot be guaranteed, remove the strict de-identification claim and require callers to submit pre-redacted reports. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:48
Finding
Arbitrary API base URL can expose the bearer credential and medical report<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:48-51`, `scripts/run.py:212`, and `scripts/run.py:222` **Vulnerability Type**: Unrestricted credential-bearing outbound request **Risk Level**: High ### Vulnerable Code ```python def make_llm_caller(appkey: str, base: str = DEFAULT_LLM_BASE, model: str = DEFAULT_LLM_MODEL, timeout: int = 0): url = f"{base.rstrip('/')}/chat/completions" headers = {"Authorization": f"Bearer {appkey}"} def llm(messages: List[Dict[str, str]]) -> str: payload = {"model": model, "messages": messages, "temperature": 0} resp = _http_post(url, payload, headers, timeout=timeout) try: return resp["choices"][0]["message"]["content"].strip() except (KeyError, IndexError, TypeError) as e: raise RuntimeError(f"Unexpected LLM response: {resp}") from e return llm ``` The destination is accepted directly from the command line: ```python parser.add_argument("--base", default=DEFAULT_LLM_BASE, help=f"内部大模型 base URL(默认:{DEFAULT_LLM_BASE})。") ``` It is then used without validation: ```python llm = make_llm_caller(args.appkey, args.base, args.model, args.timeout) ``` ### Technical Analysis The `--base` option accepts an arbitrary URL. The code appends `/chat/completions` and sends an `Authorization: Bearer <appkey>` header to that destination. It also places the complete medical-report prompt in the request body. There is no hostname allowlist, scheme validation, TLS requirement, custom-endpoint warning, or separation between credentials intended for the default service and credentials used for alternate endpoints. Consequently, a caller can select an attacker-controlled HTTP or HTTPS service and cause the skill to disclose both the API credential and health-report data. The URL handler may also follow redirects. The implementation does not explicitly verify the final destination or ensure that credential-bearing requests remain on the approved host. ...[truncated 1227 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the user-configurable base URL if custom endpoints are not a strict functional requirement. 2. Otherwise, enforce an explicit allowlist of approved HTTPS hostnames and ports. 3. Reject plaintext HTTP, URLs containing embedded credentials, unexpected ports, IP-literal destinations, and malformed URLs. 4. Validate the final destination after every redirect, or disable redirects for credential-bearing requests. 5. Never forward a credential issued for the default service to a custom endpoint. Require a separate, endpoint-specific, least-privilege credential. 6. Require explicit user confirmation before sending medical information to any non-default destination and display the normalized destination hostname. 7. Use short-lived, narrowly scoped API credentials and provide immediate revocation and rotation procedures. 8. Add tests verifying that unapproved schemes, hosts, ports, and redirect destinations are rejected before any request or authorization header is sent. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises runnable commands and external API access, implying file read, file write, and network capabilities, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization and transparency gap: a host may permit broader-than-expected actions, and users or reviewers cannot easily verify whether filesystem and network access are intentionally constrained.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The description specifies the skill's behavior entirely in Chinese and states the output is '语言通俗易懂', with no indication that other languages are supported or that the user can opt in to Chinese. Under the policy rule, a skill that forces a specific language without user choice is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill sends full health-exam report content directly to a remote LLM endpoint, and the content is likely to contain highly sensitive medical and possibly identifying information. There is no explicit user-facing consent notice, minimization step, redaction, or safeguard around third-party transmission, which creates a real privacy and compliance risk if the service is external, logged, retained, or misconfigured.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The system prompt requires the model to produce Chinese-oriented patient-facing output and examples entirely in Chinese, with no option for the user to select another language or locale. This is a language policy issue because the skill imposes a specific language by default rather than offering an opt-in or configurable choice.

Static analysis

No suspicious patterns detected.