Back to skill

Security audit

Variant Annotation

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent research-oriented variant annotation purpose, but it needs Review because it automatically handles sensitive genetic data and contains unsafe clinical scoring behavior that can overstate pathogenicity when data is missing.

Install only if you are comfortable with variant identifiers being sent to NCBI and with results being stored wherever you choose to write them. Do not use its ACMG classification for diagnosis or treatment; have outputs reviewed by qualified genetics professionals, especially because missing population data can be treated as pathogenic evidence in the current implementation. Prefer running it in an isolated environment, avoid patient-identifying data, and do not provide an NCBI API key unless needed.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/main.py:157
Finding
NCBI API Key Exposed in Request URLs and Duplicated During Retries<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:157-179` **Vulnerability Type**: Credential exposure through URL query parameters and unsafe recursive retry **Risk Level**: Medium ### Complete Code Snippet ```python def _ncbi_request(self, url: str) -> Optional[Dict]: """Make NCBI API request with rate limiting and error handling.""" self._rate_limit() if self.api_key: url = f"{url}&api_key={self.api_key}" if "?" in url else f"{url}?api_key={self.api_key}" try: req = urllib.request.Request( url, headers={ "User-Agent": "VariantAnnotator/1.0 (academic research)", "Accept": "application/json" } ) with urllib.request.urlopen(req, timeout=30) as response: return json.loads(response.read().decode('utf-8')) except urllib.error.HTTPError as e: if e.code == 429: time.sleep(1) return self._ncbi_request(url) return None except Exception: return None ``` ### Technical Analysis The NCBI API key is inserted into the complete request URL. Query strings can be retained by HTTP access logs, proxies, debugging systems, monitoring platforms, exception diagnostics, or process instrumentation. Although HTTPS protects the request in transit, it does not prevent the endpoint or trusted intermediary infrastructure from recording the URL. The HTTP 429 handler recursively calls `_ncbi_request(url)` with a URL that already contains the API key. The next invocation appends the same key again. Repeated rate-limit responses therefore create URLs containing multiple copies of the credential and cause unbounded recursion. This can eventually result in oversized URLs, excessive delays, or a `RecursionError`. ### Attack Path 1. A user supplies an NCBI key through `--api-key`. 2. The Skill appends the key to each NCBI request URL. 3. A proxy, monitoring system, exception coll ...[truncated 714 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Avoid recording or displaying request URLs containing API credentials. - Construct each retry from an immutable base URL rather than passing an already modified URL back into the function. - Add the API key exactly once using a structured query-parameter builder. - Redact `api_key` values in application, proxy, and diagnostic logs. - Replace recursive retries with a bounded iterative retry loop. - Set a maximum retry count and use exponential backoff with jitter. - Honor the HTTP `Retry-After` header when it is present. - Return a clear, sanitized error after the retry budget is exhausted. ]]>

other

Warning
Location
scripts/main.py:225
Finding
Potentially Sensitive Genetic Variant Data Is Automatically Transmitted to an External Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:225-250` **Vulnerability Type**: External disclosure of potentially sensitive genetic information **Risk Level**: Medium ### Complete Code Snippet ```python def _query_clinvar(self, query: str) -> Optional[Dict]: """Query ClinVar database via NCBI E-utilities.""" encoded_query = quote(query) # ESearch to get IDs search_url = ( f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?" f"db=clinvar&term={encoded_query}&retmode=json&retmax=10" ) search_result = self._ncbi_request(search_url) if not search_result or not search_result.get('esearchresult', {}).get('idlist'): return None ids = search_result['esearchresult']['idlist'] if not ids: return None # ESummary to get details id_string = ','.join(ids[:5]) # Limit to top 5 results summary_url = ( f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?" f"db=clinvar&id={id_string}&retmode=json" ) return self._ncbi_request(summary_url) ``` The automatic call is made for every queried variant: ```python # Query ClinVar clinvar_result = self._query_clinvar(variant) ``` ### Technical Analysis Each user-supplied variant identifier, HGVS expression, genomic coordinate, or batch-file entry is encoded into an HTTPS query string and transmitted to the official NCBI E-utilities service. This network access is declared in `SKILL.md` and is necessary for the Skill's online annotation functionality. It is therefore not covert or malicious exfiltration. However, genetic variants can constitute sensitive health or patient information. Query-string values may be retained in remote access logs and trusted intermediary infrastructure. The implementation has no explicit consent gate, privacy confirmation, offline mode, or mechanism for distinguishing public research variants from patient-derived data. Batch processi ...[truncated 1261 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Display a clear privacy warning before transmitting variant data. - Require explicit opt-in for external processing of patient-derived or otherwise sensitive data. - Document the exact destination, fields transmitted, and applicable NCBI privacy considerations. - Provide an offline mode using locally downloaded ClinVar/dbSNP datasets where practical. - Encourage users to submit only the minimum variant data required and to remove patient identifiers. - Avoid including unrelated clinical context in API queries. - Where supported by the external API, prefer request methods that do not place sensitive values in URLs. - Add organizational controls for regulated data, including retention, access, and audit policies. ]]>

