Back to skill

Security audit

GO/KEGG Enrichment

Security checks for vulnerabilities and agentic risk

Overview

This looks like a real gene-enrichment analysis tool, but it needs Review because its documentation understates external gene-list sharing and its plotting code contains an unsafe evaluation path.

Install only if you are comfortable reviewing or fixing the plotting eval() bug and using an isolated environment. Do not use --use-enrichr or KEGG/online modes with confidential, regulated, or pre-publication gene lists unless external submission is authorized. Prefer a virtual environment with pinned dependencies and direct output to a dedicated results folder.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:596
Finding
Arbitrary Python Code Execution Through Unsafe Ratio Evaluation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:596-603` **Vulnerability Type**: Unsafe use of `eval()` on enrichment-result data **Risk Level**: High ### Vulnerable Code ```python if ratio_col: # Convert ratio to numeric if needed if plot_data[ratio_col].dtype == object: plot_data['ratio_numeric'] = plot_data[ratio_col].apply( lambda x: eval(x) if '/' in str(x) else float(x) ) else: plot_data['ratio_numeric'] = plot_data[ratio_col] ``` ### Technical Analysis The visualization code evaluates string values from the `GeneRatio` or `Overlap` result column as Python expressions whenever the value contains `/`. Python's `eval()` does not restrict the expression to arithmetic and exposes Python built-ins by default. A malicious value such as the following would execute an operating-system command before completing the arithmetic expression: ```python __import__("os").system("attacker-command") / 1 ``` The affected result data originates from the enrichment-analysis output. Exploitation therefore requires an attacker to influence that output, such as through a compromised upstream data source, a malicious or compromised dependency, or replacement of the result object in an integrating application. The vulnerable function is reached by the local GO and KEGG analysis paths when Matplotlib is available and results contain a ratio column. ### Attack Path 1. An attacker gains control over, or compromises, the enrichment data returned to `run_go_enrichment_local()` or `run_kegg_enrichment_local()`. 2. The attacker places a Python expression containing `/` in the `GeneRatio` or `Overlap` field. 3. The program passes the resulting DataFrame to `create_visualizations()`. 4. The object-typed ratio column is processed by `eval()`. 5. The expression executes with the privileges and environment of the user running the Skill. ### Impact Assessment Successful exploitation permits arbitrary Python code ...[truncated 384 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Remove `eval()` entirely and parse ratios using strict numeric conversion: ```python def parse_ratio(value): text = str(value).strip() parts = text.split("/") if len(parts) != 2: raise ValueError(f"Invalid ratio: {text}") numerator = float(parts[0]) denominator = float(parts[1]) if not np.isfinite(numerator) or not np.isfinite(denominator): raise ValueError("Ratio components must be finite") if denominator == 0: raise ValueError("Ratio denominator cannot be zero") return numerator / denominator ``` Then use: ```python plot_data["ratio_numeric"] = plot_data[ratio_col].apply(parse_ratio) ``` Additional hardening should include: - Validate that expected result columns contain only numeric values or strictly formatted numeric ratios. - Reject malformed upstream records rather than attempting permissive interpretation. - Add tests containing Python expressions, zero denominators, non-finite values, and malformed ratios. - Run the analysis with minimum filesystem and network privileges as defense in depth. ]]>

other

