Back to skill

Security audit

unisound-clinical-trial-design

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches a clinical-trial design helper, but it has under-disclosed sensitive data handling and a hidden fallback that can execute an external local Python module outside the reviewed package.

Install only in an environment where the remote medical-model endpoint, credential handling, and document-processing tools are approved for clinical or proprietary R&D data. Prefer JSON input for sensitive work, avoid passing real appkeys on the command line when possible, sandbox untrusted documents, and review or remove the external _shared fallback before relying on the skill.

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:187
Finding

API Credential Exposure Through Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: scripts/run.py:187
Additional Documentation Location: SKILL.md:81, SKILL.md:143
Vulnerability Type: Insecure credential handling
Risk Level: Medium

Vulnerable Code

python
parser.add_argument("--appkey", required=True, help="内部医疗大模型鉴权key(必填)")

The documented invocation also instructs users to place the credential directly in the command:

bash
python3 scripts/run.py --input input.json --output output.json --appkey YOUR_KEY

Technical Analysis

The application requires the bearer credential to be supplied as a command-line argument. Command-line arguments may be exposed through:

  • Operating-system process listings such as ps.
  • Shell history files.
  • Process monitoring and observability systems.
  • CI/CD job logs.
  • Container or workload metadata.
  • Diagnostic reports that capture process arguments.

HTTPS protects the credential while it is transmitted to the API, but it does not mitigate disclosure through local process metadata or command history.

Attack Path

  1. A user follows the documented invocation and passes a valid credential using --appkey.
  2. The shell records the command in its history, or the operating system exposes it in the process argument list while the program is running.
  3. A local user, administrator, monitoring service, or party with access to execution logs reads the argument.
  4. The party extracts the credential and submits unauthorized requests to the medical-model API.

Impact Assessment

Exploitation does not directly grant operating-system privileges. It can grant access equivalent to the compromised API credential, including unauthorized consumption of API quota, submission of model requests, and access to any capabilities associated with that credential.

The impact is limited by the permissions, expiration, rate limits, and billing scope assigned to the exposed credential.

Remediation
View remediation

Remediation Suggestions

  • Read the credential from a protected environment variable or operating-system secret store rather than a command-line argument.
  • Support secure standard-input entry, preferably without terminal echo, for interactive execution.
  • Integrate with the deployment platform's native secret-management mechanism.
  • Remove the credential-bearing command example from SKILL.md.
  • If --appkey must remain for backward compatibility, clearly mark it as deprecated and insecure.
  • Ensure application and orchestration logs redact authorization credentials.
  • Use short-lived, narrowly scoped credentials and provide a rotation procedure.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:87
Finding

LLM Prompt Injection Through Untrusted Clinical-Trial Fields

Content
View full analysis

Vulnerability Details

File Location: scripts/run.py:87-98
Related Parsing Location: scripts/run.py:156-165
Vulnerability Type: Untrusted input incorporated into an instruction-bearing LLM prompt
Risk Level: Medium

Vulnerable Code

python
user_prompt = f"""请审阅以下临床试验设计:

适应症:{indication}
干预措施:{intervention}
试验分期:{phase}
研究目的:{objective}
目标人群:{population}
对照方式:{control}
随机化方式:{randomization}
盲法:{blinding}
终点列表:{json.dumps(endpoints, ensure_ascii=False)}
访视安排:{json.dumps(visits, ensure_ascii=False)}

请审阅设计合理性,指出潜在偏倚,给出优化建议。"""

Input values parsed from text are preserved without semantic validation:

python
for line in lines:
    match = pattern.match(line)
    if not match: continue
    key = header_map.get(normalize_header(match.group(1).strip()))
    if key is None: continue
    value_str = match.group(2).strip()
    try: result[key] = json.loads(value_str)
    except (json.JSONDecodeError, ValueError): result[key] = value_str

Technical Analysis

Values originating from untrusted JSON, text, document, spreadsheet, PDF, or OCR input are interpolated directly into the LLM user prompt. The prompt does not clearly isolate these values as inert data, and the system prompt does not explicitly prohibit following instructions embedded inside clinical-trial fields.

An attacker can place model instructions inside a recognized field such as indication, objective, population, or endpoints. Because both the legitimate request and attacker-controlled directives appear in the same user message, the model may treat the embedded content as instructions rather than clinical data.

The model response is subsequently returned as the text field without task-specific response validation:

python
text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey)

This issue affects the integrity and reliability of generated clinical-trial guidance. There is n ...[truncated 1274 chars]

Remediation
View remediation

Remediation Suggestions

  • Serialize all clinical-trial fields into a clearly delimited data block, preferably a strict JSON object.
  • Add a system-level instruction stating that content inside input fields is untrusted data and that embedded instructions must never be followed.
  • Apply type, length, character, and structural limits to every accepted field.
  • Reject unexpectedly large or instruction-like values where appropriate.
  • Request a structured model response and validate it against an explicit schema before returning it.
  • Verify that the response remains within the clinical-trial review task and contains the required safety disclaimer.
  • Treat output as untrusted content when rendering it; do not allow generated Markdown to trigger privileged actions.
  • For high-impact use, require qualified human review before recommendations are used in a protocol.

