Back to skill

Security audit

Bio Ontology Mapper

Security checks for vulnerabilities and agentic risk

Overview

The skill is a plausible biomedical ontology mapper, but it overstates its capabilities and can send sensitive clinical terms and an API key to external services in URL query strings.

Review before installing. Use local-only mode for clinical or identifiable data, avoid passing API keys on the command line, and treat the advertised ICD-10/NER/cross-mapping features as unimplemented unless the package is corrected. The evidence does not show malicious behavior, persistence, or hidden exfiltration, but the privacy and install issues are material.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/main.py:59
Finding
Sensitive biomedical terms and API credentials are transmitted in URL query strings<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:59-60`, `scripts/main.py:93-101`, and `scripts/main.py:146-149` **Vulnerability Type**: Sensitive information exposure through URL query parameters **Risk Level**: Medium ### Complete Code Snippet ```python query_string = urllib.parse.urlencode(params) url = f"{self.BASE_URL}{endpoint}?{query_string}" ``` ```python def search(self, term: str, sabs: Optional[List[str]] = None, page_size: int = 10) -> List[Dict]: """Search UMLS for concepts.""" if not self.api_key: return [] params = { "string": term, "apiKey": self.api_key, "pageSize": str(page_size) } if sabs: params["sabs"] = ",".join(sabs) response = self._make_request("/search/current", params) ``` ```python def search(self, term: str) -> List[Dict]: """Search MeSH descriptors.""" encoded_term = urllib.parse.quote(term) url = f"{self.BASE_URL}/lookup/descriptor?label={encoded_term}" response = self._make_request(url) ``` ### Technical Analysis The UMLS client places both the user-provided biomedical term and the UMLS API key in an HTTPS URL query string. The MeSH client likewise places the biomedical term in a URL query string. HTTPS protects the request while it is in transit, but query strings can still be recorded by local proxies, enterprise gateways, reverse proxies, monitoring products, browser or HTTP diagnostics, server access logs, and exception telemetry. An API key in a query string therefore has a broader exposure surface than a credential carried in an authorization header. Biomedical terms may also contain protected health information when derived from clinical records. Remote access is explicitly enabled through `--use-api`, and the destinations are fixed official NLM domains. Consequently, this behavior is necessary for the optional external lookup feature and is not covert exfiltration. However, transmitting raw clinica ...[truncated 1539 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use an API-supported authorization header instead of placing the UMLS key in the query string. If the upstream service mandates a query parameter, clearly document this residual risk and avoid recording request URLs. 2. Use a POST request body for sensitive search terms where the API supports it. 3. Add an explicit warning or confirmation before remote lookup, stating that every input term will be sent to NLM. 4. Reject or de-identify likely PHI before external transmission. For clinical datasets, default to local-only processing. 5. Ensure exception messages, debug output, proxy configuration, and telemetry never record full request URLs. 6. Accept the API key through an environment variable or protected credential store rather than a command-line argument, because command-line values may be visible in process listings and shell history. 7. Document the exact external hosts contacted and provide a network allowlist limited to: - `uts-ws.nlm.nih.gov` - `id.nlm.nih.gov` 8. Add tests verifying that credentials and sensitive terms are absent from application logs. 9. Rotate any API key that may already have been captured in logs. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unnecessary and unpinned packages create a dependency-confusion and supply-chain risk<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-2` **Vulnerability Type**: Unnecessary unpinned third-party dependencies using standard-library module names **Risk Level**: Medium ### Complete Code Snippet ```text dataclasses difflib ``` ### Technical Analysis The script uses `dataclasses` and `difflib`, which are part of the Python standard library in the expected Python 3 environment. Listing these names in `requirements.txt` instructs package installers to retrieve same-named distributions from a package index even though external installations are not required. Neither dependency has a version constraint or an integrity hash. This allows the resolved package contents to change over time and creates exposure to dependency confusion, package compromise, malicious release replacement, or unexpected package behavior. Python packages can execute code during installation or when imported, so compromise could occur before the ontology mapper itself runs. The `dataclasses` backport can be legitimate for older Python versions, but the project uses modern Python 3 syntax and does not declare that such legacy compatibility is required. No justification for an external `difflib` package is present. ### Attack Path 1. A user or automated deployment process runs `pip install -r requirements.txt`. 2. The installer queries the configured public or private package index for `dataclasses` and `difflib`. 3. Because versions and hashes are not constrained, the installer accepts whichever matching distribution the index selects. 4. If a package, release, index, or dependency-resolution path is compromised, attacker-controlled installation code or module code is installed. 5. Malicious package code executes during installation or when the application imports the affected module. 6. The code runs with the privileges of the user or service account performing installation or executing the mapper. ### Impact Assessment Successful supply-chain exp ...[truncated 741 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove both entries from `requirements.txt` when supporting Python versions that include these modules: ```text # No external runtime dependencies required. ``` 2. Declare the minimum supported Python version in project metadata, such as Python 3.8 or later. 3. If legacy Python compatibility genuinely requires the `dataclasses` backport, apply an environment marker and an exact audited version constraint, for example: ```text dataclasses==<audited-version>; python_version < "3.7" ``` 4. Do not install an external `difflib` distribution; use the Python standard-library module. 5. For every future third-party dependency: - Pin an audited version. - Use cryptographic hashes. - Generate a lock file from a trusted index. - Run dependency and package-integrity scanning in CI. - Restrict installation to approved package repositories. 6. Install dependencies in an isolated, least-privileged virtual environment rather than under an administrative account. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code substantially aligns with ontology term normalization for SNOMED CT and MeSH, including confidence scoring and optional API-backed lookup. However, the declared description overstates the implemented behavior in two material ways: (1) ICD-10 mapping is not present anywhere in the code, and (2) the code does not extract entities from unstructured biomedical text; it accepts a single term or newline-delimited inputs and maps those strings directly. These are core functional claims rather than minor omissions, so this is a meaningful description-behavior mismatch.