other

Error
Location
scripts/main.py:290
Finding
Missing Population Frequency Data Is Incorrectly Treated as ACMG PM2 Evidence<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:290-302` **Vulnerability Type**: Unsafe medical classification logic caused by failure to distinguish unavailable data from zero frequency **Risk Level**: High ### Complete Code Snippet ```python # PM2: Absent from controls (MAF < 0.0001) max_freq = 0.0 for pop, freq in allele_freqs.items(): if freq and freq > max_freq: max_freq = freq if max_freq < 0.0001: criteria.append("PM2") score += self.ACMG_SCORES["PM2"] evidence["PM2"] = f"Absent from population databases (max MAF: {max_freq:.2e})" elif max_freq > 0.05: criteria.append("BA1") score += self.ACMG_SCORES["BA1"] evidence["BA1"] = f"Common variant (MAF: {max_freq:.3f} > 5%)" elif max_freq > 0.01: criteria.append("BS1") score += self.ACMG_SCORES["BS1"] evidence["BS1"] = f"Allele frequency greater than expected ({max_freq:.3f})" ``` The frequency dictionary can contain only unavailable values: ```python allele_freq_dict = { 'gnomad_all': annotation.frequencies.gnomad_genome_all, 'thousand_genomes': annotation.frequencies.thousand_genomes_all } annotation.acmg = self._calculate_acmg_score(clinvar_dict, allele_freq_dict) ``` ### Technical Analysis Population frequency fields default to `None`, but `max_freq` is initialized to `0.0`. The loop ignores `None` values and any numerical zero because the condition begins with `if freq`. If all databases are unavailable, were not queried, failed to respond, or did not provide a parseable result, `max_freq` remains zero. The subsequent condition interprets zero as proof that the variant is absent from population databases and awards ACMG criterion PM2. This conflates two materially different states: - The database was successfully queried and reported absence or a qualifying low frequency. - No usable population evidence was retrieved. In the current implementation, gnomAD is not queried at all despite being advertised, so `gnomad_geno ...[truncated 1415 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Track whether each population database was successfully queried separately from the returned frequency. - Initialize the aggregate frequency as `None`, not zero. - Apply PM2 only if at least one appropriate population dataset produced authoritative evidence of absence or a qualifying low frequency. - Treat network failure, unsupported data, parse failure, and absent records as “evidence unavailable.” - Preserve explicit zero frequencies as valid numeric values rather than rejecting them through a truthiness check. - Include the database, population, dataset version, allele count, and query status in the evidence provenance. - Add tests for: - all frequencies being `None`; - explicit numerical zero; - partial API failure; - malformed frequency responses; - successful absence evidence; - conflicting frequencies across populations. - Have the classification model and thresholds reviewed by qualified clinical genetics experts before use beyond education or research. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unnecessary and Unpinned Third-Party Dependency Expands the Supply-Chain Attack Surface<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1` **Vulnerability Type**: Unpinned and unnecessary package installation **Risk Level**: Low ### Complete Code Snippet ```text dataclasses ``` The installation instruction is: ```bash pip install -r requirements.txt ``` ### Technical Analysis The dependency is specified without a version constraint or integrity hash. Consequently, installation resolves whatever package version is available from the configured package index at installation time. This makes builds non-reproducible and exposes users to index compromise, dependency substitution, or changes in future releases. For modern supported Python versions, `dataclasses` is part of the standard library. The project already imports it directly: ```python from dataclasses import dataclass, asdict, field ``` Requiring a separate package is therefore unnecessary unless the project explicitly supports an older Python version needing the backport. No such version constraint is declared in the reviewed project. ### Attack Path 1. A user follows the documented prerequisite and runs `pip install -r requirements.txt`. 2. Pip resolves `dataclasses` from the user's configured package index or mirror. 3. If the package source, mirror, account, or resolved release is compromised or substituted, attacker-controlled package code may be installed. 4. Malicious installation or import behavior could execute with the privileges of the user running pip or the application. No evidence was found that the currently named package is malicious; the finding concerns avoidable and insufficiently constrained supply-chain exposure. ### Impact Assessment Potential impact depends on the privileges used during installation. A compromised dependency could access files and network resources available to that user and modify the Python environment. If installation is performed with elevated privileges, the scope could be broader. The present repository does no ...[truncated 113 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `dataclasses` from `requirements.txt` when supporting Python 3.7 or newer. - Declare the minimum supported Python version in project metadata and documentation. - If an older Python version must be supported, pin the required backport to a reviewed version. - Use cryptographic hashes with a locked dependency file for reproducible installation. - Install dependencies from a trusted, explicitly configured package index. - Run dependency installation without administrative privileges and inside an isolated virtual environment or container. - Add automated dependency auditing to the release process. ]]>

