T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:596- Finding
Arbitrary Python Code Execution Through Unsafe Ratio Evaluation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
scripts/main.py:596Arbitrary Python Code Execution Through Unsafe Ratio Evaluation
scripts/main.py:449External Transmission of Gene Lists Is Inconsistently Disclosed
requirements.txt:1Unbounded Dependency Versions Weaken Supply-Chain Reproducibility
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.
# 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]
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.
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.
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.
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.
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.
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.
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.
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.
# Python Dependencies for GO/KEGG Enrichment Analysis
pandas>=1.3.0
numpy>=1.21.0
matplotlib>=3.4.0
seaborn>=0.11.0
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.
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.
# 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
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.
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.
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
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.
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
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.
numpy>=1.21.0
matplotlib>=3.4.0
seaborn>=0.11.0
rpy2>=3.4.0
openpyxl>=3.0.0 # For Excel output
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.
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.
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
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.
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.
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
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.
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.
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
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.
pandas>=1.3.0
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
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.
numpy>=1.20.0
gseapy>=1.0.0
matplotlib>=3.5.0
openpyxl>=3.0.0
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.
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.
Detected: suspicious.dynamic_code_execution