Back to skill

Security audit

unisound-rehab-plan-view

Security checks for vulnerabilities and agentic risk

Overview

This skill is a rehab-plan viewer, but it processes sensitive medical files through a remote model and includes an unsafe fallback that can execute Python code from outside the reviewed package.

Review before installing. Use only with explicit authorization to send rehabilitation data to the listed external API, avoid real patient identifiers where possible, do not pass production API keys on the command line, and avoid untrusted PDFs, Office files, or images unless the runtime is sandboxed. The external _shared/doc-preprocess fallback should be removed or integrity-checked before production use.

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)

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/run.py:185
Finding
Execution of an Unverified Python Module from an External Writable Path## Vulnerability Details **File Location**: `scripts/run.py:185-192` **Vulnerability Type**: Untrusted local module loading and execution **Risk Level**: High ### Vulnerable Code ```python _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) ``` ### Technical Analysis When local preprocessing raises `PreprocessError`, the application constructs a path outside the audited skill package and dynamically executes the `preprocess.py` file found there. For the audited deployment path, `Path(__file__).resolve().parent.parents[3]` resolves to `/tmp`, resulting in the expected module location: ```text /tmp/_shared/doc-preprocess/scripts/preprocess.py ``` The application does not verify the module's cryptographic digest, ownership, permissions, provenance, or containment beneath a trusted application directory. Calling `exec_module()` executes all module-level Python statements immediately. Consequently, any party able to create or replace the expected fallback file can cause arbitrary Python code to run. Exploitation requires local write access to the relevant external directory and an input that causes the primary preprocessor to raise `PreprocessError`. ### Attack Path 1. An attacker with local write access creates or replaces `/tmp/_shared/doc-preprocess/scripts/preprocess.py`. 2. The attacker places arbitrary module-level Python code in that file. 3. The attacker or a victim invokes the skill with an input that causes the bundled preprocessing implementation to raise `PreprocessError`, such as an unsupported or unprocessable document. 4. The exception ...[truncated 838 chars]
Remediation
## Remediation Suggestions - Remove the external dynamic-import fallback and use only the preprocessor shipped inside the reviewed package. - If shared preprocessing is required, package it as a pinned and audited dependency installed in a protected application environment. - Resolve the dependency beneath an immutable, administrator-controlled application root rather than a temporary or broadly writable directory. - Before loading any fallback module, verify its canonical path, owner, permissions, and cryptographic digest against trusted metadata. - Reject symbolic links and ensure every parent directory is not writable by untrusted users. - Avoid `exec_module()` for runtime discovery of source files. Import a statically declared package through a controlled Python environment instead. - Run the skill under a dedicated least-privileged operating-system account and restrict its filesystem and network access to reduce impact if module loading is compromised.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:172
Finding
API Credential Exposed Through a Command-Line Argument## Vulnerability Details **File Location**: `scripts/run.py:172`; documented at `SKILL.md:68` and `SKILL.md:157` **Vulnerability Type**: Insecure secret handling **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument("--appkey", required=True, help="内部医疗大模型鉴权key(必填)") ``` The documented invocation explicitly places the credential on the command line: ```bash python3 scripts/run.py --input input.json --output output.json --appkey YOUR_KEY ``` ### Technical Analysis Passing bearer credentials as command-line arguments exposes them beyond the intended application boundary. Depending on the operating system and deployment environment, process arguments may be available through process inspection facilities, monitoring agents, job metadata, audit logs, crash diagnostics, or orchestration dashboards. Interactive use can also retain the complete command in shell history. Although the application does not print the credential directly, the documented secret-delivery mechanism creates avoidable credential exposure. The key is subsequently used as a bearer token, so possession of the value may be sufficient to authenticate to the configured model endpoint. ### Attack Path 1. A user follows the documented command and provides a real API key through `--appkey`. 2. The full command is recorded in shell history, process telemetry, orchestration logs, or another process-visible argument listing. 3. A local user, system operator, log reader, or compromised monitoring component retrieves the argument value. 4. The exposed bearer credential is replayed against the configured medical-model API. 5. Unauthorized requests can be made until the key expires or is revoked. ### Impact Assessment The attacker may gain the API access associated with the exposed key. Potential effects include unauthorized model usage, consumption of quotas, financial charges, service disruption through quota exhaustion, and actions att ...[truncated 196 chars]
Remediation
## Remediation Suggestions - Deprecate and remove the `--appkey` argument. - Retrieve the credential from an approved secret manager or a protected environment variable. - For interactive execution, support a non-echoing secret prompt through `getpass`. - Where supported, accept the credential through a restricted file descriptor or a credential file with strict owner-only permissions. - Update `SKILL.md` so examples never place real secrets in command-line arguments. - Ensure orchestration logs, errors, telemetry, and crash reports redact authorization values. - Use short-lived, narrowly scoped credentials and implement rotation and immediate revocation procedures for exposed keys.

other

