Back to skill

Security audit

unisound-lab-report-interpret

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent medical report interpreter, but its privacy claims are not matched by the code, which sends raw report text to a remote model and can save sensitive results to disk.

Review before installing or using with real patient data. Treat this as sending the full supplied report, and any identifiers it contains, to the configured model endpoint; manually de-identify reports first, avoid non-default endpoints unless trusted, use secure output locations, and do not rely on the generated interpretation without 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:143
Finding
Medical reports are transmitted without the documented de-identification## Vulnerability Details **File Location**: `SKILL.md:31-34`, `scripts/run.py:143-149` **Vulnerability Type**: Privacy control omission and transmission of sensitive medical information **Risk Level**: High The documentation states that identifiable information will be strictly de-identified before processing: ```markdown - **最小必要原则**:仅处理解读所必需的检查结果;不要求包含患者姓名等身份信息。 - **严格脱敏**:发送前对可识别身份信息进行脱敏处理。 - **不做本地持久化**:仅在内存中短暂处理;**本次调用结束即销毁**。 ``` However, the implementation inserts the complete report into an LLM request without applying redaction: ```python def run_lab_interpret(lab_report: str, llm, output_path: str = "") -> int: prompt = f"""请对以下检查报告进行辅助解读。 【检查报告】 {lab_report.strip()} 请严格按照要求输出 JSON + 摘要。""" print("正在解读检查报告...") result = llm([sys_msg(SYSTEM_PROMPT), user_msg(prompt)]) ``` ### Technical Analysis `load_input` can accept arbitrary report text, including names, patient identifiers, contact details, demographics, and clinical history. `run_lab_interpret` then copies that input verbatim into the outbound model prompt. No function detects, removes, masks, or rejects identifying data. This behavior conflicts with the documented claim that identifiable information is strictly de-identified before transmission. Merely advising users not to provide identifying information is not equivalent to enforcing de-identification. ### Attack Path 1. A user supplies a laboratory report containing patient identifiers or other protected medical information. 2. `load_input` reads and returns the report without modification. 3. `run_lab_interpret` interpolates the complete report into `prompt`. 4. The `llm` closure passes the prompt to `_http_post`. 5. The report is transmitted to the configured remote model endpoint. 6. The remote service consequently receives both the clinical information and any identifiers present in the source report. ### Impact Assessment The remote endpoint can receive ...[truncated 490 chars]
Remediation
## Remediation Suggestions 1. Add a local de-identification stage before constructing any network request. 2. Detect and redact common direct identifiers, including names, identification numbers, medical-record numbers, telephone numbers, addresses, email addresses, and account identifiers. 3. Reject or require explicit confirmation for reports that still appear to contain identifiable information. 4. Present the exact destination and categories of transmitted data before processing. 5. Minimize outbound data by extracting only clinically necessary measurements and context. 6. Add automated tests demonstrating that representative identifiers are removed before `_http_post` is called. 7. Update `SKILL.md` if de-identification cannot be reliably implemented; the documentation must accurately state that users are responsible for redaction. 8. Establish appropriate retention, access-control, and data-processing requirements for the receiving model service.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:47
Finding
Unrestricted model endpoint can receive the bearer credential and complete medical report## Vulnerability Details **File Location**: `scripts/run.py:47-53`, `scripts/run.py:203-204` **Vulnerability Type**: Unrestricted credential-bearing outbound request **Risk Level**: High The application constructs the request URL from an unrestricted caller-provided base URL and sends the API key as a bearer credential: ```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) ``` The corresponding command-line option accepts any value: ```python parser.add_argument("--base", default=DEFAULT_LLM_BASE, help=f"内部大模型 base URL(默认:{DEFAULT_LLM_BASE})。") parser.add_argument("--model", default=DEFAULT_LLM_MODEL, help=f"模型名称(默认:{DEFAULT_LLM_MODEL})。") ``` ### Technical Analysis No validation restricts `base` to the documented service, requires HTTPS, verifies an approved hostname, or prevents credentials from being sent to another origin. The generated request contains: - `Authorization: Bearer <appkey>` - The complete medical report in the request body - The system prompt and model configuration Consequently, any party that can influence the invocation arguments can redirect both the credential and sensitive report data to an arbitrary HTTP or HTTPS server. Depending on `urllib` redirect behavior and server responses, cross-origin redirect handling should also be treated as part of the trust-boundary review. ### Attack Path 1. An attacker, malicious wrapper, unsafe automation configuration, or social-engineering instruction causes the skill to run with `--base` set to an attacker-controlled endpoint. 2. `make_llm_caller` appends `/chat/completions` ...[truncated 967 chars]
Remediation
## Remediation Suggestions 1. Remove arbitrary endpoint selection in production, or enforce an explicit allowlist of trusted HTTPS origins. 2. Parse the URL and reject non-HTTPS schemes, embedded credentials, unexpected ports, fragments, and unapproved hostnames. 3. Bind each credential to its intended origin and refuse to attach it to any other destination. 4. Disable redirects or validate every redirect target before forwarding authorization headers or request data. 5. Prefer short-lived, narrowly scoped credentials with usage limits and rapid revocation support. 6. Keep endpoint configuration in a trusted administrative configuration rather than ordinary invocation arguments. 7. Warn users when a non-default endpoint is selected and never send sensitive content until explicit authorization is obtained. 8. Add tests confirming that unapproved origins and insecure schemes are rejected before network access occurs.