Warning
Location
scripts/main.py:449
Finding
External Transmission of Gene Lists Is Inconsistently Disclosed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:449-455` and `scripts/main.py:501-507`; contradictory assessment at `SKILL.md:193-200` **Vulnerability Type**: Undisclosed or understated external data transmission **Risk Level**: Medium ### Vulnerable Code GO analysis submits the gene list through Enrichr: ```python enr = gp.enrichr( gene_list=gene_list, gene_sets=library, organism=enrichr_organism, outdir=None, cutoff=args.pvalue_cutoff ) ``` KEGG analysis performs the same external submission: ```python enr = gp.enrichr( gene_list=gene_list, gene_sets=kegg_library, organism=enrichr_organism, outdir=None, cutoff=args.pvalue_cutoff ) ``` The security assessment states: ```markdown | Risk Indicator | Assessment | Level | |----------------|------------|-------| | Code Execution | Python/R scripts executed locally | Medium | | Network Access | No external API calls | Low | | File System Access | Read input files, write output files | Medium | | Instruction Tampering | Standard prompt guidelines | Low | | Data Exposure | Output files saved to workspace | Low | ``` ### Technical Analysis When `--use-enrichr` is selected, the Skill passes the complete input gene list to `gseapy.enrichr()`, which uses the external Enrichr service. The command-line help identifies this as an online API mode, but the formal risk assessment incorrectly states that no external API calls occur and characterizes data exposure only in terms of workspace output. Gene identifiers can reveal confidential experimental targets, unpublished findings, disease-focused research, or aspects of clinical cohort analysis. The contradiction may prevent users from making an informed privacy decision before submitting the data. ### Attack Path 1. A user reviews the Skill's risk table and relies on the statement that no external API calls are made. 2. The user supplies a confidential gene list and invokes the Skill with `--use-enrichr ...[truncated 835 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Correct the `SKILL.md` risk table to state that Enrichr mode performs external API requests. - Clearly document the service destination, categories of transmitted data, and applicable privacy or retention considerations. - Require explicit confirmation before transmitting a gene list, particularly in interactive use. - Keep online mode opt-in and preserve a genuinely offline mode. - Display a warning such as: ```text Enrichr mode transmits the supplied gene identifiers to an external service. Do not continue with confidential or regulated data unless this transmission is authorized. ``` - Avoid logging complete gene lists unless explicitly requested. - Document whether transport security, proxies, request timeouts, and organizational data-governance controls are supported. - Update the “Data Exposure” assessment to include external submission rather than only local output files. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unbounded Dependency Versions Weaken Supply-Chain Reproducibility<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-5` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```text pandas>=1.3.0 numpy>=1.20.0 gseapy>=1.0.0 matplotlib>=3.5.0 openpyxl>=3.0.0 ``` A second, inconsistent dependency specification also appears at `references/requirements.txt:3-8`: ```text pandas>=1.3.0 numpy>=1.21.0 matplotlib>=3.4.0 seaborn>=0.11.0 rpy2>=3.4.0 openpyxl>=3.0.0 # For Excel output ``` ### Technical Analysis All dependencies use lower-bound-only constraints. A future installation can therefore resolve to versions that were not reviewed with this Skill. No lock file, exact version constraints, or package hashes are provided. This does not establish that any currently named package is malicious. It creates a conditional supply-chain exposure: if a future release, transitive dependency, or distribution channel is compromised, a normal installation may retrieve and run the affected code. The inconsistent secondary requirements file also makes it unclear which dependency set represents the supported environment. Python packages can execute code during installation and are subsequently imported into the analysis process. In particular, `gseapy` is directly trusted with gene data, result generation, and online service interaction. ### Attack Path 1. A user follows the documented `pip install -r requirements.txt` command. 2. The resolver selects the newest versions satisfying the broad `>=` constraints. 3. A selected package or transitive dependency contains a compromised release, or an incompatible future version introduces a security defect. 4. Package code executes during installation or import. 5. The compromised component receives the privileges, files, environment, and network access available to the Skill process. This path is contingent on an upstream compromise or unsafe future release; no such malicious package was identified in the reviewed repositor ...[truncated 449 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin dependencies to versions that have been tested and reviewed. - Generate a lock file that includes all transitive dependencies. - Require package hashes during installation, for example through a hash-locked requirements file and `pip install --require-hashes`. - Consolidate or clearly separate the root and reference dependency files so users cannot accidentally install an obsolete or inconsistent environment. - Install packages in an isolated virtual environment without administrator privileges. - Use an approved package index or internal mirror where appropriate. - Add automated vulnerability and license scanning to dependency update workflows. - Review dependency changelogs and rerun security tests before updating locked versions. - Treat the lock file as a controlled artifact while maintaining a scheduled process for security updates. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (25)

eval() call detected

High
Category
Dangerous Code Execution
Content
# Convert ratio to numeric if needed
            if plot_data[ratio_col].dtype == object:
                plot_data['ratio_numeric'] = plot_data[ratio_col].apply(
                    lambda x: eval(x) if '/' in str(x) else float(x)
                )
            else:
                plot_data['ratio_numeric'] = plot_data[ratio_col]
Confidence
99% confidence
Finding
The code uses Python eval() on values derived from result data when converting ratio strings for plotting. If an attacker can influence the enrichment result contents—such as through malicious or compromised upstream libraries, APIs, or crafted input that propagates into result tables—this can lead to arbitrary code execution in the analyst's environment when visualizations are generated.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill documentation indicates file read/write behavior but does not declare an explicit tool scope such as permissions or allowed-tools. That mismatch weakens policy enforcement and user awareness, increasing the chance the skill is executed with broader filesystem access than intended.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The manifest says to trigger when the user 'wants to understand biological functions of gene sets,' which is a broad natural-language condition rather than a narrowly scoped invocation phrase. This could overlap with ordinary biological interpretation requests that do not specifically ask for GO/KEGG enrichment, increasing the chance of unintended activation.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The file states both that KEGG REST API access and internet queries are required and that there are 'No external API calls' in the risk assessment. This inconsistency can mislead reviewers and operators into approving networked behavior without appropriate controls, logging, or egress restrictions.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest says the skill input organism is limited to human/mouse/rat, but the code explicitly adds zebrafish, fly, and yeast to the supported organism configuration and exposes them via the CLI choices. This is a semantic scope mismatch between the stated capability and the implemented behavior.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
When --use-enrichr is enabled, user-provided gene lists are sent to an external Enrichr service without an explicit runtime warning or confirmation at the point of transmission. In a bioinformatics context, gene lists may be sensitive research or pre-publication data, so silent exfiltration to a third party creates a real confidentiality and compliance risk.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest lists visualizations including barplot, dotplot, and enrichment map, but the visualization functions only implement bar plots and dot plots. No enrichment map generation appears anywhere in the file, so the documented output overstates actual behavior.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The documentation shows extensive file and directory outputs, including reports, CSVs, PDFs, and pathway diagrams, but does not present this as a clear warning or disclosure before use. For markdown skills, behaviors that affect user data or the filesystem should be explicitly called out so users understand that running the skill will create persistent artifacts.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# Python Dependencies for GO/KEGG Enrichment Analysis

pandas>=1.3.0
numpy>=1.21.0
matplotlib>=3.4.0
seaborn>=0.11.0
Confidence
95% confidence
Finding
The dependency is specified with a minimum version only, which allows future unresolved versions to be installed and makes builds non-reproducible. This increases supply-chain risk and can unintentionally introduce vulnerable or breaking releases over time, though the requirements file alone does not prove active exploitation.

Unverifiable Dependency: pandas has 1 known advisory(ies) (CVE-2020-13091 (** DISPUTED ** pandas through 1.0.3 can unserialize and execute commands from an)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# Python Dependencies for GO/KEGG Enrichment Analysis

pandas>=1.3.0
numpy>=1.21.0
matplotlib>=3.4.0
seaborn>=0.11.0
rpy2>=3.4.0
Confidence
95% confidence
Finding
Using an unpinned numpy dependency permits installation of any newer version satisfying the constraint, reducing reproducibility and increasing exposure to supply-chain or newly introduced vulnerable versions. This is a real dependency hygiene weakness, even if no specific exploit path is shown in this file.

Unverifiable Dependency: numpy has 16 known advisory(ies) (CVE-2014-1859 (Numpy arbitrary file write via symlink attack); CVE-2021-41495 (NumPy NULL Pointer Dereference); CVE-2021-33430 (NumPy Buffer Overflow (Disputed)) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
numpy>=1.21.0
matplotlib>=3.4.0
seaborn>=0.11.0
rpy2>=3.4.0
openpyxl>=3.0.0  # For Excel output
Confidence
95% confidence
Finding
An unpinned matplotlib requirement allows environmental drift and could pull in unexpected package versions with security or stability issues. In a data-analysis skill this is primarily a software supply-chain and reproducibility concern rather than an immediate direct exploit.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
numpy>=1.21.0
matplotlib>=3.4.0
seaborn>=0.11.0
rpy2>=3.4.0
openpyxl>=3.0.0  # For Excel output
Confidence
95% confidence
Finding
Leaving seaborn unpinned allows the runtime to resolve to different versions across environments, which weakens integrity controls and can introduce vulnerable transitive dependencies. The risk is low in isolation but still a valid security concern for operational deployments.

Unpinned Dependencies

Low
Category
Supply Chain
Content
numpy>=1.21.0
matplotlib>=3.4.0
seaborn>=0.11.0
rpy2>=3.4.0
openpyxl>=3.0.0  # For Excel output
Confidence
97% confidence
Finding
The unpinned rpy2 dependency is a legitimate supply-chain concern, and it is somewhat more sensitive than pure plotting libraries because it bridges Python and R environments. Version drift here can introduce unsafe behavior, compatibility failures, or vulnerable transitive components in either ecosystem.

Unverifiable Dependency: openpyxl has 2 known advisory(ies) (CVE-2017-5992 (Improper Restriction of XML External Entity Reference in Openpyxl); CVE-2017-5992 (Openpyxl 2.4.1 resolves external entities by default, which allows remote attack)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
Confidence
94% confidence
Finding
The dependency is specified with a lower bound only, which allows installation of any future pandas release. This weakens reproducibility and can silently introduce vulnerable or breaking versions through supply-chain drift, especially in environments that rebuild dependencies over time.

Unverifiable Dependency: pandas has 1 known advisory(ies) (CVE-2020-13091 (** DISPUTED ** pandas through 1.0.3 can unserialize and execute commands from an)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
79% confidence
Finding
The manifest does not pin pandas, so it is impossible to verify whether installed environments are using a version affected by a known advisory. This is dangerous because the project cannot make reliable claims about exposure, patch status, or reproducibility across deployments.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
Confidence
94% confidence
Finding
Using numpy>=1.20.0 permits unbounded version resolution, so builds may consume different releases over time. That increases supply-chain risk and makes it difficult to determine whether deployed environments include vulnerable versions.

Unverifiable Dependency: numpy has 16 known advisory(ies) (CVE-2014-1859 (Numpy arbitrary file write via symlink attack); CVE-2021-41495 (NumPy NULL Pointer Dereference); CVE-2021-33430 (NumPy Buffer Overflow (Disputed)) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding
Because numpy is not pinned, the package could resolve to versions with known historical vulnerabilities, and exposure cannot be validated from this file alone. This uncertainty is a supply-chain weakness that hinders secure deployment and forensic review.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
Confidence
91% confidence
Finding
The gseapy dependency is not pinned to a specific release, allowing unexpected upstream changes to enter the environment. In a data-analysis skill, this is mainly a supply-chain and reproducibility issue rather than an immediately exploitable flaw by itself.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
Confidence
93% confidence
Finding
matplotlib>=3.5.0 allows any later version, which can introduce vulnerable or incompatible transitive behavior during future installs. This undermines deterministic builds and complicates incident response or advisory triage.

Unpinned Dependencies

Low
Category
Supply Chain
Content
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
Confidence
95% confidence
Finding
openpyxl is unpinned, so the environment may resolve to different releases with different security properties. Because this package processes spreadsheet files, version ambiguity is more relevant than for a purely internal utility library.

Unverifiable Dependency: openpyxl has 2 known advisory(ies) (CVE-2017-5992 (Improper Restriction of XML External Entity Reference in Openpyxl); CVE-2017-5992 (Openpyxl 2.4.1 resolves external entities by default, which allows remote attack)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding
openpyxl has known XML-related advisories, and the absence of version pinning makes it unknown whether a vulnerable release could be installed. In this skill context, spreadsheet handling is plausible, so ambiguity around a parser dependency is more concerning because attacker-supplied files may trigger vulnerable code paths.

Missing User Warnings

Low
Confidence
75% confidence
Finding
The program creates directories and later writes CSV/TSV/Excel/PNG/REPORT.txt outputs under the provided output directory. While some print statements announce paths during execution, there is no upfront warning in the function documentation or main workflow that running the tool will create and overwrite analysis artifacts in the filesystem.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/main.py:600