Back to skill

Security audit

unisound-overall-report

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says broadly, but it sends full medical reports to a remote model despite promising de-identification.

Review before installing or running with real patient data. Use only redacted or minimized reports, keep the API key low-privilege, do not override --base unless the endpoint is trusted, and do not rely on the skill’s stated de-identification claim unless the implementation is fixed.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:145
Finding
Medical Reports Are Transmitted Without the Promised De-identification## Vulnerability Details **File Location**: `scripts/run.py:45-50, 145-147`; related privacy guarantee in `SKILL.md:25-29` **Vulnerability Type**: Sensitive medical information disclosure **Risk Level**: High ### Vulnerable Code ```python def llm(messages: List[Dict[str, str]]) -> str: payload = {"model": model, "messages": messages, "temperature": 0} resp = _http_post(url, payload, headers, timeout=timeout) try: return resp["choices"][0]["message"]["content"].strip() ``` ```python result = llm([sys_msg(SYSTEM_PROMPT), user_msg(prompt)]) ``` The `prompt` variable contains `report_text.strip()`, which is the complete report loaded from the input file. ### Technical Analysis The documentation states that directly identifying information will be de-identified before information is sent to any model or API. The implementation does not contain a redaction, tokenization, pseudonymization, or identifier-detection stage. The report is inserted into the user prompt without modification and passed to the LLM caller. The caller serializes the complete messages collection and sends it to the configured remote API. Consequently, names, identity numbers, telephone numbers, addresses, examination dates, patient identifiers, and sensitive medical findings can all be transmitted if they appear in the supplied report. This violates the documented privacy boundary and the data-minimization principle. It is especially significant because health records can contain both identifying information and highly sensitive medical data. ### Attack Path 1. A user supplies a health report containing personal identifiers and medical findings. 2. `load_input()` reads and returns the complete report without sanitization. 3. `run_overall_report()` embeds the unchanged report in the LLM prompt. 4. The prompt is passed to the `llm` closure. 5. The closure serializes the messages and transmits them to the remote completion API. 6. The remote service receives the identif ...[truncated 888 chars]
Remediation
## Remediation Suggestions 1. Add a local de-identification stage before prompt construction. Detect and redact names, telephone numbers, email addresses, postal addresses, identity numbers, medical-record numbers, and other direct identifiers. 2. Apply data minimization by extracting only findings necessary for interpretation rather than sending the complete original report. 3. Validate the sanitized result before transmission and block the request when high-confidence identifiers remain. 4. Clearly inform users that medical data will be sent to a remote model and obtain explicit consent before transmission. 5. Update the documentation so that its privacy claims exactly match implemented behavior and residual risks. 6. Add automated tests containing representative identifiers and verify that none appear in the outgoing request payload. 7. Define retention and logging restrictions with the API operator, including suppression of request-body logging where supported. 8. Consider a locally hosted processing option for reports that cannot legally or contractually be disclosed to a remote service.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.py:42
Finding
Unrestricted API Base URL Can Exfiltrate the Bearer Key and Medical Report## Vulnerability Details **File Location**: `scripts/run.py:42-50, 191-193, 211` **Vulnerability Type**: User-controlled sensitive-data destination **Risk Level**: High ### Vulnerable Code ```python def make_llm_caller(appkey: str, base: str = DEFAULT_LLM_BASE, model: str = DEFAULT_LLM_MODEL, timeout: int = 0): url = f"{base.rstrip('/')}/chat/completions" headers = {"Authorization": f"Bearer {appkey}"} def llm(messages: List[Dict[str, str]]) -> str: payload = {"model": model, "messages": messages, "temperature": 0} resp = _http_post(url, payload, headers, timeout=timeout) ``` ```python parser.add_argument("--base", default=DEFAULT_LLM_BASE, help=f"Internal model base URL; default: {DEFAULT_LLM_BASE}.") ``` ```python llm = make_llm_caller(args.appkey, args.base, args.model, args.timeout) ``` The displayed argument description is translated for reporting clarity; the executable behavior is the unrestricted assignment of `args.base` to the request destination. ### Technical Analysis The command-line `--base` value is accepted without validation and directly determines the destination of the HTTP request. There is no hostname allowlist, scheme validation, certificate pinning, or confirmation when the default endpoint is replaced. The application attaches the supplied API key as a bearer token to every request and includes the complete LLM message payload, which contains the medical report. A malicious base URL can therefore receive both the credential and sensitive report data. The absence of scheme enforcement also permits an `http://` destination. In that case, the authorization header and medical data can travel without transport encryption. Even when HTTPS is used, an attacker-controlled host legitimately receives the bearer token because the application intentionally places it in the request header. ### Attack Path 1. An attacker persuades a user or automation operator to invoke the skill with a malicious option such as ...[truncated 1544 chars]
Remediation
## Remediation Suggestions 1. Remove the production `--base` override if custom endpoints are not an essential feature. 2. If endpoint customization is required, enforce an explicit allowlist of approved HTTPS hostnames and ports. 3. Reject all non-HTTPS URLs and URLs containing user-information components, fragments, unexpected ports, or ambiguous hostname encodings. 4. Resolve and validate the final destination, including redirects. Do not forward authorization headers across hosts. 5. Bind production API credentials to the expected audience or hostname where the API platform supports scoped credentials. 6. Require separate, low-privilege development credentials for custom or test endpoints. 7. Display a clear confirmation and suppress credential transmission when the destination differs from the approved production service. 8. Avoid accepting sensitive configuration through untrusted wrapper commands or editable job parameters. 9. Add tests proving that unapproved hosts, plaintext HTTP destinations, and cross-host redirects are rejected before any report or authorization header is transmitted.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documentation describes capabilities that read local files, write output files, and send report contents to a remote model endpoint, but it does not declare any explicit tool scope or permission boundaries. For a health-report skill handling sensitive medical data, this creates an authorization and data-governance gap: operators cannot clearly constrain file/network access, and the documented external transmission could expose PHI if the runtime grants broad defaults.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The system prompt requires the model to respond in Chinese and structures all user-facing output around Chinese labels and wording. This is a natural-language locale constraint, but the skill does not offer opt-in language selection or clearly justify that the tool is restricted to Chinese-speaking users only.

Ssd 4

Medium
Confidence
97% confidence
Finding
Untrusted report text is interpolated directly into the prompt as if it were authoritative clinical content, with no delimiter-based trust boundary or instruction to ignore embedded commands. An attacker can place prompt-injection text inside the report to steer the model into ignoring formatting rules, fabricating conclusions, leaking hidden instructions, or producing unsafe medical guidance; in a health-report context, that can mislead users about clinically important findings.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill sends full medical report text to a remote LLM endpoint, but there is no explicit user-facing notice, consent step, redaction guidance, or privacy control before transmitting sensitive health data. Because the data is medical in nature, the context materially raises the severity: exposed PHI/PII can create confidentiality, compliance, and trust risks even if the endpoint is an internal service.

Static analysis

No suspicious patterns detected.