other

Warning
Location
scripts/run.py:143
Finding
Untrusted report content can inject instructions into the medical interpretation prompt## Vulnerability Details **File Location**: `scripts/run.py:143-149` **Vulnerability Type**: LLM prompt injection through untrusted report content **Risk Level**: Medium The report is inserted directly into an instructional user message: ```python def run_lab_interpret(lab_report: str, llm, output_path: str = "") -> int: prompt = f"""请对以下检查报告进行辅助解读。 【检查报告】 {lab_report.strip()} 请严格按照要求输出 JSON + 摘要。""" print("正在解读检查报告...") result = llm([sys_msg(SYSTEM_PROMPT), user_msg(prompt)]) ``` ### Technical Analysis The model receives trusted task instructions and untrusted report text in the same natural-language context. Delimiters are present, but they do not enforce a security boundary. A crafted report can include instructions asking the model to ignore prior requirements, suppress critical findings, fabricate normal results, disclose prompt content, or emit misleading follow-up recommendations. The application does not detect instruction-like report content, encode the report into a constrained structured representation, or independently validate medical conclusions. It only attempts to parse the generated text as JSON. Successful JSON parsing proves syntactic validity, not clinical integrity or compliance with the intended task. This is classified as an LLM prompt-injection issue rather than skill-load instruction hijacking because the hostile instructions originate from runtime report data, not from the skill instructions loaded into the agent session. ### Attack Path 1. An attacker creates or modifies a report file to include adversarial natural-language instructions alongside plausible laboratory values. 2. The application reads the file as ordinary report content. 3. `run_lab_interpret` places the malicious content directly in the LLM user message. 4. The model interprets the embedded text as instructions and may follow it instead of the intended medical-analysis requirements. 5. The model return ...[truncated 925 chars]
Remediation
## Remediation Suggestions 1. Treat all report contents as untrusted data and state explicitly in the system prompt that text inside the report must never be followed as instructions. 2. Parse supported report formats locally into a constrained schema of test names, values, units, reference ranges, and patient context before invoking the model. 3. Reject or quarantine unexpected imperative text, prompt-control phrases, markup, or unrelated content. 4. Use an API-supported structured output or JSON schema and validate every field, enumeration, and data type. 5. Independently calculate abnormal-range status and known critical-value triggers in deterministic local code. 6. Compare model conclusions against the extracted measurements and reject inconsistent urgency classifications. 7. Clearly label all model-generated interpretations as untrusted decision support requiring clinician verification. 8. Add adversarial tests containing instructions to ignore prior prompts, hide critical values, disclose system messages, or fabricate results.
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 (6)

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill describes file input/output and outbound network access to an external model endpoint, but it does not declare any explicit tool scope or permission boundaries. In an agent environment, this can lead to overbroad execution privileges, making it easier for the skill to read unintended files, write sensitive outputs, or exfiltrate patient data if invoked with unsafe inputs or modified code.

Ssd 3

Medium
Confidence
98% confidence
Finding
The code injects the full user-provided medical report directly into the prompt and then prints or saves the model output with no privacy guardrails. In a medical context, this materially raises the chance of exposing PHI both in transit and at rest, and prompt content may also be retained or logged by upstream services.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill sends raw lab report text to a remote LLM endpoint, and those reports can contain highly sensitive medical and personal data. There is no explicit consent flow, privacy notice, minimization, or redaction before transmission, so patient data may be disclosed to a third-party service unexpectedly.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The parsed interpretation and summary may be written to disk without warning, and the output can include sensitive medical information derived from the input report. Saving this data to arbitrary paths increases the risk of unauthorized access, backup leakage, shared-workstation exposure, or accidental retention beyond policy.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The system prompt instructs the model to use '国内常规标准' as the reference range basis, which imposes a specific locale/standard by default. There is no indication that users can choose another locale or that the regional constraint is exposed as an explicit, justified opt-in policy.

Static analysis

No suspicious patterns detected.