T08 · Insecure Dependencies

Note
Location
references/clinvar-guide.md:104
Finding
Reference Documentation Recommends Unauthenticated Plaintext FTP Downloads<![CDATA[ ## Vulnerability Details **File Location**: `references/clinvar-guide.md:104-107` **Vulnerability Type**: Insecure transport for external clinical dataset retrieval **Risk Level**: Low ### Complete Code Snippet ```bash # Download latest ClinVar VCF wget ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/clinvar.vcf.gz wget ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/clinvar.vcf.gz.tbi ``` ### Technical Analysis The documented commands retrieve the ClinVar VCF and index over plaintext FTP. FTP does not provide authenticated transport or protection against modification in transit. The commands also do not verify a checksum or digital signature after download. These commands are contained in reference documentation and are not automatically executed by `scripts/main.py`. Nevertheless, users following the guide can obtain data whose integrity was altered by a network attacker or compromised intermediary. ### Attack Path 1. A user follows the reference guide and executes the supplied `wget ftp://...` commands. 2. The FTP connection traverses an untrusted or compromised network. 3. An attacker intercepts or modifies the VCF file, index file, DNS response, or FTP data connection. 4. The user receives the altered dataset without any integrity warning. 5. The modified data is used for variant annotation or downstream analysis. ### Impact Assessment An attacker could corrupt clinical significance records, omit variants, introduce false records, or create inconsistencies between the VCF and its index. This could undermine the integrity of downstream genetic analysis. The documented commands do not directly grant additional local privileges. The scope is primarily dataset integrity, although exploitation of an unknown parser defect by a specially crafted dataset could increase impact in downstream tools. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Replace the FTP URLs with official NCBI HTTPS download URLs. - Document verification using checksums published independently by NCBI. - Download the VCF, index, and checksum metadata over authenticated HTTPS. - Fail closed if integrity verification does not succeed. - Record the dataset release date and checksum to support reproducible analysis. - Clarify that downloaded clinical datasets must be treated as untrusted input and parsed with maintained, isolated tools. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises network, file read, and file write behavior but does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization gap where the runtime may grant broader capabilities than reviewers or orchestrators expect, increasing the risk of unintended file access, data exfiltration, or unsafe outbound requests.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger conditions include broad natural-language phrases like mentions of ClinVar, dbSNP, pathogenicity, or clinical significance, which can cause the skill to activate on loosely related conversations. Over-broad invocation is dangerous here because the skill is high-risk, uses network and file capabilities, and produces medically sensitive interpretations that should only run when the user clearly requests variant annotation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This reference document presents ACMG variant-classification guidance in a way that could be used directly for clinical interpretation without any explicit warning that it is informational only and not sufficient for diagnosis or treatment decisions. In the context of a variant-annotation skill, users or downstream agents may over-rely on these rules and produce medical recommendations without clinician review, creating a realistic risk of harmful misclassification.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- **PM3**: For recessive disorders, detected in trans with a pathogenic variant
- **PM4**: Protein length changes as a result of in-frame deletions/insertions in a nonrepeat region or stop-loss variants
- **PM5**: Novel missense change at an amino acid residue where a different missense change determined to be pathogenic has been seen before
- **PM6**: Assumed de novo, but without confirmation of paternity and maternity

### Supporting Pathogenic (PP)
- **PP1**: Cosegregation with disease in multiple affected family members in a gene definitively known to cause the disease
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- **PM3**: For recessive disorders, detected in trans with a pathogenic variant
- **PM4**: Protein length changes as a result of in-frame deletions/insertions in a nonrepeat region or stop-loss variants
- **PM5**: Novel missense change at an amino acid residue where a different missense change determined to be pathogenic has been seen before
- **PM6**: Assumed de novo, but without confirmation of paternity and maternity

### Supporting Pathogenic (PP)
- **PP1**: Cosegregation with disease in multiple affected family members in a gene definitively known to cause the disease
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill sends user-supplied variant identifiers and related genetic data to NCBI services without any explicit notice or consent step. Genetic and clinical variant information can be sensitive personal or research data, so silent transmission to a third-party service creates a real privacy risk, especially in regulated or enterprise environments.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The tool can write annotation results containing potentially sensitive genetic and clinical interpretations to an arbitrary user-specified file path without warning about persistence. This increases the chance of accidental long-term storage, leakage through shared filesystems, backups, or insecure permissions, particularly when handling patient or research data.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The line 'AI自主验收状态: 需人工检查' introduces Chinese-language content in otherwise English documentation. Because no language choice, localization note, or justification is provided, this creates a locale/language policy inconsistency.

Unpinned Dependencies

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

Static analysis

No suspicious patterns detected.