Back to skill

Security audit

unisound-diagnosis-review

Security checks for vulnerabilities and agentic risk

Overview

This medical-record review skill has a coherent purpose, but it can send sensitive records and a bearer credential to a caller-selected LLM endpoint and can persist prepared record text despite claiming no local persistence.

Use this only in an approved healthcare environment. Keep LLM use disabled unless the endpoint is controlled and approved, do not pass real patient identifiers unless your organization has authorized that data flow, avoid --save-prepared for PHI, and do not let untrusted callers set --base or payload.base.

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/diagnosis_review.py:246
Finding

Caller-Controlled LLM Endpoint Can Receive Medical Records and Bearer Credentials

Content
View full analysis
str: selected_model = model_name or self._settings.default_model model_config = self._settings.models.get(selected_model) if model_config is None: raise ValueError(f"未找到模型配置: {selected_model}") payload = { "model": model_config.model_id or selected_model, "messages": [{"role": "user", "content": prompt}], "temperature": model_config.temperature, } if model_config.type == "openai_compatible": return self._post_chat( url=f"{model_config.base_url.rstrip('/')}/chat/completions", payload=payload, headers={"Authorization": f"Bearer {model_config.api_key}"}, ) raise ValueError(f"不支持的模型类型: {model_config.type}") def _post_chat(self, url: str, payload: dict[str, Any], headers: dict[str, str]) -> str: body = json.dumps(payload, ensure_ascii=False).encode("utf-8") req = request.Request( url=url, data=body, headers={"Content-Type": "application/json", **{key: value for key, value in headers.items() if value}}, method="POST", ) opener = request.urlopen(req) if not self._settings.timeout else request.urlopen(req, timeout=self._settings.timeout) with opener as response: response_payload = json.loads(response.read().decode("utf-8")) ``` The endpoint and credential are populated from the request payload: ```python def _load_llm_client(use_llm: bool, appkey: str, base: str = DEFAULT_LLM_BASE, model: st ...[truncated 3384 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/diagnosis_review.py:496
Finding

Medical Record Content Is Vulnerable to LLM Prompt Injection

Content
View full analysis
str: evidence_text = "\n\n".join( ( f"【文书#{doc['doc_index']} / {doc['doc_type']} / {doc['file_name']}】\n" f"{doc['content']}" ) for doc in evidence_docs ) prompt = f""" 你是一个医疗审核专家。你的任务是根据{guideline.diagnosis_code} {guideline.diagnosis_name}的审核规则审核病例文书内容,判断是否满足审核点要求,返回该审核的审核结果。 ## 审核规则 {guideline.diagnosis_name} ## 病例文书内容(已按依赖文书过滤) {evidence_text or '未检索到优先文书'} 注意: -先看审核规则,然后按照审核规则对病例文书内容进行审核; 输出格式要求: {{ "final_decision":"通过" 或 "不通过", "confidence": 0.0-1.0之间的浮点数(表示对最终结论的置信度), "reasoning": "详细的审核理由和推理过程...", "evidence": "列出审核规则中每个审核条目的审核结果(特别是对不通过的审核条件)", "source": "列出每个审核条目审核结果对应的病例文书原始内容", }} 请直接返回JSON,不要添加其他说明。 """ return prompt ``` The resulting prompt is sent directly to the model: ```python prompt = build_diagnosis_review_prompt(case_id, diagnosis, guideline, evidence_docs) try: raw_response = llm_client.complete(prompt, model_name=model_name) audit_thinking = _extract_audit_thinking(raw_response) parsed = parse_json_object(raw_response) review_payload = _coerce_llm_review_payload(parsed, docs) ``` ### Technical Analysis Medical-document content is untrusted input. It may originate from uploaded PDF, DOC, DOCX, spreadsheet, CSV, text, or JSON files. The code concatenates this content into the same user message that contains the model's operational instructions. There is no strong instruction/data separation and no explicit rule telling the model that commands found inside medical documents must be treated solely as quoted ...[truncated 1976 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tainted flow: 'req' from os.getenv (line 264, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/diagnosis_review.py (reported line 180)May include surrounding context.

python
headers["Authorization"] = f"Bearer {self._settings.api_key}"
        req = request.Request(url=url, headers=headers, method="GET")
        try:
            with request.urlopen(req, timeout=self._settings.timeout) as response:
                return json.loads(response.read().decode("utf-8"))
        except error.HTTPError as exc:
            if exc.code == 404:

Tainted flow: 'req' from os.getenv (line 264, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/diagnosis_review.py (reported line 270)May include surrounding context.

python
headers={"Content-Type": "application/json", **{key: value for key, value in headers.items() if value}},
            method="POST",
        )
        opener = request.urlopen(req) if not self._settings.timeout else request.urlopen(req, timeout=self._settings.timeout)
        with opener as response:
            response_payload = json.loads(response.read().decode("utf-8"))
        choices = response_payload.get("choices") or []

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 164)May include surrounding context.

md
- 发布目录只保留 `SKILL.md`、`_meta.json`、`scripts/`;示例输入、运行输出、自测脚本放在 skill 包外。

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/diagnosis_review.py (reported line 100)May include surrounding context.

python
def load_guideline_api_settings_from_env() -> GuidelineApiSettings:
    return GuidelineApiSettings(
        base_url=_required_env("GUIDELINE_API_BASE"),
        api_key=os.getenv("GUIDELINE_API_KEY", ""),
        timeout=_env_int("GUIDELINE_API_TIMEOUT", 30),

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/diagnosis_review.py (reported line 337)May include surrounding context.

python
for candidate in diagnosis_code_lookup_candidates(diagnosis_code):
        guideline = guideline_repository.find_diagnosis_guideline_by_code(candidate)
        if guideline is not None:
            return guideline
    return None

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/diagnosis_review.py (reported line 525)May include surrounding context.

python
}}
请直接返回JSON,不要添加其他说明。
    """
    return prompt

def _snippet_from_doc(content: str, term: str, radius: int = 36) -> tuple[str, int, int] | None:
    if not term:

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

At this call site, the record payload's document content is assembled into prompts and transmitted to a remote LLM without any disclosure, consent mechanism, or visible safeguard. Because the documents are medical records, this materially increases the severity: the feature can expose PHI and sensitive clinical narratives to third-party services without user awareness.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code allows callers to supply an arbitrary LLM base URL via payload/CLI and then sends full diagnosis-review prompts containing medical record excerpts to that endpoint. This creates a direct exfiltration channel where any user able to invoke the skill can route sensitive patient data to an attacker-controlled server under the guise of normal model inference.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill exposes capabilities that include environment-variable access, file read/write, and network access, but it does not declare any explicit tool scope such as permissions or allowed-tools. In this skill, those capabilities are material because it handles sensitive medical records, accepts an appkey at runtime, reads guideline configuration from environment variables, can save prepared records to disk, and calls external/internal APIs; without an explicit least-privilege declaration, the runtime may grant broader access than necessary and increase the blast radius of misuse or prompt-injection-driven actions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill processes sensitive medical records but is implemented to send case data and diagnosis context to remote services for guideline lookup and LLM review. In a medical auditing context, silent external transmission of PHI/medical content is dangerous because it can violate privacy requirements, data residency rules, and organizational controls even if the remote service is otherwise legitimate.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The diagnosis guideline lookup sends diagnosis codes to a remote API without disclosure. Although diagnosis codes are less sensitive than full chart text, they can still reveal medical conditions and become identifying when linked with workflow context, so undisclosed transmission remains a real privacy issue.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The debug helper writes the fully prepared medical-record text to disk, including document contents aggregated from the patient record. Because this data is sensitive and the file is saved in a predictable location, it can persist beyond the review operation and be exposed to other users, backups, logs, or accidental sharing.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script packages medical-record content into a payload and sends it to an LLM backend via review_diagnosis_payload, with the endpoint and model selectable from CLI arguments. In a medical diagnosis-review skill, transmitting PHI/medical text to an external or configurable service without strict boundary controls, minimization, and explicit disclosure creates a real confidentiality and compliance risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script forwards the assembled payload, which may include full medical-record text and diagnosis data, to an LLM service without any user-facing disclosure at the point of transmission. Given the highly sensitive healthcare context, lack of explicit notice, consent, and transmission controls materially increases privacy, contractual, and regulatory exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The --save-prepared option can be enabled during normal execution and causes preprocessed medical text to be stored locally, but the CLI help provides no strong privacy warning or safeguards. In a healthcare context, silent persistence of patient text is dangerous because operators may enable debugging without realizing they are creating durable PHI artifacts.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.