Back to skill

Security audit

unisound-similar-case-retrieval

Security checks for vulnerabilities and agentic risk

Overview

This skill coherently performs user-provided similar-case analysis through a documented remote medical LLM, but users must treat the case text and API key carefully.

Install only if you are comfortable sending de-identified case summaries to the documented remote endpoint. Do not include identifiable patient data unless your organization has approved that data flow, and prefer a safer secret-handling method than passing the API key directly on the command line.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.py:73
Finding
Untrusted Clinical Case Data Can Inject Instructions into the LLM Prompt<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:73-105` **Vulnerability Type**: Prompt injection through untrusted model input **Risk Level**: Medium ### Vulnerable Code ```python def build(data: Dict[str, Any], appkey: str) -> Dict[str, Any]: anchor = (data.get("anchor_case") or "").strip() if not anchor: raise ValueError("anchor_case 不能为空") raw = data.get("candidate_cases") if not isinstance(raw, list) or not raw: raise ValueError("candidate_cases 必须为非空数组") candidates: List[Dict[str, Any]] = [] for i, item in enumerate(raw): if not isinstance(item, dict): continue cid = str(item.get("id", f"c{i}")).strip() summary = (item.get("summary") or "").strip() if summary: candidates.append({"id": cid, "summary": summary}) if not candidates: raise ValueError("candidate_cases 中需至少一条含 summary 的病例") top_k = data.get("top_k", 5) try: top_k = max(1, min(20, int(top_k))) except (TypeError, ValueError): top_k = 5 hint = (data.get("task_hint") or "").strip() user = f"""锚点病例: {anchor} 候选病例(共 {len(candidates)} 条): ```json {json.dumps(candidates, ensure_ascii=False, indent=2)} ``` 请重点展开讨论最接近的前 {top_k} 条与其余病例的差异。 {f"科研关注点:{hint}" if hint else ""} """ text = call_llm(SYSTEM, user, appkey) ``` ### Technical Analysis The `anchor_case`, candidate `summary`, candidate `id`, and `task_hint` fields originate from the input JSON and are incorporated directly into the user message sent to the remote language model. The code validates whether required values are present, but it does not establish a semantic trust boundary between application instructions and untrusted clinical text. Placing candidate records inside a Markdown JSON code fence does not prevent a language model from interpreting instructions embedded in those records. An attacker able to control any of these fields can include text instructing the mo ...[truncated 1887 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Explicitly classify case content as untrusted data.** Strengthen the system message with a clear rule that instructions, requests, role declarations, and policy text appearing inside case fields are data and must never be followed. 2. **Use strong structural delimiters.** Pass each field in an explicit structured envelope and identify its trust level and purpose. Do not rely on Markdown code fences as a security boundary. 3. **Constrain the response format.** Request a machine-readable schema containing only supplied candidate IDs, bounded similarity scores, comparison dimensions, and explanations. Reject responses that do not conform to the schema. 4. **Validate model output.** Confirm that every ranked ID exists in the submitted candidate set, reject duplicate or invented IDs, enforce the requested `top_k`, and verify required disclaimer and research-only fields before returning success. 5. **Limit attacker-controlled input.** Apply reasonable character and record-count limits. Where operationally acceptable, detect or flag instruction-like content in clinical fields for review. Detection should be defense in depth rather than the sole control. 6. **Separate data processing from instruction generation.** Consider extracting normalized clinical attributes before ranking and pass only those validated attributes to the ranking stage. This reduces exposure to free-form adversarial instructions. 7. **Treat output as untrusted.** Clearly mark generated text as model-produced content and require human review before it is used in research decisions or downstream automation. 8. **Add adversarial tests.** Include cases containing phrases such as “ignore previous instructions,” forged role markers, Markdown fence termination, and requests to invent candidate IDs. Tests should verify that these strings cannot alter the intended task or output schema. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Missing User Warnings

High
Confidence
98% confidence
Finding
The script sends anchor and candidate case summaries to an external remote API, and these fields can contain highly sensitive clinical or patient-associated information. In a medical research context, undisclosed off-host transmission materially increases privacy, compliance, and data-governance risk, especially if users assume processing is local or do not know what leaves their environment.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises file and network-driven execution (`scripts/run.py`, JSON input/output, and a remote model endpoint) but does not declare any `permissions` or `allowed-tools` scope. This creates a governance gap where reviewers and runtime policy may not have explicit least-privilege constraints, increasing the risk of unintended file access, data exfiltration, or broader tool use if the implementation evolves or is misconfigured.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The manifest description and the entire user-facing markdown are written in Chinese, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-speaking or China-region workflow. Under the policy, imposing a specific language without opt-in is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
Accepting the API key on the command line can expose the credential through shell history, process listings, job control tools, audit logs, and orchestration metadata. This is a real secret-handling weakness even if the script itself does not print the key, because other local users or logging systems may capture it.

Tainted flow: 'text' from pathlib.Path.read_text (line 131, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Content
if args.output:
            p = Path(args.output)
            p.parent.mkdir(parents=True, exist_ok=True)
            p.write_text(text, encoding="utf-8")
        else:
            print(text, end="")
        return 0
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
The natural-language system prompt is written in Chinese and prescribes the output format and behavior, effectively constraining the interaction to a specific language context. There is no indication that the user can opt into another language or that the language restriction is configurable.

Static analysis

No suspicious patterns detected.