Back to skill

Security audit

unisound-followup-record

Security checks for vulnerabilities and agentic risk

Overview

This skill performs a coherent medical-record extraction task, but its privacy and persistence guarantees do not match the code handling sensitive medical records.

Review carefully before installing or using with real patient records. Treat inputs and outputs as sensitive, do not rely on the stated de-identification or no-persistence guarantees, use only approved HTTPS model endpoints, avoid --save-prepared unless retention is intended, and require human review of extracted clinical fields.

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

other

Error
Location
scripts/run.py:184
Finding

Medical records are transmitted without the promised de-identification

Content
View full analysis
tuple[str, str]: """构建 LLM 提示词,返回 (part1_prompt, part2_prompt).""" record = payload.get("record") or payload.get("text") or payload.get("content") or "" if not record.strip(): raise ValueError("输入缺少 record 字段") # 第一步:分块 - 将病历分为"患者的情况"和"医生的处理意见" chunk_prompt = """给定下面的病历文本,请抽取出两部分 1.患者的情况 2.医生的处理意见 输入: {} 输出: """.strip().format(record) return chunk_prompt, None ``` The derived medical content is subsequently transmitted two more times: ```python def run( payload: dict[str, Any], *, base: str, model: str, appkey: str, timeout: int ) -> str: """执行复诊病历生成.""" chunk_prompt, _ = build_prompt(payload) # 第一步:分块 chunk_result = call_llm(chunk_prompt, base=base, model=model, appkey=appkey, timeout=timeout) # 提取两部分内容 record1, record2 = extract_chunk_result(chunk_result) # 第二步:分别抽取 part1_prompt, part2_prompt = build_extract_prompts(record1, record2) part1_result = call_llm(part1_prompt, base=base, model=model, appkey=appkey, timeout=timeout) part2_result = call_llm(part2_prompt, base=base, model=model, appkey=appkey, timeout=timeout) ``` There is no redaction stage for names, government identifiers, telephone numbers, addresses, or other identifying information. Consequently, records containing personally identifiable health informat ...[truncated 1118 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:165
Finding

Unrestricted API base URL can disclose the bearer credential and medical records

Content
View full analysis
str: """调用内部医疗大模型.""" url = f"{base.rstrip('/')}/chat/completions" headers = {"Authorization": f"Bearer {appkey}"} if appkey else {} payload = { "model": model, "messages": [{"role": "user", "content": prompt}], "temperature": 0, } response = _http_post(url, payload, headers, timeout=timeout) ``` The base URL is exposed as an unrestricted command-line argument: ```python parser.add_argument( "--base", default=DEFAULT_LLM_BASE, help=f"内部大模型 base URL(默认:{DEFAULT_LLM_BASE}).", ) ``` The implementation does not enforce HTTPS, validate the destination hostname against a trusted allowlist, or prevent the credential from being sent to an untrusted origin. If an attacker can influence invocation arguments, a malicious endpoint can collect both the API key and sensitive record content. An `http://` destination can additionally expose both values in cleartext over the network. ### Attack Path 1. An attacker influences the command invocation, wrapper configuration, deployment configuration, or user instructions. 2. The attacker sets `--base` to an endpoint under their control, such as `https://attacker.example/v1`. 3. The application constructs `https://attacker.example/v1/chat/completions`. 4. `_http_post()` sends the `Authorization: Bearer ...` header and the medical-record prompt to the attacker-controlled server. 5. The attacker captures the crede ...[truncated 795 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:218
Finding

Untrusted medical-record content is directly embedded into LLM instructions

