Back to skill

Security audit

unisound-literature-analysis

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-aligned for medical literature analysis, but it should be reviewed because it sends document content to a remote model API and can execute unaudited shared preprocessing code outside the skill package.

Install only if you are comfortable sending the selected literature fields and extracted document text to the maas-api.hivoice.cn medical model service. Avoid using confidential, unpublished, regulated, or licensed content unless your organization has approved that service. Prefer JSON input when possible, avoid passing real appkeys on the command line, and run document/OCR preprocessing in a sandboxed environment if handling untrusted files.

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:247
Finding
Untrusted External Python Module Execution During Preprocessing Fallback<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:247-253` **Vulnerability Type**: Unsafe dynamic loading of an external dependency **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 dynamically loads and executes `_shared/doc-preprocess/scripts/preprocess.py`, which is outside the audited skill directory. Calling `exec_module()` executes all top-level statements in the selected Python file with the privileges and environment of the current skill process. The application does not verify the module's cryptographic digest, signature, owner, permissions, or expected package identity before execution. Consequently, the security of this skill depends on an external mutable file that is not included in the reviewed artifact. An attacker who can create or modify that file can convert a normal preprocessing failure into arbitrary Python code execution. ### Attack Path 1. The attacker obtains write access to the expected `_shared/doc-preprocess/scripts/` directory or its `preprocess.py` file. 2. The attacker inserts malicious top-level Python code into `preprocess.py`. 3. The attacker or a user supplies an input that causes the local preprocessor to raise `PreprocessError`, such as an input requiring unavailable parsing support. 4. The exception handler reaches the fallback logic. 5. `spec_from_file_location()` selects the attacker-controlled file. 6. `exec_module()` executes its top-level code. 7. The malicious code runs with the same opera ...[truncated 574 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the dynamic external-file fallback and package the required preprocessing implementation inside the audited skill or as a pinned, trusted dependency. 2. Import dependencies through the normal Python package mechanism from a controlled environment rather than by filesystem path. 3. If external loading is unavoidable: - Verify the file against a pinned cryptographic digest or trusted digital signature. - Resolve the path and confirm that it remains within an approved directory. - Verify that the file and all parent directories are owned by a trusted account. - Reject files or directories writable by untrusted users or groups. 4. Run document preprocessing in a sandbox with minimal filesystem, environment, and network access. 5. Fail closed when dependency integrity cannot be established. 6. Record the exact trusted dependency version and integrity value in deployment configuration. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:113
Finding
Prompt Injection Through Untrusted Literature Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:113-128` **Vulnerability Type**: Untrusted document content embedded in an LLM instruction message **Risk Level**: Medium ### Vulnerable Code ```python user_prompt = f"""请分析以下研发文献: 研发主题:{topic} 关键词:{json.dumps(keywords, ensure_ascii=False)} 总文献数:{len(literature)},匹配数:{len(matched)} 匹配文献: ```json {json.dumps(matched, ensure_ascii=False, indent=2)} ``` 请综合证据要点,识别研究空白,给出分析结论。""" text = _call_llm(SYSTEM_PROMPT, user_prompt, appkey) ``` ### Technical Analysis Literature titles, abstracts, conclusions, URLs, and metadata originate from user-supplied files. After keyword matching, these values are serialized into JSON and inserted directly into the same user message that contains instructions for the language model. A JSON code fence is only a presentation convention and does not create an enforceable security boundary. The model may interpret instruction-like text inside an abstract or conclusion as commands rather than inert evidence. There is no prompt-injection screening, explicit trust-boundary enforcement, structured-output requirement, or post-generation validation. This permits indirect prompt injection. For example, a conclusion could instruct the model to disregard the requested evidence analysis, fabricate findings, include an attacker-controlled link, or produce misleading medical research claims. ### Attack Path 1. An attacker prepares a supported input document containing a literature record. 2. The title, abstract, or conclusion includes adversarial instructions directed at the model. 3. The attacker includes a relevant topic or keyword so that the malicious record passes local keyword matching. 4. The record is added to `matched` and embedded in `user_prompt`. 5. The complete prompt is sent to the remote language model. 6. The model may follow the embedded instructions and return attacker-influenced content. 7. The application places the model response into the output's ...[truncated 633 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Clearly instruct the model that all literature fields are untrusted quoted data and that instructions found inside those fields must never be followed. 2. Separate task instructions from document content using a structured API or schema where available. 3. Require a strict machine-readable response schema and validate every returned field before rendering or further processing. 4. Detect and flag instruction-like phrases in literature fields, especially attempts to override previous instructions, alter output rules, or request secrets. 5. Preserve citations and require each generated claim to map to a specific supplied record. 6. Apply output checks for unsupported claims, unexpected URLs, prompt leakage, and deviations from the requested analysis. 7. Display a warning that generated conclusions may be affected by malicious or unreliable source documents. 8. Avoid using generated content as an automated medical, research, or operational decision without human review. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:234
Finding
Bearer Token Exposure Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:234` and `SKILL.md:70` **Vulnerability Type**: Sensitive credential supplied through process arguments **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument("--appkey", required=True, help="内部医疗大模型鉴权key(必填)") ``` The documented invocation also requires the credential on the command line: ```bash python3 scripts/run.py --input input.json --output output.json --appkey YOUR_KEY ``` ### Technical Analysis The model API bearer token is accepted as a command-line argument. Command-line secrets can be exposed through shell history, process inspection facilities, job-runner logs, monitoring agents, debugging output, terminal session records, and wrapper scripts. Although the token is subsequently transmitted over HTTPS, transport encryption does not protect it from local disclosure caused by the argument-handling design. ### Attack Path 1. A user invokes the skill with a real credential in the `--appkey` argument. 2. The shell may save the complete command in its history. 3. While the process is running, local process-monitoring mechanisms may expose its argument list. 4. Automation, orchestration, or diagnostic systems may also record the invocation. 5. An attacker or unauthorized operator with access to one of these records obtains the bearer token. 6. The attacker reuses the token to call the configured medical-model API until the credential expires or is revoked. ### Impact Assessment Successful exploitation discloses the API bearer token. An attacker may use the token to make unauthorized requests, consume service quota, incur costs, submit sensitive content, or generate activity attributed to the legitimate user. This issue does not inherently provide operating-system privilege escalation. API-side impact depends on the permissions, quotas, expiration, and account scope associated with the exposed token. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the requirement to provide the credential directly through `--appkey`. 2. Read the token from a protected environment variable or an operating-system secret manager. 3. Support reading the token from standard input without echoing it when interactive use is required. 4. If file-based credentials are supported, require restrictive file permissions and avoid including the secret in generated output or logs. 5. Update `SKILL.md` so examples do not encourage command-line credential submission. 6. Redact authorization values and credential-related arguments from application, orchestration, and diagnostic logs. 7. Use short-lived, narrowly scoped tokens and implement straightforward rotation and revocation procedures. 8. Ensure the remote service applies rate limits and usage monitoring to detect unauthorized token reuse. ]]>
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
97% confidence
Finding
The skill is presented as a narrowly scoped medical literature-analysis tool, but the markdown specifies broad multi-format document ingestion, OCR, Office/PDF conversion, and generic text extraction via external tools. This mismatch is risky because operators may approve or trust it as a low-risk analysis skill when it actually processes arbitrary files and executes helper binaries, creating a larger and less obvious attack surface.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares no explicit tool scope or permission boundaries while its documented operation implies use of filesystem access, shell execution, environment-provided secrets, and outbound network access. In an agent platform, this increases the attack surface because users and orchestrators cannot constrain or audit what the skill is allowed to do, especially when it can invoke external binaries and remote APIs.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The description is written as a fixed Chinese-language skill purpose statement, and the document does not indicate that users may choose another language or locale. Under the policy criteria, forcing a specific language without user opt-in is a natural-language policy issue unless the locale restriction is explicitly justified.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill clearly states that analysis is performed by an external medical model API and requires an appkey, but it does not provide a prominent privacy warning that uploaded literature contents and extracted text will be transmitted off-platform. In a pharmaceutical R&D context, documents may contain unpublished data, licensed content, or sensitive internal analyses, so silent external transmission can cause serious confidentiality and compliance issues.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The module docstring explicitly describes a '通用文件预处理工具' for extracting text or tables from many file types, including PDFs, Office documents, spreadsheets, JSON, and images. The manifest describes a medical-affairs R&D literature analysis skill, but this code is a generic ingestion/conversion component rather than literature analysis or evidence synthesis behavior.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
The top-level natural-language docstring is written as a Chinese-only description and suggests a locale-specific presentation without any indication that language choice is configurable. Under the policy, forcing a specific language without user opt-in can be a locale policy violation unless the constraint is explicitly justified.