T07 · Tool Hijacking and Spoofing

Warning
Location
scripts/run.py:213
Finding

Unauthenticated Execution of an External Shared Python Module

Content
View full analysis

Vulnerability Details

File Location: scripts/run.py:213-224
Vulnerability Type: External local module hijacking
Risk Level: Medium

Vulnerable Code

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 derives a path outside the audited skill package and executes a preprocess.py file from that location using exec_module().

No cryptographic integrity check, trusted ownership check, permission check, package signature, or pinned version validation is performed before execution. Python executes all top-level statements in the external file as soon as exec_module() is called.

The actual resolved path also depends on the skill's installation depth. This makes the trust boundary implicit and allows a file outside the reviewed package to influence runtime behavior.

Exploitation requires the attacker to create or modify the resolved external module. Therefore, practical severity depends on filesystem ownership and permissions in the deployment environment.

Attack Path

  1. An attacker gains write access to the resolved _shared/doc-preprocess/scripts directory or replaces its preprocess.py.
  2. The attacker inserts arbitrary Python statements at module scope.
  3. The attacker or a victim supplies input that cause ...[truncated 1057 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove dynamic loading from a path outside the skill package.
  • Package the fallback preprocessor as a normal, version-pinned dependency and import it through the standard module system.
  • Prefer failing closed when the packaged preprocessor cannot handle an input.
  • If shared loading is operationally required, configure an explicit trusted path rather than deriving it from parent-directory depth.
  • Verify the external file against a pinned cryptographic hash or trusted signature before importing it.
  • Check that the file and all parent directories are owned by a trusted account and are not writable by untrusted users.
  • Run the skill under a dedicated least-privileged account with restricted filesystem and network access.
  • Document the shared component and include it in future security reviews and release integrity checks.
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 (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose is clinical trial design assistance, but the skill also supports broad document ingestion, OCR, format conversion, and external tool execution. This mismatch is dangerous because users may trust it as a narrow medical-design helper while it actually processes arbitrary files and may invoke shell-based converters, increasing attack surface for malicious documents, unexpected data access, or unsafe parser/tool chains.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill declares capabilities that imply file access, shell execution, environment access, and network use, but it does not define any explicit tool scope such as permissions or allowed-tools. That creates an unnecessary trust gap: a user or host system cannot easily determine or constrain what the skill may access when processing untrusted documents or invoking external services.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
78% confidence
Finding

The entire skill description is written as Chinese-only instructions and specifies OCR tooling with chi_sim+eng, but there is no statement that language is user-selectable or that the skill is intentionally limited to a China-specific user base. Per the policy, forcing a specific language/locale without opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill mandates sending user-provided clinical trial content to an externalized internal API endpoint, yet the description does not prominently warn that potentially sensitive research, patient-related, or proprietary protocol information will be transmitted off-box. In a pharmaceutical and clinical context, this raises confidentiality, compliance, and data-governance risks even if the endpoint is organizationally controlled.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest describes a skill for assisting pharmaceutical clinical trial design, but this file implements a broad, reusable document-ingestion utility for PDFs, Office docs, spreadsheets, JSON, text, and images. That preprocessing may be useful as a supporting detail, but the code itself is not specialized to protocol or trial-design tasks and materially broadens the skill's actual behavior beyond the declared domain intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The top-level docstring is written as a Chinese-only description of a general-purpose preprocessing tool, while the code also prefers Chinese OCR language data later in the file. For a general-purpose skill, this indicates a locale/language preference without any opt-in or documented regional justification.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The skill launches external office, PDF, and OCR executables against user-supplied files, which materially expands the attack surface beyond the stated clinical-trial-design purpose. Even without shell injection, processing untrusted documents through complex third-party parsers can expose the host to parser vulnerabilities, crashes, or resource abuse if these tools are not isolated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code automatically performs document conversion and OCR on untrusted user files without any visible consent, warning, or trust-boundary acknowledgment. In a skill that is nominally about clinical trial design, silently invoking heavyweight external parsers increases unexpected processing risk and can subject the environment to malformed-file attacks or denial-of-service conditions.

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

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code automatically sets the OCR language to "chi_sim+eng" when those models are installed, rather than selecting language based on user choice or neutral defaults. This is a natural-language locale policy issue because it imposes a language preference on all image OCR inputs.

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

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code sends full clinical trial design inputs, which may include sensitive proprietary R&D or regulated medical information, to an external API endpoint via _call_llm without any in-code notice, consent flow, minimization, or policy gating. In this skill context, the data is especially sensitive because trial protocols, endpoints, populations, and interventions can reveal confidential drug development strategy and potentially regulated health-related information.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

Natural-language strings in the module docstring, system prompt, user prompt template, and CLI description consistently assume Chinese-language use. The file does not provide a user opt-in, language selection mechanism, or explicit justification that the skill is intended only for a Chinese-language environment.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

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