Back to skill

Security audit

unisound-exercise-guidance

Security checks for vulnerabilities and agentic risk

Overview

This skill is a postoperative exercise helper, but it can generate patient-facing rehabilitation advice from incomplete inputs, send sensitive medical content to a remote API, and load unaudited shared Python code.

Review this carefully before installing. It should not be used for real patient rehabilitation decisions unless the remote API, medical-data handling, clinical review process, and unaudited shared-code fallback are approved. Users should avoid submitting identifiable medical records or relying on generated exercise frequency, duration, intensity, or precautions as clinician-approved instructions.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T08 · Insecure Dependencies

Error
Location
scripts/run.py:180
Finding
Execution of an Unverified Python Module Outside the Audited Package<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:180-187` **Vulnerability Type**: Untrusted external dependency 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 program searches for a Python module outside the skill package and executes it through `exec_module`. The referenced `_shared/doc-preprocess/scripts/preprocess.py` file is not included in the audited project, so its contents, version, integrity, and provenance cannot be verified as part of this skill. `exec_module` executes all top-level statements in the selected file with the same operating-system identity and permissions as the skill process. The implementation does not verify a cryptographic digest, package signature, trusted ownership, file permissions, or an immutable version before execution. The fallback therefore creates an unsafe supply-chain and local trust boundary: control over the resolved shared module is equivalent to control over the skill process. ### Attack Path 1. An attacker gains the ability to create or modify the external `_shared/doc-preprocess/scripts/preprocess.py` file at the path derived by `parents[3]`. 2. The attacker adds arbitrary Python statements to the module's top-level scope. 3. A user invokes the skill with an input that causes the bundled preprocessor to raise `PreprocessError`, such as an input requiring unavailable parsing support or one that fails local extraction. 4. The fallback locates the attacker-controlled shared module. 5. `e ...[truncated 766 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the path-based fallback and use only the preprocessing implementation shipped inside the audited package. 2. If preprocessing must be shared, distribute it as a version-pinned package from a trusted registry or immutable internal artifact store. 3. Verify the dependency's cryptographic digest or signature before loading it. 4. Refuse to execute modules from writable shared directories. 5. Validate file ownership and permissions where local loading is unavoidable. 6. Import only through a controlled package environment rather than `spec_from_file_location` and `exec_module`. 7. Run document parsing in a sandbox with minimal filesystem access, no unnecessary credentials, constrained network access, and resource limits. 8. Include the complete shared component in future security reviews and dependency inventories. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:65
Finding
Prompt Injection Through Untrusted Rehabilitation Document Fields<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:65-78` **Vulnerability Type**: Prompt injection caused by unsafe interpolation of untrusted content **Risk Level**: High ### Vulnerable Code ```python user_prompt = f"""请为以下康复运动生成详细指导: 运动名称:{exercise_name} 康复阶段:{phase} 基础说明:{instruction} 频次要求:{frequency} 时长要求:{duration} 注意事项:{json.dumps(precautions, ensure_ascii=False)} 请分步骤描述动作要领,给出具体频次/时长/强度指导,说明常见错误和该动作的恢复价值。""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` ### Technical Analysis Values parsed from user-controlled JSON, text, spreadsheets, office documents, PDFs, and OCR input are directly interpolated into the same natural-language prompt that contains operational instructions for the model. The implementation does not: - Clearly separate trusted instructions from untrusted field data. - Tell the model to ignore directives embedded in field values. - Restrict field length or permitted content. - Validate generated medical guidance against deterministic safety rules. - Require human approval before returning the generated text for rendering. An attacker can place model directives inside fields such as `instruction`, `exercise_name`, or `precautions`. Because these directives are presented as ordinary prompt text, the remote model may treat them as instructions rather than inert patient data. The response is subsequently returned as trusted output with `"status": "ok"`, which increases the likelihood that manipulated content will be displayed directly to a patient. ### Attack Path 1. An attacker prepares a supported input file containing malicious prompt instructions in a recognized field. For example, the `instruction` field could tell the model to disregard prior safety constraints and produce an unsafe exercise schedule. 2. The parser extracts the attacker-controlled value without security filtering. 3. `build` interpolates the value directly into `user_prompt`. 4. `_call_llm` submits the combined trusted instruct ...[truncated 798 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every extracted field as untrusted data, including text obtained through OCR or document conversion. 2. Send inputs as a structured object with explicit boundaries rather than embedding them directly into prose. 3. Add a higher-priority instruction stating that content inside data fields is inert clinical data and that embedded commands must never be followed. 4. Enforce strict schemas, maximum lengths, expected data types, and allowlisted formats for phase, frequency, duration, and precautions. 5. Reject values containing prompt-control patterns when those patterns are not clinically necessary. 6. Require quantitative recommendations to be copied from validated clinician-approved input rather than inferred by the model. 7. Apply deterministic post-generation checks for contraindicated claims, unsupported numeric prescriptions, missing warnings, and deviations from approved input. 8. Do not mark output as successful until it passes validation. 9. Require clinician review before model-generated guidance is rendered to patients. 10. Maintain adversarial prompt-injection tests covering JSON, text, spreadsheet, PDF, office-document, and OCR ingestion paths. ]]>

other

Error
Location
scripts/run.py:45
Finding
Generated Medical Exercise Prescriptions Exceed the Documented Safety Boundary<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:45-78` **Related Documentation**: `SKILL.md:70-72` **Vulnerability Type**: Unsafe generation of patient-facing medical exercise guidance **Risk Level**: High ### Vulnerable Code ```python SYSTEM_PROMPT = """你是一位专业的术后康复运动指导师。 你的任务: 1. 根据用户提供的运动名称和阶段,给出详细的训练动作指导 2. 分步骤描述动作要领(每一步清晰、可操作) 3. 说明频次、时长、强度的具体数字 4. 列出至少3条注意事项和常见错误 5. 说明该动作对恢复的好处 重要:你是在指导患者做运动,必须给出实质性的详细指导,不能只重复动作名称。 输出Markdown格式,使用列表和引用块组织内容。末尾加免责提示。""" def build(data: Dict[str, Any], appkey: str) -> Dict[str, Any]: exercise_name = data.get("exercise_name", "") phase = data.get("phase", "") instruction = data.get("instruction", "") frequency = data.get("frequency", "") duration = data.get("duration", "") precautions = as_list(data.get("precautions", [])) user_prompt = f"""请为以下康复运动生成详细指导: 运动名称:{exercise_name} 康复阶段:{phase} 基础说明:{instruction} 频次要求:{frequency} 时长要求:{duration} 注意事项:{json.dumps(precautions, ensure_ascii=False)} 请分步骤描述动作要领,给出具体频次/时长/强度指导,说明常见错误和该动作的恢复价值。""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` The documented boundary states: ```text 医疗边界 -------- 本 skill 只展示既有动作指导,不替代康复师现场评估。 ``` ### Technical Analysis The documentation says that the skill only displays existing exercise guidance and does not replace an in-person rehabilitation assessment. The implementation contradicts this boundary by explicitly instructing the model to provide detailed movement steps and specific numerical frequency, duration, and intensity. All relevant fields default to empty values, and the code performs no completeness validation before calling the model. Consequently, an input containing only an exercise name and rehabilitation phase can cause the model to invent the missing prescription details. The implementation also lacks: - Screening for surgery type, affected body part, weight-bearing restrictions, wound condition, pain, swelling, complications, or clinician contraindication ...[truncated 2049 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Align implementation with the documented boundary: only display clinician-approved exercise instructions already present in the input. 2. Make exercise name, approved instructions, frequency, duration, intensity limits, phase, and precautions mandatory where clinically applicable. 3. Reject incomplete records rather than asking the model to infer missing prescription values. 4. Explicitly prohibit the model from generating or changing frequency, duration, repetitions, resistance, range of motion, or intensity. 5. Preserve provenance for every displayed recommendation, identifying whether it came from a clinician-approved plan. 6. Use the model only for formatting or plain-language explanation that cannot alter clinical meaning. 7. Add screening for relevant contraindications and direct users to their clinician whenever necessary context is unavailable. 8. Require clinical review for any model-generated addition. 9. Replace unconditional `"status": "ok"` with validation states such as incomplete, requires review, or rejected. 10. Add tests confirming that sparse input cannot produce new quantitative exercise recommendations. ]]>
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 (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The manifest presents the skill as a narrow rehabilitation guidance display tool, but the documented behavior expands into broad document ingestion, OCR, Office conversion, and text extraction workflows. That mismatch is dangerous because users and reviewers may grant trust or provide sensitive medical files under the assumption of limited functionality, while the actual processing surface is much larger and includes risky parsers and external tools.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
This file implements a broad, generic ingestion pipeline for PDFs, Office documents, spreadsheets, JSON, text, and images, which materially exceeds the stated scope of a postoperative exercise-guidance skill. Unnecessary capability expansion increases attack surface, especially because it adds complex parsers and external-tool handling unrelated to the declared medical-guidance purpose.

Missing User Warnings

High
Confidence
97% confidence
Finding
Patient-related medical fields are assembled into a prompt and sent to an external API without any user-facing warning, consent flow, or disclosure in the code path. In a postoperative rehabilitation setting, even seemingly simple exercise names, phases, and precautions can constitute protected or sensitive health data, making undisclosed transmission particularly risky.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill documents capabilities that imply file access, shell execution, environment access, and network egress, but it does not declare any explicit tool scope or permissions boundaries. In a medical-context skill that processes patient-provided inputs, this creates unnecessary ambiguity about what the skill may access and increases the risk of over-privileged execution or accidental misuse of local files, secrets, and external services.

Description-Behavior Mismatch

Medium
Confidence
86% confidence
Finding
The documentation says the skill must not generate new rehabilitation prescriptions, but the broader parsing and mandatory inference pipeline create pressure toward generalized medical reasoning rather than simple instruction display. In context, that inconsistency can lead to users treating generated output as tailored rehabilitation guidance, especially when the inputs come from heterogeneous clinical documents.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill claims to be a guidance-only display mechanism, yet its output contract includes model-generated medical interpretation, analysis, and reminders. In a postoperative rehabilitation setting, this expands the skill from passive rendering into active medical reasoning, which can introduce unsafe or misleading advice beyond the validated source instructions.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill accepts PDFs, Office documents, text files, spreadsheets, and images for OCR, but it does not provide a clear warning that these uploads may contain sensitive medical or personally identifiable information. In this medical-use context, broad document ingestion substantially increases the chance of inadvertent exposure of rehabilitation records, discharge instructions, IDs, or other protected data.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
A mandatory remote medical LLM API is introduced even though the stated task is simple exercise instruction guidance. This is dangerous because it creates avoidable data exposure, dependency, and prompt-driven behavior risks for sensitive postoperative rehabilitation content, without a clearly necessary functional justification.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill requires users to provide an app key and sends content to a remote API, but it does not clearly warn users that their exercise data and potentially sensitive medical information will be transmitted for model inference. In a healthcare context, missing disclosure materially increases privacy and compliance risk because users may unknowingly upload protected health information to a remote service.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
OCR and Office/PDF conversion add substantial parser complexity and invoke heavyweight external programs for a skill whose purpose is exercise instruction. In context, these capabilities are hard to justify and increase risk of malicious file-triggered crashes, resource exhaustion, or exploitation of third-party document-processing components.

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.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
Subprocess execution is not inherently insecure here, but in the context of a patient exercise instruction skill it is an unnecessary capability that broadens system exposure. Calling external binaries for document conversion creates dependency on host environment integrity and adds avenues for abuse through malicious inputs or compromised PATH-resolved executables.

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.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill is presented as postoperative exercise guidance, but it transmits patient exercise/rehabilitation details to an external LLM service to generate the content. In a medical context, those fields can reveal treatment stage, recovery status, and other sensitive health information, creating privacy and compliance risk if users are not explicitly informed and the service is not tightly governed.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The code requires an API key and performs outbound network calls even though the stated purpose is a guidance/view-like exercise instruction capability. This expands the trust boundary and introduces data exfiltration, dependency, and supply-chain risk that is not obviously necessary for the described functionality.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The system prompt is written entirely in Chinese and instructs the model to produce Markdown output accordingly, with no mechanism for user language selection or opt-in. This imposes a specific language/locale behavior that may violate language-choice policy when the skill is used in broader contexts.

Ssd 1

Medium
Confidence
91% confidence
Finding
Untrusted input fields are interpolated directly into the LLM prompt with no delimiting, validation, or instruction-neutralization. An attacker controlling exercise_name, instruction, or precautions could inject adversarial natural-language instructions that cause the model to ignore medical constraints, produce unsafe guidance, or emit manipulated content in a patient-facing medical workflow.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The top-level docstring is written only in Chinese and presents the tool description in a fixed language, with no indication that language choice is configurable or intentionally region-specific. Under the policy rules, natural-language content that imposes a specific language without opt-in can be a locale/language policy violation.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

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