Back to skill

Security audit

unisound-followup-reminder

Security checks for vulnerabilities and agentic risk

Overview

The skill broadly matches a patient follow-up reminder, but it handles sensitive medical files and sends patient data to a remote model without enough controls, and it can load unaudited code from outside the skill.

Review before installing in any real patient or regulated healthcare setting. Use only non-sensitive test data unless the remote model provider, retention terms, consent process, and data-minimization requirements are approved. Prefer JSON-only inputs, avoid free-form notes with identifiers, avoid --save-prepared for real records, and remove or verify the external _shared preprocessing fallback before deployment.

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)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:95
Finding

Prompt Injection Through Untrusted Patient Data

Content
View full analysis
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/run.py:221
Finding

Unverified Execution of a Python Module Outside the Audited Skill Directory

Content
View full analysis
Remediation
View remediation

other

Error
Location
scripts/run.py:30
Finding

External Disclosure of Patient Health Information Without Data Minimization

Content
View full analysis
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"""请为以下患者生成复诊提醒: 疾病类型:{disease_type} 上次就诊:{last_visit_date_str} 复诊日期:{followup_date.isoformat()} 当前日期:{today.isoformat()} 是否逾期:{'是,已逾期' + str(days_overdue) + '天' if is_overdue else '否,还有' + str(-days_overdue) + '天'} 备注:{note} 请分析风险并给出复诊建议和准备清单。""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` ### Technical Analysis The skill transmits disease type, visit dates, overdue status, and free-form notes to `https://maas-api.hivoice.cn/v1/chat/completions`. These values constitute health-related information, and the free-form note may additionally contain patient names, contact information, identifiers, medication details, diagnoses, or other sensitive records. The endpoint and mandatory API use are documented in `SKILL.md`, so this is not concealed network exfiltration. However, the implementation provides no consent gate, redaction, pseudonymization, field-level minimization, retention warning, or local-only execution mode. Even though HTTPS protects data in transit, it does not address disclosure to, storage by, or subsequent processing by the external service. ### Attack Path 1. A user supplies a patient record as J ...[truncated 1106 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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared purpose is a narrow follow-up reminder, but the documented behavior expands into general document ingestion, OCR, office conversion, and preprocessing. This mismatch is dangerous because it creates hidden attack surface and can cause users or reviewers to underestimate the amount of sensitive medical data being collected, transformed, and potentially exposed.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file implements a broad document ingestion and extraction utility that is not aligned with the stated patient follow-up reminder purpose. This capability expansion materially increases attack surface by adding parsing of PDFs, Office files, spreadsheets, JSON, and OCR, including execution of external converters, without clear necessity for the skill's mission.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code sends patient medical context to an external HTTP API without any visible user warning, consent flow, or policy guardrails. Because the transmitted fields can reveal health status and treatment history, this creates a meaningful confidentiality and compliance risk, especially in healthcare workflows.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises broad capabilities including environment access, file read/write, network access, and shell execution, but does not declare any explicit tool scope or permission boundaries. In a medical reminder context, this lack of least-privilege controls increases the chance that a simple reminder skill can access sensitive files, invoke external tools, or transmit data beyond what users would reasonably expect.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill accepts unrestricted documents and images, extracts their contents via OCR/parsers, and then generates user-facing output through a remote LLM API. In a patient follow-up setting, that means arbitrary uploaded medical content may be collected and transmitted off-process, greatly increasing confidentiality risk and enabling overcollection far beyond the minimum needed for reminder logic.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill explicitly states that API inference is mandatory and that the user's appkey is used to send content to a remote model endpoint, even though the business purpose is a simple reminder/overdue calculation that can be done locally. Forcing remote transmission of patient follow-up data without necessity increases privacy exposure and creates avoidable dependency on an external service for sensitive healthcare processing.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation notes transmission of medical input to an internal model API but does not prominently warn users about privacy, retention, sharing, or consent implications. In a healthcare context, insufficient disclosure can lead to unauthorized handling of sensitive patient information and undermines informed use of the skill.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
78% confidence
Finding

The module docstring is written entirely in Chinese and the OCR logic later prefers Chinese plus English when available, indicating a language-specific default without any visible user choice or justification in this file. Under the policy, forcing a specific language or locale without opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code invokes external office, PDF, and OCR executables even though the skill is described as a patient follow-up/reminder capability. In context, this mismatch makes the behavior more suspicious because it enables processing complex untrusted content and expands system interaction beyond what users or reviewers would reasonably expect from a reminder workflow.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The code silently performs subprocess-based conversion and OCR on user-supplied files without any visible disclosure, warning, or consent mechanism in this component. In a medical context, that can violate user expectations and complicate privacy, audit, and risk review because sensitive patient documents may be handed to external tools unexpectedly.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/preprocess.py (reported line 111)May include surrounding context.

python
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,
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/preprocess.py (reported line 126)May include surrounding context.

python
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)],

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/preprocess.py (reported line 153)May include surrounding context.

python
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,
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/preprocess.py (reported line 171)May include surrounding context.

python
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

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/preprocess.py (reported line 178)May include surrounding context.

python
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,
    )

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill transmits patient follow-up details, including disease type, visit dates, and notes, to an external LLM endpoint. In a medical context this is sensitive health information, and the manifest/code shown does not provide clear disclosure, consent handling, minimization, or contractual/privacy controls for third-party processing.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The system prompt instructs the model entirely in Chinese and requires markdown output in a specific Chinese-language tone, with no mechanism for user language selection. This is a natural-language locale constraint that appears mandatory rather than optional or region-justified.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

Accepting the API credential via a command-line argument can expose the secret through shell history, process listings, job logs, and orchestration metadata. In shared systems this can allow other users or services to recover the token and make unauthorized requests to the medical LLM backend.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The comment says this block falls back to shared document preprocessing, implying equivalent behavior after preprocessing succeeds. However, line L243 calls build(data, args.appkey) even though build requires (data, today, appkey), so the fallback path does not actually perform the documented reminder generation behavior and will instead error or misbehave.

Content

No source excerpt is available for this finding.