Warning
Location
scripts/run.py:76
Finding
Patient Health Information Transmitted to an External API Without Data-Minimization Controls## Vulnerability Details **File Location**: `scripts/run.py:76-91` and `scripts/run.py:21-31` **Vulnerability Type**: Sensitive health data disclosure **Risk Level**: Medium ### Vulnerable Code The complete mapped rehabilitation record is embedded in the model prompt: ```python user_prompt = f"""请解读以下康复计划: 计划ID:{plan_id} 手术类型:{surgery_type} 当前阶段:{current_phase} 阶段目标:{phase_goal} 康复任务:{json.dumps(tasks, ensure_ascii=False)} 注意事项:{json.dumps(precautions, ensure_ascii=False)} 请生成患者友好的康复计划解读。""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` The prompt is transmitted to the configured external endpoint: ```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"] ``` ### Technical Analysis The skill transmits the plan identifier, surgery type, rehabilitation phase, treatment goals, assigned tasks, and precautions to `maas-api.hivoice.cn`. These fields can contain health information and may become identifying when combined with a patient-specific plan identifier or free-form task and precaution content. The network behavior is documented and uses HTTPS, so it is not hidden exfiltration. However, the implementation does not enforce explicit consent, remove identifiers, minimize free-form content, warn before transmission, provide a local-processing option, or expose controls concerning ...[truncated 1703 chars]
Remediation
## Remediation Suggestions - Require explicit, informed authorization before transmitting patient information to the external processor. - Clearly disclose the destination service, categories of data transmitted, processing purpose, retention period, and applicable privacy terms. - Remove `plan_id` from the prompt unless it is demonstrably necessary for model inference. - Pseudonymize or redact direct and linkable identifiers before request construction. - Apply field-level data minimization and limit free-form tasks and precautions to information required for the requested summary. - Add configurable redaction rules for names, patient numbers, contact details, dates, and other identifying information. - Provide a local or non-network rendering mode for users who cannot authorize third-party processing. - Establish an approved data-processing agreement with the API provider and verify encryption, access control, retention, deletion, audit, and incident-response requirements. - Avoid logging request bodies and ensure patient data is removed from diagnostics and error telemetry.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The stated purpose is a narrow patient rehab-plan viewing skill, but the specification also enables broad document ingestion, OCR, Office/PDF conversion, and generic preprocessing pipelines. That mismatch is dangerous because it expands the attack surface well beyond a simple viewer, introducing parser, converter, and prompt-injection-style risks from untrusted files without making those risks obvious to users or reviewers.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises executable behaviors such as file access, shelling out to external tools, network calls, and possible environment access, but does not declare any tool scope or permission boundaries. In a medical-data workflow, this lack of explicit capability restriction increases the risk of over-privileged execution, unintended file/system access, and unreviewed outbound data transfer.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill accepts broad medical document/image inputs and sends content to a remote model endpoint, but does not clearly warn users that potentially sensitive health information may be transmitted off-box for inference. In a healthcare context, this creates significant privacy and compliance risk, especially when OCR and document parsing may extract more PHI than the user expects.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
The file specifies Chinese OCR support via "chi_sim+eng" and uses exclusively Chinese-facing descriptions and field examples, but it does not state that the skill is intentionally limited to a China-specific workflow or offer language/locale selection. Under the policy, forcing a specific language or locale without user opt-in is a natural-language policy concern.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The file implements a broad ingestion utility for many document and image formats that exceeds the stated scope of a patient rehab-plan viewing skill. This unnecessary capability expansion increases attack surface by introducing multiple parsers and conversion paths that may be reachable through skill workflows, especially in a medical context that may handle sensitive patient documents.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code invokes external conversion and OCR binaries (LibreOffice, pdftotext, tesseract) on input files, significantly widening the trusted computing base. In a patient-facing rehab-plan skill, this is more dangerous because the feature appears broader than necessary and may expose the system to third-party parser exploits, sandbox escapes, or denial-of-service through crafted files.

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
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
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
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
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
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
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.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill's stated function is to let patients view or interpret a rehab plan, yet it depends on a remote model endpoint and separate authentication key. That expands the trust boundary and introduces unnecessary exposure, since sensitive medical data and credentials now flow to an external service not implied by the feature description.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The code sends the full rehabilitation-plan content, including medical details such as surgery type, phase, tasks, and precautions, to an external LLM API for interpretation. In a medical patient-facing context this is sensitive health information, so undisclosed third-party transmission materially increases privacy, confidentiality, and compliance risk.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The system prompt mandates Chinese behavior and Markdown output in Chinese-context phrasing, with no indication that the user may choose another language. This is a natural-language locale policy concern because the skill enforces a specific language without documented opt-in or justification.

Ssd 1

Medium
Confidence
95% confidence
Finding
Untrusted input fields from the rehab plan are interpolated directly into the LLM prompt, allowing an attacker controlling plan content to inject instructions that compete with the system prompt. In this implementation the model output is returned to the patient, so the main risk is manipulated or unsafe medical guidance rather than code execution, but in a healthcare context misleading recovery instructions can still cause harm.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The build path constructs a prompt from patient rehab-plan fields and sends it to the external API without any visible user-facing notice, consent, or privacy warning. In healthcare, silent disclosure of treatment-related data to a third party can violate user expectations and regulatory requirements, making the context more dangerous than a generic content-summarization tool.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The code sets Tesseract to use "chi_sim+eng" whenever those models are available, which imposes a specific language policy in natural-language processing behavior. There is no user choice, configuration input, or documented region-specific justification in this file.

Missing User Warnings

Low
Confidence
88% confidence
Finding
Accepting the API credential via a command-line argument can expose it through shell history, process listings, job logs, and orchestration metadata. While this is not as severe as direct patient-data leakage, it can enable unauthorized use of the external medical-model service if the host environment is shared or monitored.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

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