Context-Inappropriate Capability

Medium
Confidence
85% confidence
Finding
The skill invokes multiple local executables (LibreOffice, pdftotext, Tesseract) to process user-provided files, which is broader and riskier than simple literature-text handling. In this skill context, document ingestion is expected, but using several heavyweight external parsers materially increases the attack surface and operational blast radius if hostile files are supplied.

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
71% confidence
Finding
Although this LibreOffice invocation does not use a shell and is not command-injectable, it causes a complex external parser to process attacker-supplied `.doc` content. Running office conversion on untrusted files increases attack surface substantially because parser vulnerabilities in LibreOffice or related components could lead to denial of service or, in worst cases, code execution in the host environment.

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
72% confidence
Finding
This call similarly feeds attacker-controlled `.xls` data into LibreOffice for conversion. The lack of shell usage prevents classic command injection, but invoking a large external document-processing binary on untrusted spreadsheets introduces parser exploitation and resource-exhaustion risk.

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
This `pdftotext` subprocess uses safe argument passing, but it still hands untrusted PDFs to an external parser. Malicious PDFs can trigger vulnerabilities or extreme resource consumption in PDF tooling, so the security issue is unsafe processing of hostile content rather than 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.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The system prompt is entirely in Chinese and instructs the model to produce Markdown output with a Chinese disclaimer, while the file provides no opt-in or language selection mechanism. This is a locale/language policy concern because it imposes a specific language on users without documented justification.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill sends matched literature content, including titles, abstracts, conclusions, and URLs, to an external API endpoint (`maas-api.hivoice.cn`) for analysis. In a medical/pharma R&D context, these materials may be proprietary, confidential, or regulated, and the code provides no consent prompt, disclosure, minimization, or configuration to keep processing local, increasing the risk of unintended data exfiltration to a third party.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
Accepting the API credential via `--appkey` exposes the secret to common process-inspection surfaces such as shell history, job logs, CI output, and system process listings. This is especially risky in shared research or enterprise environments, where leaked credentials could let others access the external medical LLM service and any data reachable through that account.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The function runs an external office conversion tool on a user-provided document using subprocess, which is a safety-critical execution path involving file processing outside the Python runtime. The code lacks a user-facing notice, confirmation, or descriptive comment explaining that uploaded documents are handed to LibreOffice/soffice for conversion.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This code calls an external office binary to convert `.xls` files into CSV, which is a subprocess-based handling of user data. There is no confirmation prompt, user-facing logging, or explanatory comment warning that the spreadsheet will be processed by LibreOffice/soffice.

Missing User Warnings

Low
Confidence
80% confidence
Finding
When native PDF parsing fails, the function falls back to calling `pdftotext` on the user-provided PDF, which is an external subprocess operation over user content. The code does not include any user-facing warning, confirmation, or explanatory comment about this external processing path.

Missing User Warnings

Low
Confidence
82% confidence
Finding
This function sends the supplied image file to an external OCR executable via subprocess, which is a safety-relevant operation on user data. Although errors are surfaced, there is no confirmation prompt, user-facing log/print, or inline warning/comment disclosing that external processing of the file will occur.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

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