Content
View full analysis
tuple[str, str]: """构建抽取提示词.""" # 第一部分:病史相关 part1_prompt = f""" 给定患者病历中病史相关内容,请按要求抽取出其中对应的部分 1.基本原则:忠实于原文,只做拆解,不要加工 2.抽取以下字段,如果没有对应内容,请回答"未提及" {chr(10).join(FIELDS_PART1)} 病史相关内容如下: {record1} """.strip() # 第二部分:处理意见相关 part2_prompt = f""" 给定患者病历中处理意见和随诊相关内容,请按要求抽取出其中对应的部分 1.基本原则:忠实于原文,只做拆解,不要加工 2."本人就诊"不用抽取 3.抽取以下字段,如果没有对应内容,请回答"未提及" {chr(10).join(FIELDS_PART2)} 处理意见相关内容如下: {record2} """.strip() return part1_prompt, part2_prompt ``` A crafted medical record can contain instructions directing the model to ignore the extraction task, fabricate fields, omit information, or emit attacker-selected output. The first model call also processes the untrusted record and its output is then reused in subsequent prompts, allowing malicious instructions to propagate through the multi-stage pipeline. The final response is handled as free-form text. `postprocess_result()` normalizes selected lines but does not enforce an exact field allowlist, reject unexpected content, or verify that values are grounded in the source record. ### Attack Path 1. An attacker places prompt-like instructions inside a medical record or an imported document. 2. The complete record is passed to the first LLM request. 3. The first model may preserve or amplify the embedded directives in `record1` or `record2`. 4. `build_extract_prompts()` inserts that content directly after the extraction instructions. 5. The model follows the embedded direct ...[truncated 892 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The documented behavior does not match the stated purpose: it claims JSON structuring but describes line-based text output, remote transmission to an LLM API, and optional local file persistence. This mismatch can mislead users and reviewers about what data leaves the environment and how it is handled, increasing the risk of improper use with sensitive medical information.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documentation states there is no local persistence and that data is destroyed after the call, yet the interface includes options to save output JSON, output text, and prepared intermediate text to disk. In a healthcare setting, such contradictory guarantees can cause accidental retention of sensitive patient data and compliance violations.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code sends raw medical record contents to a remote LLM API via messages without any user-facing disclosure, consent, minimization, or execution-path warning. Because the data is medical and therefore highly sensitive, silent transmission to an external service can cause serious privacy, compliance, and data-governance violations even if the endpoint is described as internal.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill documentation exposes capabilities to read files, write outputs, and make network requests, but it does not declare any tool scope or permissions boundary. In a medical-records context, undeclared capability expansion is dangerous because sensitive patient data may be transmitted or persisted without clear operator awareness or policy enforcement.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill promises strict de-identification before any model or API transmission, but the documented workflow does not describe a concrete de-identification step before sending records to the remote LLM. For clinical follow-up notes, this can result in direct exposure of personal health information if operators rely on the privacy claim and submit raw records.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

模块顶层文档字符串明确写明“抽取并规范化为细粒度字段 JSON”,但实现中的提示词、后处理和输出路径都围绕换行分隔的 字段:值 文本展开,没有实际 JSON 规范化步骤。这里不是文档信息缺失,而是文档对最终产物的描述与代码行为相矛盾。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

Natural-language descriptions, prompts, and CLI help text are written exclusively in Chinese, and the skill provides no language-selection mechanism or documented opt-in for that locale. Under the stated policy, forcing a specific language without user choice is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Ssd 1

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Untrusted medical record text is interpolated directly into the prompt with no delimiting, quoting, or instruction-hierarchy safeguard, so content inside the record can masquerade as instructions to the model. In this medical context, prompt injection can degrade extraction integrity, cause omission or fabrication of clinical fields, or steer the model into disclosing/transcribing unintended content.

Content

No source excerpt is available for this finding.

Ssd 1

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The workflow feeds first-stage model output (record1, record2) back into new prompts, allowing any injected instructions or manipulated content that survives stage one to influence subsequent extraction calls. This amplification across multiple LLM steps is especially risky for medical record processing because it can systematically corrupt structured clinical outputs and make tampering harder to detect.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

清单描述该技能应将门诊复诊病历“结构化为细粒度字段,输出 JSON”,但主流程 run() 仅拼接两次 LLM 返回的文本并做字符串后处理,main() 也直接将该文本写出或打印,没有将结果解析/组装为 JSON 对象。该行为与技能对外宣称的输出形态存在直接语义不一致。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The JSON value is entirely in Chinese and provides a fixed natural-language record template/content without indicating that language selection is optional. Under the policy rule, forcing a specific language without user opt-in can be a locale-policy violation unless the constraint is explicitly justified.

Content

No source excerpt is available for this finding.