Back to skill

Security audit

unisound-function-self-assessment

Security checks for vulnerabilities and agentic risk

Overview

This skill is not overtly malicious, but it needs review because it sends patient assessment data to a fixed external medical-model API and can load preprocessing code from outside the package.

Review this before installing in any real patient or clinical workflow. Use it only if the fixed hivoice.cn model endpoint is approved for the health data being processed, users explicitly consent to sending assessment contents externally, and the external processor's retention and compliance terms are acceptable. Prefer JSON input with minimal identifiers, avoid uploading sensitive documents unless necessary, and ensure any _shared preprocessing directory is trusted and not writable by unprivileged users.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:75
Finding
Prompt Injection Through Untrusted Assessment Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py`, lines 75-95 **Vulnerability Type**: Prompt injection caused by unsafe incorporation of user-controlled data into an LLM prompt **Risk Level**: High ### Vulnerable Code ```python answer_summary = [] total = 0.0 for q in questions: qid = q.get("id", "") qtext = q.get("text", "") ans = answers.get(qid, 0) score = to_float(ans) or 0.0 total += score answer_summary.append({"id": qid, "question": qtext, "answer": ans, "score": score}) user_prompt = f"""Please interpret the following functional self-assessment results: Assessment ID: {assessment_id} Assessment time: {assessed_at} Number of questions: {len(questions)} Total score: {total} (maximum {len(questions)*5}, 1-5 points per question) Scores by question: ```json {json.dumps(answer_summary, ensure_ascii=False, indent=2)} ``` Please interpret the score for each dimension, assess the overall recovery stage, and provide recommendations.""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` ### Technical Analysis The values used to construct `answer_summary`, including question identifiers, question text, and answers, originate from the input assessment. They are directly interpolated into the downstream language-model prompt without being treated as untrusted content. Placing the content inside a JSON-formatted Markdown block does not establish a security boundary for a language model. A malicious question or answer can contain instructions asking the model to disregard its system role, suppress medical disclaimers, fabricate a diagnosis, or provide unsafe recommendations. The generated response is accepted without policy validation and returned directly as patient-facing `text`. This is especially significant because the skill operates in a medical context and its documented boundary states that it should not perform diagnosis or replace professional assessment. ### Attack Path 1. An attacker or untrust ...[truncated 1014 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define and enforce a strict schema for questions and answers, including data types, allowed lengths, identifier syntax, and permitted score ranges. 2. Explicitly state in the system prompt that all assessment fields are untrusted data and must never be interpreted as instructions. 3. Submit assessment data through a structured interface or tool/function schema where supported, rather than concatenating it into an instruction-bearing prompt. 4. Separate trusted instructions from untrusted content using clearly identified fields, while recognizing that delimiters alone are not a complete defense. 5. Reject or flag question content containing model-directed instructions when such content is not legitimate for the assessment. 6. Validate the generated response before displaying it. Enforce mandatory disclaimers and reject diagnostic claims or recommendations outside the skill's intended scope. 7. Prefer deterministic local generation for score summaries where model inference is not necessary. 8. Add adversarial tests covering instructions embedded in question text, answers, assessment identifiers, and dates. ]]>

other

Warning
Location
scripts/run.py:21
Finding
Mandatory External Disclosure of Patient Assessment Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py`, lines 21-31 and 75-95 **Vulnerability Type**: Sensitive health-data disclosure to an external model service **Risk Level**: Medium ### Vulnerable Code ```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"] except HTTPError as exc: raise RuntimeError(f"API HTTP {exc.code}") except URLError as exc: raise RuntimeError(f"API unreachable: {exc.reason}") ``` The transmitted prompt is populated with assessment data: ```python user_prompt = f"""Please interpret the following functional self-assessment results: Assessment ID: {assessment_id} Assessment time: {assessed_at} Number of questions: {len(questions)} Total score: {total} (maximum {len(questions)*5}, 1-5 points per question) Scores by question: ```json {json.dumps(answer_summary, ensure_ascii=False, indent=2)} ``` Please interpret the score for each dimension, assess the overall recovery stage, and provide recommendations.""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` ### Technical Analysis The skill transmits the assessment identifier, assessment date, question text, patient answers, and calculated scores to `maas-api.hivoice.cn`. These fields may constitute sensitive health information and may be identifying when combined. The endpoint and mandatory API inference are disclosed in `SKILL.md`, so this behav ...[truncated 1613 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Obtain explicit, informed consent before transmitting assessment data to an external model service. 2. Remove assessment identifiers and exact dates from the prompt unless they are strictly necessary. 3. Pseudonymize records and minimize question and answer content before transmission. 4. Provide a local-only mode for score calculation and basic summaries. 5. Permit only administrator-approved model endpoints and enforce endpoint allowlisting. 6. Document the external processor's retention, deletion, geographic processing, and secondary-use policies. 7. Establish appropriate data-processing agreements and verify compliance with applicable health-data requirements. 8. Display a clear runtime warning identifying the destination and categories of data that will be sent. 9. Avoid persisting prepared patient data by default, and protect any output or debugging files with appropriate filesystem permissions. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/run.py:194
Finding
Dynamic Execution of an Unverified External Preprocessing Module<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py`, lines 194-205 **Vulnerability Type**: Unverified external dependency loading and arbitrary Python code execution **Risk Level**: Medium ### Vulnerable Code ```python except PreprocessError as exc: # Fall back to _shared/doc-preprocess try: _shared_dir = Path(__file__).resolve().parent.parents[3] / "_shared" / "doc-preprocess" / "scripts" if not _shared_dir.exists(): print(f"ERROR: Unable to read input file; local preprocessing failed and _shared/doc-preprocess is unavailable. Reason: {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 skill locates a `preprocess.py` file outside the audited project and executes it using `exec_module()`. Python module loading executes all top-level statements in the target file with the privileges of the current skill process. The external component is not included in this project, is not version-pinned, and is not checked against a cryptographic hash or signature. Consequently, the effective behavior of the skill depends on code that was outside the audit scope. If an attacker can write to the shared directory or influence how the project is deployed, the fallback becomes a local arbitrary-code execution path. The issue requires prior ability to place or modify the shared module, so it is not independently exploitable by assessment input alone. A malformed or unsupported input can then serve as the trigger for executing the compromised dependency. ### Attack Path 1. An attacker gains write access to the expected `_shared/doc-preprocess/scripts` directory or co ...[truncated 1090 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the dynamic fallback and package all required preprocessing code within the reviewed skill or a trusted installed package. 2. Pin the dependency to an exact reviewed version and verify its integrity during installation and execution. 3. If external loading is unavoidable, validate a cryptographic hash or trusted signature before calling `exec_module()`. 4. Require the shared directory and module to be owned by a trusted administrator and non-writable by the skill account or other unprivileged users. 5. Resolve the module through a controlled configuration rather than relying on a relative ancestor path. 6. Fail closed when dependency integrity cannot be established. 7. Run document preprocessing in a sandbox with minimal filesystem access, no unnecessary network access, resource limits, and a dedicated low-privilege account. 8. Record and audit the exact dependency version and digest used for each deployment. ]]>
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 (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill presents itself as questionnaire construction/assessment, but it also sends assessment content to an external network endpoint and requires an API key, which is a materially different behavior from local form handling. In a medical context, this is especially dangerous because patient responses may contain sensitive health information that is transmitted off-system without clear up-front disclosure and consent.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill presents itself as questionnaire construction/assessment, but it also sends assessment content to an external network endpoint and requires an API key, which is a materially different behavior from local form handling. In a medical context, this is especially dangerous because patient responses may contain sensitive health information that is transmitted off-system without clear up-front disclosure and consent.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The file implements a broad local document ingestion and transformation utility that is materially unrelated to a patient self-assessment questionnaire skill. This mismatch increases the likelihood of hidden data collection, unnecessary attack surface, and processing of sensitive local files beyond user expectations, especially in a medical context where users may provide highly sensitive records.

Context-Inappropriate Capability

High
Confidence
94% confidence
Finding
The skill adds local document conversion and OCR capabilities that are not directly required for postoperative self-assessment, enabling ingestion of arbitrary user files through complex external parsers. In a healthcare-related skill, this broad capability is dangerous because it can process sensitive medical documents and substantially increases exposure to parser exploits, over-collection of data, and unexpected local file handling.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill advertises capabilities that imply file access, shell execution, environment access, and network use, but it does not declare any explicit tool scope or permissions boundary. This is dangerous because operators and users cannot accurately assess what the skill may access, and the combination of document parsing plus shell/network access increases the chance of unintended data exposure or command abuse.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The documentation states that assessment content is sent to an internal medical model API, but it does not provide an explicit warning about privacy, data sharing, or handling of potentially sensitive medical information. Because the skill is for postoperative patient self-assessment, the data is likely health-related, so silent transmission creates a meaningful confidentiality and compliance risk.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
User files are sent through external conversion and OCR tools without any evident user-facing warning, consent flow, or disclosure in the code. For a medical self-assessment skill, silent processing of potentially sensitive documents is risky because users may not realize their files are being transformed by third-party binaries with distinct privacy and security implications.

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
88% confidence
Finding
The skill’s stated purpose is narrow self-assessment interpretation, but the code hard-depends on a remote chat-completions API and model capability. This expands the data exposure surface and operational privileges beyond what is obviously required for simple questionnaire scoring, increasing the chance of unnecessary external processing of sensitive medical information.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The skill sends detailed patient self-assessment contents, scores, questions, answers, assessment ID, and timestamp to an external remote LLM service. In a medical context, this is sensitive health data processing and creates privacy, compliance, and third-party exposure risk, especially because the transfer is not minimized, consent-gated, or clearly disclosed in the code path.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The code transmits assessment metadata and answer contents to a remote API without any visible user-facing disclosure, consent flow, or privacy notice in the execution path. For a patient-facing rehabilitation self-assessment skill, this omission is dangerous because users may reasonably expect local processing and may unknowingly disclose protected health information to a third party.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The skill mandates a specific remote model/API path and states that execution must use API inference, without presenting a user choice or opt-in. While not inherently exploit code, this forced remote execution weakens user control over where sensitive questionnaire data is processed and increases privacy risk in a healthcare setting.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The module docstring is written entirely in Chinese and later OCR logic prefers the Chinese Simplified plus English Tesseract language set when available. This indicates a language/locale preference without any visible opt-in or documented language choice for users.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The system prompt is written to require the model to respond in Chinese and does not provide any user opt-in or language-selection mechanism. This is a natural-language locale policy issue because the skill imposes a specific language by default rather than letting the user choose.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

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