Back to skill

Security audit

unisound-common-disease-advice

Security checks for vulnerabilities and agentic risk

Overview

This medical advice skill is coherent in purpose, but it sends sensitive patient information to a remote model while promising de-identification that the code does not perform.

Review before installing or using with real patients. Do not provide names, IDs, phone numbers, exact addresses, medical record numbers, or other identifiers unless the skill is changed to perform local de-identification and you have approval for the remote endpoint's data handling. Treat all generated diagnoses, medication doses, and referral recommendations as untrusted decision support requiring clinician review.

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:123
Finding
Patient Data Is Transmitted Without the Promised De-identification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:123-132` **Related Documentation**: `SKILL.md:27-30` **Vulnerability Type**: Sensitive patient-data disclosure caused by missing de-identification **Risk Level**: High ### Vulnerable Code ```python def run_advice(patient_info: str, llm, output_path: str = "") -> int: prompt = f"""请根据以下患者信息,给出常见病诊疗建议。 【患者信息】 {patient_info.strip()} 请严格按照要求输出 JSON + 摘要。""" print("正在分析患者信息并生成诊疗建议...") result = llm([sys_msg(SYSTEM_PROMPT), user_msg(prompt)]) ``` The relevant documentation states that identifiable information will be de-identified before being sent to any model or API. However, the implementation does not contain identifier detection, redaction, pseudonymization, or filtering logic. ### Technical Analysis `load_input()` accepts arbitrary text or JSON-based patient records and returns their content without modification. `run_advice()` then interpolates the complete value of `patient_info` into the LLM prompt. The resulting prompt is sent through `make_llm_caller()` to the configured `/chat/completions` endpoint. Consequently, names, telephone numbers, government identifiers, detailed addresses, medical record numbers, and other protected health information present in the input may be transmitted to a remote service verbatim. This behavior directly conflicts with the documented guarantee of strict de-identification. The use of HTTPS protects the transport channel against ordinary passive interception, but it does not prevent the receiving API operator from obtaining the unredacted data. The caller-controlled `--base` option can also redirect transmission to a different endpoint when an unsafe or incorrect base URL is supplied. ### Attack Path 1. A patient-information file contains clinical data together with direct identifiers. 2. `load_input()` reads and returns the record without sanitization. 3. `run_advice()` inserts the entire record into the prompt using `patient_info.s ...[truncated 870 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Implement local de-identification before constructing the prompt. Detect and redact, at minimum: - Names - Telephone numbers and email addresses - Government and insurance identifiers - Medical record numbers - Exact addresses - Other organization-specific patient identifiers 2. Use an allowlisted structured input schema so only clinically necessary fields are transmitted. 3. Display a blocking warning or require explicit confirmation if likely identifiers remain after redaction. 4. Add automated tests covering identifiers in both plain-text and JSON input. 5. Restrict `--base` to an approved HTTPS endpoint allowlist, or require an explicit unsafe-development flag for custom endpoints. 6. Clearly document residual privacy limitations and data-handling behavior. 7. Do not claim strict de-identification until the control is implemented and validated. 8. Review the remote provider's retention, logging, access-control, and data-processing policies before transmitting health information. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:123
Finding
Untrusted Patient Records Can Inject Instructions into the Medical LLM Prompt<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:123-154` **Vulnerability Type**: Prompt injection with insufficient output validation **Risk Level**: High ### Vulnerable Code ```python def run_advice(patient_info: str, llm, output_path: str = "") -> int: prompt = f"""请根据以下患者信息,给出常见病诊疗建议。 【患者信息】 {patient_info.strip()} 请严格按照要求输出 JSON + 摘要。""" print("正在分析患者信息并生成诊疗建议...") result = llm([sys_msg(SYSTEM_PROMPT), user_msg(prompt)]) # 分离 JSON 和摘要 json_part = "" summary_part = "" if "【摘要】" in result: idx = result.index("【摘要】") json_part = result[:idx].strip() summary_part = result[idx:].strip() else: json_part = result # 验证 JSON 可解析 parsed_json = None try: # 提取 JSON 块(可能包含 markdown 代码块) if "```" in json_part: start = json_part.find("{") end = json_part.rfind("}") + 1 json_part = json_part[start:end] parsed_json = json.loads(json_part) ``` ### Technical Analysis Patient records are untrusted input, but the implementation directly concatenates them into an instruction-bearing user message. There is no separation between clinical data and instructions found inside that data, and the system prompt does not explicitly require the model to treat embedded directives as untrusted record content. A crafted record can therefore include text instructing the model to ignore the required clinical rules, change referral decisions, recommend a particular medication, omit warnings, or generate misleading output. The response control only attempts to parse the result with `json.loads()`. JSON syntax validation does not validate: - Required keys - Field types - The maximum number of diagnoses - Permitted probability values - Medication safety - Referral consistency - The presence of warnings for red-flag symptoms - Whether the output was influenced by instructions embedded in the patient record A malicious response that r ...[truncated 1560 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly state in the system prompt that all content inside the patient-data section is untrusted data and that instructions found there must never be followed. 2. Serialize patient information into a strict structured schema instead of embedding unrestricted text in an instruction-bearing prompt. 3. Delimit and encode patient content clearly, while recognizing that delimiters alone are not a complete prompt-injection defense. 4. Validate the response against a strict schema, including: - Required and additional properties - Exact field types - A maximum of three diagnoses - Enumerated probability values - Boolean and nullable-field constraints 5. Add deterministic checks for high-risk output: - Medication dose and contraindication validation - Referral consistency - Mandatory escalation for recognized red-flag symptoms - Rejection of unsupported or missing clinical fields 6. Fail closed when output does not satisfy the schema or safety rules instead of preserving and presenting raw model output. 7. Clearly label all generated content as untrusted decision support requiring clinician review. 8. Add adversarial tests using patient records containing instruction-override attempts and verify that those instructions do not alter the output policy. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Missing User Warnings

High
Confidence
96% confidence
Finding
The script sends raw patient information to a remote LLM endpoint via `run_advice(patient_info, llm, ...)` without any built-in consent notice, privacy warning, minimization, or de-identification step. In a medical context this is especially sensitive because symptom narratives may contain personally identifiable and protected health information, creating confidentiality, regulatory, and data-handling risks if operators use the tool without understanding the external transmission.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill describes capabilities to read input files, write output files, and make outbound network requests to a remote medical-model API, but it does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch weakens least-privilege controls and makes it harder for a platform to constrain what the skill may access, especially given the handling of sensitive patient data and external transmission.

Natural-Language Policy Violations

Medium
Confidence
86% confidence
Finding
The file's natural-language instructions, prompts, and output format are entirely fixed in Chinese, which imposes a specific language/locale on users. The file does not offer an opt-in language choice or explain that the skill is intentionally restricted to a Chinese-language regional context.

Static analysis

No suspicious patterns detected.