Back to skill

Security audit

unisound-surgery-sufficiency-review

Security checks for vulnerabilities and agentic risk

Overview

The skill performs the advertised medical-record review, but it can send sensitive record text and the supplied model credential to an unrestricted caller-selected LLM endpoint.

Install only in an environment where operators understand that records may be sent to an LLM service. Keep use to a fixed trusted HTTPS endpoint, avoid passing a custom --base from untrusted input, de-identify records before use, protect the appkey, prefer --no-llm for offline review when possible, and avoid --save-prepared unless local retention of medical text is acceptable.

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/surgery_sufficiency_review.py:509
Finding

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

Content
View full analysis
None: self._settings = llm_settings def complete(self, prompt: str, model_name: str | None = None) -> 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")) ``` ```python llm_client = _load_llm_client( use_llm, ...[truncated 3347 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/surgery_sufficiency_review.py:410
Finding

Untrusted Medical-Record Content Can Manipulate the LLM Review Decision

Content
View full analysis
list[dict[str, Any]]: preferred_doc_types = guideline.required_doc_types or DEFAULT_SURGERY_DOC_TYPES selected: list[dict[str, Any]] = [] docs = record_payload.get("docs", []) for doc_type in preferred_doc_types: for index, doc in enumerate(docs): if not isinstance(doc, dict) or not _doc_matches_type(doc, doc_type): continue selected.append( { "doc_index": index, "doc_type": str(doc.get("docClassName", "")), "file_name": str(doc.get("fileName", "")), "content": truncate_text_by_utf8_bytes(str(doc.get("content", ""))), } ) if len(selected) >= 8: return selected ``` The untrusted evidence is interpolated into the same prompt as the audit instructions: ```python def _build_prompt( *, case_id: str, surgery: CaseSurgery, guideline: SufficiencyGuideline, evidence_docs: list[dict[str, Any]], ) -> str: role_label = "主手术" if _scope(surgery.role) == "primary" else "其他手术" prompt=f""" 你是住院病历"手术/操作依据充分性审核助手"。 请严格根据【审核指南】、【待审核手术信息】和【病例文书】完成审核。不得脑补。 你的任务判断"手术依据是否充分" - 主手术:必须同时满足"手术指征成立"+"是本次住院核心手术"。 - 其他手术:只要求"手术指征成立",且在住院管理中有合理记录。 - 只能依据输入内容判断,不得脑补。 - 注意病历文书截断情况:若因篇幅限制导致部分文书内容未完整展示,截断本身不能作为"手术依据不充分"的理由。 - 审核时只能基于当前可见内容判断:可见内容中如存在支持目标手术指征成立的证据,应予采纳,并可作为"依据充分"的依据; - 不得将"证据可能出现在被截断部分"作为判定不通过的直接原因。 请严格按以下 JSON 格式输出: {{ "surgery_name_equivalence": "等价" 或 "不等价", "equivalence_reason": "说明原因", ...[truncated 3190 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 (16)

Tainted flow: 'req' from os.getenv (line 525, 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/surgery_sufficiency_review.py (reported line 174)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 525, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The code sends prompts containing medical record evidence to an LLM endpoint using credentials and a base URL derived from runtime input/environment. Because the destination can be user-controlled via payload fields and there is no allowlist or egress restriction, sensitive patient data can be exfiltrated to arbitrary external services.

Content

Scanner excerpt · scripts/surgery_sufficiency_review.py (reported line 531)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 166)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/surgery_sufficiency_review.py (reported line 87)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/surgery_sufficiency_review.py (reported line 361)May include surrounding context.

python
for candidate in surgery_code_lookup_candidates(surgery.code):
        guideline = repository.find_surgery_sufficiency_guideline_by_code_scope(candidate, scope)
        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/surgery_sufficiency_review.py (reported line 456)May include surrounding context.

python
"""


    return prompt

def _extract_audit_thinking(text: str) -> str:
    match = re.search(r"<thinking>(.*?)</thinking>", text or "", flags=re.S)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code accepts arbitrary LLM base URLs from input and then sends evidence documents to that endpoint. An attacker can direct highly sensitive medical record content to an attacker-controlled server, making this a straightforward exfiltration channel.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises capabilities that include environment access, file read/write, and network use, but the manifest does not declare any explicit tool scope or permission boundaries. In a medical-review skill that processes sensitive records and accepts an appkey, this increases the risk of unintended data exposure, unauthorized outbound requests, or filesystem writes without clear operator visibility.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest description and the full markdown instructions are presented exclusively in Chinese, which effectively forces a specific language for users. Under the policy rule, this is a natural-language locale constraint unless the skill offers opt-in language choice or clearly documents a justified region-specific limitation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

When --save-prepared is used, the script writes a .prepared.txt file containing concatenated patient-record text to disk. In a medical-review skill, this is sensitive PHI/medical data, and persisting it outside the primary review output increases exposure, retention, and unauthorized-access risk beyond the core stated function.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The debugging path saves full preprocessed medical-record text locally with no consent prompt, redaction, or lifecycle controls. Because the data is likely highly sensitive healthcare information, this creates a realistic confidentiality and compliance risk if files are left on disk, backed up, or accessed by other users/processes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script collects an appkey and passes the patient payload to an LLM review function, implying likely transmission of sensitive medical data to a remote service, yet there is no visible notice, confirmation, minimization, or transport/privacy control in this file. In a healthcare context, undisclosed external transmission of records materially increases privacy, regulatory, and third-party exposure risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This code path transmits patient record evidence to an external HTTP service without any explicit user-facing warning or consent mechanism. In a medical-review skill, undisclosed transfer of record contents increases privacy, compliance, and trust risks substantially.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill advertises structured case review, but the implementation forwards case document contents to an external LLM for evaluation. This creates a material hidden-data-sharing risk because users may reasonably expect local processing while the code transmits potentially sensitive medical text off-system.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The manifest says the input is a structured case record plus a surgery/operation list. However, the CLI explicitly supports many raw file formats such as pdf/doc/docx/xls/xlsx/csv/txt/json and converts them into a synthetic record payload, which expands the skill from structured-record review into document ingestion and preprocessing.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The repository fetches guideline data from a remote API by making HTTP GET requests that include surgery code and scope in the URL query/path. This file contains no user-facing notice or warning that runtime data will be sent to a remote service for guideline retrieval.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.