Lp1

High
Category
MCP Least Privilege
Confidence
98% confidence
Finding
The code performs outbound network requests via urllib to external biomedical APIs, yet the static finding indicates network capability is not covered by declared permissions. Even though the destinations are legitimate services, undeclared egress increases the risk of unauthorized data transmission and weakens security review of what leaves the environment.

Lp1

High
Category
MCP Least Privilege
Confidence
98% confidence
Finding
The code performs outbound network requests via urllib to external biomedical APIs, yet the static finding indicates network capability is not covered by declared permissions. Even though the destinations are legitimate services, undeclared egress increases the risk of unauthorized data transmission and weakens security review of what leaves the environment.

Intent-Code Divergence

Medium
Confidence
88% confidence
Finding
The skill documents external API-based ontology lookups while also acknowledging PHI restrictions, creating a realistic risk that users may send sensitive clinical text to third-party services. In a biomedical context, even accidental transmission of identifiable patient text can create privacy, compliance, and contractual exposure.

Context-Inappropriate Capability

Medium
Confidence
79% confidence
Finding
The manifest describes terminology normalization and code mapping, but the workflow documentation adds a validator step with `check_clinical_plausibility=True`. Assessing clinical plausibility is not an obvious requirement for mapping text to ontology codes and edges toward clinical reasoning capability that the skill explicitly says should not be used for clinical decision support.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The manifest states the skill maps biomedical text to standardized ontologies including ICD-10, but both the module docstring and the actual code implement only SNOMED CT and MeSH clients and mapping paths. There is no ICD-10 mapping logic, selector, client, or output anywhere in the file, so the implemented behavior falls short of the declared capability.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
When --use-api is enabled, user-supplied biomedical terms are sent to external UMLS and MeSH services without any explicit privacy notice or confirmation at the user interface boundary. Because biomedical text may contain protected health information or other sensitive medical context, silent transmission to third parties can create privacy, compliance, and data-governance risk.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The limitations section states 'Best support for English; other languages limited,' which is a natural-language locale constraint. While it is framed as a limitation rather than a hard prohibition, the file does not offer a user language choice or explain a region-specific compliance reason for the constraint.

Unpinned Dependencies

Low
Category
Supply Chain
Content
dataclasses
difflib
Confidence
60% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
dataclasses
difflib
Confidence
60% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.