Back to skill

Security audit

unisound-pe-missing-positive

Security checks for vulnerabilities and agentic risk

Overview

The skill fits its medical-record quality-control purpose, but it needs Review because it can send sensitive records and a bearer key to a user-supplied model endpoint and imports unverified shared code.

Install only in a controlled clinical environment. Use de-identified records, an approved HTTPS model endpoint, a narrowly scoped app key, and a trusted shared preprocessing directory. Do not pass an untrusted --base URL, avoid --save-prepared unless the output directory is protected, and treat results as advisory requiring clinician review.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/emr_qc_impl.py:54
Finding
Attacker-Controlled API Endpoint Can Exfiltrate the Bearer Key and Medical Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/emr_qc_impl.py:54-62`; user-controlled configuration originates from `scripts/emr_qc.py:24-27` and `scripts/run.py:50-53` **Vulnerability Type**: Unvalidated outbound request destination and sensitive-data disclosure **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): """返回一个 llm(messages) → str 的调用函数。""" 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) ``` The endpoint is exposed directly as a command-line option: ```python parser.add_argument( "--base", default=DEFAULT_LLM_BASE, help=f"大模型 base URL(默认:{DEFAULT_LLM_BASE})。", ) ``` ### Technical Analysis The `--base` argument is accepted without validating its scheme, hostname, port, or resolved network address. The application appends `/chat/completions` to this value and sends an HTTP POST request containing: - The API key in an `Authorization: Bearer` header. - Medical record content in the chat-completions payload. - The selected model and prompt instructions. The code does not require HTTPS, restrict the destination to the intended HiVoice service, prevent requests to private or loopback addresses, or explicitly prevent cross-origin redirects. Therefore, a malicious invocation or unsafe wrapper can redirect credentials and patient-derived data to an attacker-controlled endpoint. Because arbitrary URLs can be supplied, the behavior also provides a limited server-side request forgery primitive in environments where the program has access to internal services. ### Attack Path 1. An attacker persuades a user, automation system, or deployment configuration to invoke the Skil ...[truncated 1307 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https` for all production endpoints. 2. Allowlist the exact intended API hostname, such as `maas-api.hivoice.cn`, rather than accepting arbitrary destinations. 3. Reject URLs containing embedded credentials, fragments, unexpected ports, or non-HTTP schemes. 4. Resolve the hostname and reject loopback, private, link-local, multicast, and otherwise reserved addresses unless an explicitly trusted internal deployment requires them. 5. Disable redirects or verify every redirect destination against the same scheme and hostname policy before forwarding the authorization header. 6. If custom endpoints are operationally necessary, place them behind an explicit opt-in configuration reserved for trusted administrators and do not expose the option to untrusted callers. 7. Use separate, narrowly scoped credentials for each approved provider and deployment. 8. Clearly warn users before transmitting medical data and ensure the destination complies with the applicable privacy and retention requirements. ]]>

T08 · Insecure Dependencies

