Back to skill

Security audit

unisound-medication-record-management

Security checks for vulnerabilities and agentic risk

Overview

This skill needs Review because it handles sensitive medication records through a mandatory external model call and has an unsafe fallback that can execute code outside the reviewed package.

Install only if you are comfortable sending medication names, doses, dates, statuses, and notes to the stated external model service. Prefer not to process third-party or untrusted documents with this skill, and ask the publisher for explicit consent controls, a local-only mode, data minimization, removal or verification of the external fallback import, and clearer limits around medical advice before using it with real patient data.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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)

other

Error
Location
scripts/run.py:87
Finding
Sensitive Medication Records Are Transmitted to an External Model Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:15, 21-30, 87-105` **Vulnerability Type**: Sensitive health data disclosure **Risk Level**: High ### Complete Code Snippet ```python API_URL = "https://maas-api.hivoice.cn/v1/chat/completions" MODEL = "u2-med" def _call_llm(system_prompt: str, user_prompt: str, appkey: str) -> str: payload = {"model": MODEL, "temperature": 0.0, "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}]} try: req = Request(API_URL, data=json.dumps(payload, ensure_ascii=False).encode("utf-8"), headers={"Content-Type": "application/json", "Authorization": f"Bearer {appkey}"}) resp = urlopen(req, timeout=120) return json.loads(resp.read().decode("utf-8"))["choices"][0]["message"]["content"] ``` ```python user_prompt = f"""请整理以下用药记录: 总记录数:{len(medications)} 正在服用:{len(active)}种 已停用:{len(stopped)}种 正在服用的药品: ```json {json.dumps(active, ensure_ascii=False, indent=2)} ``` 已停用的药品: ```json {json.dumps(stopped, ensure_ascii=False, indent=2)} ``` 请生成用药管理摘要,分类展示,标注长期/短期用药,给出用药提醒。""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` ### Technical Analysis The application inserts complete medication records into a model prompt and transmits that prompt to `https://maas-api.hivoice.cn/v1/chat/completions`. The transmitted fields can include medication names, doses, frequencies, treatment dates, statuses, and free-form notes. These values constitute sensitive health information, while free-form notes may also contain names, diagnoses, contact information, or other identifying details. The transfer is documented in `SKILL.md`, so it is not covert; however, external inference is mandatory, and the implementation provides no local-only mode, field minimization, pseudonymization, redaction, or technical consent mechanism. The bearer credential is placed in an HTTPS authorization header and is not wri ...[truncated 1140 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Obtain explicit, informed user consent before transmitting any health information. 2. Clearly identify the external processor, data categories sent, purpose, retention policy, jurisdiction, and applicable privacy terms. 3. Add a local-only mode for classification and summary generation where external inference is unnecessary. 4. Minimize the transmitted fields. Do not send free-form notes by default, and omit dates or other identifiers unless essential. 5. Redact or pseudonymize patient identifiers before constructing the request. 6. Add configurable organization-approved endpoints rather than relying exclusively on a fixed external service. 7. Enforce request-size and field-length limits to prevent unintended bulk disclosure. 8. Apply appropriate transport, logging, retention, access-control, and data-processing safeguards at the API provider. 9. Avoid saving unredacted prepared files by default and warn users that `--save-prepared` can create sensitive local artifacts. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/run.py:210
Finding
Fallback Dynamically Executes an Unverified Python Module Outside the Audited Package<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:210-218` **Vulnerability Type**: Untrusted local module loading and execution **Risk Level**: High ### Complete Code Snippet ```python except PreprocessError as exc: # 回退到 _shared/doc-preprocess 尝试处理 try: _shared_dir = Path(__file__).resolve().parent.parents[3] / "_shared" / "doc-preprocess" / "scripts" if not _shared_dir.exists(): print(f"ERROR: 无法读取输入文件,本地预处理失败且 _shared/doc-preprocess 不可用。原因:{exc}", file=sys.stderr) return 1 import importlib.util as _iu _spec = _iu.spec_from_file_location("_shared_preprocess", _shared_dir / "preprocess.py") _sp = _iu.module_from_spec(_spec) _spec.loader.exec_module(_sp) input_type = _sp.detect_input_type(input_path, args.input_type) ``` ### Technical Analysis When local preprocessing raises `PreprocessError`, the application constructs a filesystem path outside the skill package and executes the referenced `preprocess.py` through `exec_module()`. The fallback module is not part of the reviewed project. The code validates only that the shared directory exists; it does not verify that the target is a regular file, resolve and constrain symlinks, validate ownership or permissions, authenticate the publisher, or compare the module against a trusted cryptographic hash. Python module execution runs all top-level statements immediately. Consequently, control of the shared module is equivalent to arbitrary Python code execution under the account running the skill. This is a conditional local trust-boundary vulnerability rather than evidence that the reviewed package itself contains a malicious fallback payload. ### Attack Path 1. An attacker who can write to, replace, or redirect the expected `_shared/doc-preprocess/scripts/preprocess.py` path installs a malicious Python module. 2. The attacker or another user supplies an input that causes the packaged preprocessor to r ...[truncated 999 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove runtime loading of arbitrary filesystem modules and package the fallback implementation with the skill. 2. Prefer a version-pinned, signed, and audited dependency installed through a controlled package-management process. 3. If external loading is unavoidable: - Resolve the target with `Path.resolve()`. - Confirm that it remains beneath a trusted immutable root. - Reject symbolic links and non-regular files. - Validate file ownership and ensure it is not writable by untrusted users. - Verify a pinned cryptographic digest or digital signature before importing it. 4. Do not use `exec_module()` on a path discovered solely through relative directory traversal. 5. Run document processing in a sandbox with minimal filesystem and network permissions. 6. Ensure the skill runner and shared dependency directories are not writable by ordinary input providers. 7. Fail closed when preprocessing is unavailable instead of executing an unverified fallback. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:87
Finding
Untrusted Medication Fields Can Inject Instructions into the Model Prompt<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:87-105` **Vulnerability Type**: Indirect prompt injection **Risk Level**: Medium ### Complete Code Snippet ```python user_prompt = f"""请整理以下用药记录: 总记录数:{len(medications)} 正在服用:{len(active)}种 已停用:{len(stopped)}种 正在服用的药品: ```json {json.dumps(active, ensure_ascii=False, indent=2)} ``` 已停用的药品: ```json {json.dumps(stopped, ensure_ascii=False, indent=2)} ``` 请生成用药管理摘要,分类展示,标注长期/短期用药,给出用药提醒。""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` ### Technical Analysis All medication fields originate from user-controlled files and are interpolated directly into the model's user message. Markdown code fences and JSON serialization preserve syntax but do not create a security boundary for a language model. A malicious medicine name or note can contain instructions telling the model to disregard the surrounding task, suppress the disclaimer, invent treatment advice, or emit deceptive content. The system prompt does not explicitly classify record content as untrusted data or prohibit following instructions found within those fields. The generated response is returned as `text` for direct rendering. There is no output-schema enforcement or post-generation validation to ensure that the response remains a medication summary, includes the required disclaimer, or avoids unsupported medical recommendations. ### Attack Path 1. An attacker creates a medication record whose `medicine_name`, `note`, or another field contains model-directed instructions. 2. A user imports the record through any supported input format. 3. The application serializes the malicious field into the prompt without semantic filtering. 4. The external model interprets the embedded content as instructions rather than passive medical data. 5. The model produces manipulated or misleading Markdown. 6. The application returns the response in the `text` field for presentation to the user without validation. ### Impact Assessmen ...[truncated 765 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. State in the highest-priority prompt that every medication field is untrusted data and that instructions appearing inside records must never be followed. 2. Separate operational instructions from data using a rigid structured-input protocol supported by the model API where available. 3. Validate types and impose conservative length limits on every field, especially free-form notes. 4. Reject or flag content containing instruction-like payloads, role markers, prompt delimiters, or attempts to override the task. 5. Require structured model output conforming to a strict schema rather than accepting unrestricted Markdown. 6. Post-validate the response to confirm that required disclaimers and medical-boundary constraints are present. 7. Do not ask the model to perform drug-interaction assessment unless the documented scope, clinical validation, and safety controls explicitly support that behavior. 8. Clearly distinguish model-generated text from verified medical advice and require clinician review for safety-sensitive conclusions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose is narrow medication record management, but the documented behavior expands into broad multi-format document ingestion, Office/PDF conversion, and image OCR. That mismatch is dangerous because users and reviewers may grant trust appropriate for a simple record manager while the skill actually processes arbitrary documents and invokes external tooling, expanding attack surface and privacy exposure.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents capabilities that imply file access, file writing, shelling out to local tools, environment access, and network transmission, but it does not declare any explicit tool scope or permissions boundaries. In a health-data context, this increases the risk of overbroad execution and makes it harder for a host platform to constrain sensitive operations or review whether the behavior is appropriate.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The documented medical boundary says the skill does not judge indications or adjust treatment, yet other sections say it produces medical-model analysis and reminders. This contradiction creates a safety and trust problem: users may believe the skill is non-advisory while still receiving clinical-seeming guidance from a model.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill claims to only manage personal medication records, but the output specification includes LLM-generated medical interpretation, analysis, and reminders. In a medical context, that functional drift can mislead users into relying on unvalidated health guidance and also implies transmission of sensitive data beyond basic record storage.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
Requiring external medical-model API inference for a basic medication record management task introduces unnecessary network dependency and sensitive-data exposure. Because medication histories are health data, sending them to a remote endpoint without strong necessity increases confidentiality and compliance risk even if the endpoint is internal.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill states that medication records are sent to an internal medical model API but provides no user-facing warning or consent flow for transmitting sensitive health information. In the healthcare context, undisclosed sharing of medication histories can expose highly sensitive personal data and create substantial privacy, regulatory, and trust risks.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The skill manifest describes a patient-side long-term medication record management capability, but this module's docstring states it is a 'general file preprocessing tool' for extracting text or tables from PDFs, Office files, JSON, and images. The code throughout the file focuses on content extraction and format conversion rather than medication-list or dose-history management, indicating a semantic mismatch with the declared skill purpose.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The module adds external office, PDF, and OCR processing capabilities that are not clearly justified by the medication-record management purpose, substantially increasing attack surface. In this skill context, that mismatch makes the behavior more suspicious and more dangerous because it processes complex untrusted formats through third-party binaries without visible isolation controls.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if not office_bin:
        raise PreprocessError("libreoffice/soffice not found for office document conversion.")
    with tempfile.TemporaryDirectory(prefix="med-skill-preprocess-") as tmp_dir:
        proc = subprocess.run(
            [office_bin, "--headless", "--convert-to", "txt:Text", "--outdir", tmp_dir, str(path)],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False,
        )
Confidence
77% confidence
Finding
Although the subprocess call avoids shell injection by passing arguments as a list, it invokes a large external document-conversion suite on potentially untrusted office files. Parsing attacker-supplied documents with LibreOffice/soffice meaningfully expands attack surface and can enable denial of service or exploitation of converter vulnerabilities, especially in a server-side preprocessing pipeline.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if not office_bin:
        raise PreprocessError("libreoffice/soffice not found for xls conversion.")
    with tempfile.TemporaryDirectory(prefix="med-skill-preprocess-") as tmp_dir:
        proc = subprocess.run(
            [office_bin, "--headless",
             "--convert-to", "csv:Text - txt - csv (StarCalc):44,34,76,1",
             "--outdir", tmp_dir, str(path)],
Confidence
78% confidence
Finding
This path uses LibreOffice to convert XLS files supplied by users, which exposes the system to a complex external parser. Even without shell injection, feeding untrusted spreadsheet files to a heavyweight conversion tool can trigger parser bugs, excessive resource consumption, or other unsafe behavior in the host environment.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
pass
    pdf_to_text = shutil_which("pdftotext")
    if pdf_to_text:
        proc = subprocess.run(
            [pdf_to_text, "-layout", str(path), "-"],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False,
        )
Confidence
72% confidence
Finding
This invokes pdftotext on untrusted PDFs, which introduces risk from external parser vulnerabilities and resource-exhaustion attacks. The subprocess usage itself is not an injection issue, but server-side processing of attacker-controlled PDFs via third-party binaries is a legitimate security concern in a generic ingestion utility.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The code forces Tesseract to use "chi_sim+eng" whenever those language packs are installed, rather than letting the user choose or documenting a justified locale constraint. This is a natural-language locale policy issue because it imposes a language preference automatically for image OCR.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd = [tesseract_bin, str(path), "stdout"]
    if lang_arg:
        cmd.extend(["-l", lang_arg])
    proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
    if proc.returncode != 0 or not proc.stdout.strip():
        raise PreprocessError(f"Image OCR failed: {proc.stderr.strip() or 'no text returned'}")
    return proc.stdout
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def detect_tesseract_langs(tesseract_bin: str) -> Sequence[str]:
    proc = subprocess.run(
        [tesseract_bin, "--list-langs"],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False,
    )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The module docstring and the embedded prompts are written to operate in Chinese only, and the system prompt mandates Chinese output formatting and disclaimer text. There is no user opt-in or configurable language choice, which is a language/locale policy concern under the stated rules.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The code sends a patient's full medication records, including drug names, doses, dates, status, and notes, to an external LLM API for processing. In a medical-record-management skill, this is sensitive health data exposure to a third party, and the code contains no consent flow, minimization, anonymization, or disclosure about off-device transfer, making privacy and compliance risk substantial.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
Sensitive medication data is transmitted to an external API endpoint, but the code offers no user-facing notice that personal health information leaves the local environment. In healthcare contexts, lack of transparent disclosure and consent materially increases privacy, legal, and trust risks even if transport uses HTTPS.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The system prompt instructs the model to assess drug interaction risks, which is a clinical decision-support function beyond simple record management. Because the output is generated by a general LLM rather than a validated medication-interaction engine, it can produce incomplete or incorrect safety advice that may influence patient behavior in a high-risk medical context.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The natural-language system prompt instructs the model entirely in Chinese and requires a fixed Chinese disclaimer at the end. Because no alternative locale or language selection mechanism is provided, the skill enforces a specific language without explicit user choice.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The dependency note specifies Tesseract with `chi_sim+eng`, which constrains OCR language handling to simplified Chinese and English. The file does not offer users a language choice or explain this as an intentional region-specific limitation.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The script requires an `--appkey` credential and uses it to authorize remote API requests, but provides no warning or explanatory comment about how that credential is used. This is relevant because the rule calls for disclosure around access to sensitive credentials when no other user-facing explanation is present.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/run.py:217