Error
Location
scripts/run.py:10
Finding
Unverified Shared-Path Import Can Execute Attacker-Controlled Python Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:10-20` **Vulnerability Type**: Unsafe dependency loading and Python import-path manipulation **Risk Level**: High ### Vulnerable Code ```python SCRIPT_DIR = Path(__file__).resolve().parent SKILLS_ROOT = SCRIPT_DIR.parents[3] _preprocess_dir = SKILLS_ROOT / "_shared" / "doc-preprocess" / "scripts" for p in (_preprocess_dir, SCRIPT_DIR): s = str(p) if s not in sys.path: sys.path.insert(0, s) from preprocess import ( # noqa: E402 PreprocessError, SUPPORTED_FILE_TYPES, detect_input_type, load_input_artifact, ) ``` ### Technical Analysis The program calculates a shared directory outside this Skill's package, prepends that directory to `sys.path`, and imports a module named `preprocess`. No package version, cryptographic digest, file owner, directory permissions, or trusted installation root is verified. Python executes module-level code immediately when importing a module. Consequently, write access to the calculated `_shared/doc-preprocess/scripts/preprocess.py` file or its containing directory is equivalent to arbitrary code execution whenever `scripts/run.py` starts. The risk is increased by using `SCRIPT_DIR.parents[3]`: installation or relocation changes can cause the shared dependency path to resolve somewhere outside the reviewed artifact. The implementation therefore relies on code that was not included in the audited project structure and whose integrity is not established by this Skill. ### Attack Path 1. The attacker determines the path produced by: ```python SCRIPT_DIR.parents[3] / "_shared" / "doc-preprocess" / "scripts" ``` 2. The attacker obtains write access to that directory or causes the Skill to be installed under a directory hierarchy they control. 3. The attacker places a malicious `preprocess.py` at that location. Module-level code can read files, access environment variables, launch processes, or modify application behavior. ...[truncated 967 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Package the preprocessing implementation as a normal, versioned Python package and import it through an installed package namespace. 2. Pin the dependency to an approved version and verify it with package hashes during installation. 3. Prefer package-relative imports for code distributed with the Skill. 4. Remove runtime `sys.path` mutation, especially insertion of directories outside the reviewed package. 5. If a shared directory is unavoidable: - Resolve and compare it against an administrator-configured trusted root. - Verify ownership and reject directories or files writable by untrusted users. - Verify the dependency against a cryptographic digest or signed manifest. - Fail closed if integrity validation cannot be completed. 6. Run the Skill under a minimally privileged service account with restricted filesystem and network access. 7. Include the exact preprocessing dependency in the audit and release manifest so the effective executable code is reviewable. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/emr_qc_impl.py:122
Finding
Medical Record Fields Can Inject Instructions into the LLM Quality-Control Prompt<![CDATA[ ## Vulnerability Details **File Location**: `scripts/emr_qc_impl.py:122-170` **Vulnerability Type**: Indirect prompt injection and unvalidated model output **Risk Level**: Medium ### Vulnerable Code ```python return llm([user_msg( f"""你是一位病历质控专家,请对门诊病历进行质控,如果没有缺陷请直接回答"无缺陷",不要进行任何分析;如果有缺陷请先回答"有缺陷"再另起一行分析原因 缺陷描述: 体格检查中没有与诊断相关的阳性体征 以下是一些示例,用<example></example>标记,请参考 <example> 【病历】 体格检查:右侧甲状腺下极可触及一1.0x1.0cm包块,质韧,边界清,活动度差,无压痛。 诊断:甲状腺多发结节。 【质控结果】 无缺陷 </example> <example> 【病历】 体格检查:神清,身高167cm,体重53kg 诊断:1.肺气肿 2.支气管哮喘 【质控结果】 有缺陷 支气管哮喘患者,体格检查中没有提到哮鸣音相关的体征 </example> 现在请对下面的门诊病历进行质控,诊断类型仅限于以下几种,如果不是,请直接回答"无缺陷" 1.诊断是"哮喘"相关,体格检查中未提及"哮鸣音|啰音|罗音|呼吸音|浊音"等体征 2.诊断是"肺炎"相关,体格检查中未提及"啰音|哮鸣音|罗音|呼吸音|浊音"等体征 3.诊断是"慢阻肺"相关,体格检查中未提及"啰音|哮鸣音|罗音|呼吸音|浊音"等体征 4.诊断是"支气管扩张伴感染"相关,体格检查中未提及"啰音|哮鸣音|罗音|呼吸音|浊音"等体征 5.诊断是"肺部感染"相关,体格检查中未提及"啰音|哮鸣音|罗音|呼吸音|浊音"等体征 6.诊断是"甲状腺结节"相关,体格检查中未提及"甲状腺下包块"等体征 7.诊断是"肺气肿"相关,体格检查中未提及"桶装胸、肋间隙饱满,语颤减弱,叩诊呈过清音"等阳性体征 【病历】 体格检查:{pe} 诊断:{dx} 【质控结果】""" )]) ``` ### Technical Analysis The application interpolates untrusted `pe` and `dx` record fields directly into the same user-role message that contains the quality-control policy and examples. The record is not encoded as a strict data structure, and the model is not given a higher-priority system instruction that identifies embedded instructions as untrusted data. An attacker who controls or influences the medical record can insert text resembling prompt delimiters, result sections, examples, or direct instructions to ignore the preceding policy. The LLM may interpret those strings as instructions rather than clinical data. The returned text is accepted without enforcing the documented output grammar. Any model response is written to the output file and printed as the result: ```python qc_result = qc_pe_missing_positive(fields, llm) out.write_text(qc_result, encoding="utf-8") print(qc_result) ``` This issue affects result integrity rather than directly granting operating-system code execution. ### Attack ...[truncated 1298 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Move the immutable quality-control policy into a system-role message. 2. State explicitly that all record fields are untrusted data and that instructions, examples, role markers, or result markers found inside them must never be followed. 3. Serialize the record as structured JSON or another rigid schema rather than interpolating it into free-form instructional text. 4. Use unique delimiters and reject or escape delimiter sequences appearing in record fields. 5. Validate the response against an allowlisted grammar: - Exactly the no-defect token; or - The defect token followed by a bounded plain-text explanation. 6. Reject and retry responses containing unexpected role markers, prompt fragments, excessive content, or unsupported verdicts. 7. Implement the listed deterministic keyword rules in local code where practical, reducing reliance on free-form model instruction following. 8. Treat the model output as advisory and require human review before it affects clinical or compliance decisions. 9. Add adversarial tests containing fake result sections, delimiter injection, instruction overrides, and multilingual prompt-injection attempts. ]]>
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 (8)

Missing User Warnings

High
Confidence
96% confidence
Finding
The code sends raw outpatient medical record content, including physical exam and diagnosis fields, to an external LLM service at a third-party endpoint. In a medical context this is highly sensitive health data, and the file shows no consent flow, minimization, de-identification, disclosure, or enforcement that the endpoint is approved for PHI handling, creating a real confidentiality and compliance risk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises capabilities that include file read, file write, and network access, but the manifest does not declare any explicit tool scope or permission boundaries. In a skill that processes medical records and calls an external API, this creates unnecessary ambiguity about what the skill is allowed to access and transmit, increasing the risk of overbroad data access or accidental exfiltration of sensitive health information.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The prompt content requires the model to perform the task and return outputs entirely in Chinese, including fixed Chinese response phrases like "无缺陷" and "有缺陷". There is no opt-in, language selection, or documented justification that the skill is intentionally limited to a Chinese-language clinical workflow.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
When --save-prepared is used, the script writes preprocessed medical record text directly to disk, which can include highly sensitive patient data. There is no privacy warning, redaction, encryption, permission hardening, or retention control, so operators may inadvertently create plaintext PHI artifacts that persist on shared systems or in insecure output locations.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script sends full clinical record text to a model endpoint specified by --base/--model using an appkey, but the CLI does not clearly disclose the privacy implications or enforce that the endpoint is trusted and internal. In a medical context, transmitting EMR content to the wrong service, misconfigured endpoint, or noncompliant environment could cause unauthorized disclosure of protected health information.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The natural-language content of the skill, including description, usage, and warnings, is presented only in Chinese. This can constitute a language/locale policy issue because the skill effectively forces a specific language without stating that the skill is Chinese-only or offering an alternative language option.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The file’s user-facing docstring and CLI descriptions are entirely in Chinese, with no indication that another language is supported or that the skill is intentionally restricted to a Chinese-only audience. Under the language/locale policy, forcing a specific language without opt-in can be a natural-language policy issue.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The module docstring and all argparse descriptions/help strings are presented only in Chinese, which imposes a specific language on users without opt-in or alternative locale support. This matches the language/locale policy concern for natural-language content embedded in code.

Static analysis

No suspicious patterns detected.