Back to skill

Security audit

Volcano Plot Script

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent volcano-plot tool, but its optional R export can place user-controlled text directly into executable R code, so it needs review before use.

Use this only in a controlled workspace with trusted inputs. Avoid `--export-r` when any argument or output path may come from an untrusted source, and review any generated `.R` file before running it. Prefer pinned dependencies or an isolated environment for installation.

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

Error
Location
scripts/main.py:248
Finding
R Code Injection Through Unescaped CLI Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py`, lines 248-292 **Vulnerability Type**: Generated-code injection **Risk Level**: High ### Vulnerable Code ```python r_script = f'''# Volcano Plot Script (R/ggplot2) # Generated by volcano-plot-script library(ggplot2) library(dplyr) # Read data data <- read.csv("{args.input}") # Parameters log2fc_col <- "{args.log2fc_col}" pvalue_col <- "{args.pvalue_col}" gene_col <- "{args.gene_col}" log2fc_thresh <- {args.log2fc_thresh} pvalue_thresh <- {args.pvalue_thresh} # Process data data$negLog10_pvalue <- -log10(data[[pvalue_col]]) data <- data %>% mutate(regulation = case_when( .data[[pvalue_col]] < pvalue_thresh & .data[[log2fc_col]] > log2fc_thresh ~ "up", .data[[pvalue_col]] < pvalue_thresh & .data[[log2fc_col]] < -log2fc_thresh ~ "down", TRUE ~ "ns" )) # Create plot p <- ggplot(data, aes(x=.data[[log2fc_col]], y=negLog10_pvalue, color=regulation)) + geom_point(alpha=0.6, size=1.5) + scale_color_manual(values=c("up"="{args.color_up}", "down"="{args.color_down}", "ns"="{args.color_ns}"), labels=c("up"="Upregulated", "down"="Downregulated", "ns"="Not significant")) + geom_hline(yintercept=-log10(pvalue_thresh), linetype="dashed", color="gray") + geom_vline(xintercept=c(-log2fc_thresh, log2fc_thresh), linetype="dashed", color="gray") + labs(x="{args.xlabel}", y="{args.ylabel}", title="{args.title}") + theme_minimal() + theme(legend.position="bottom") # Save plot ggsave("{output_path}", p, width=10, height=8, dpi={args.dpi}) print(paste("Plot saved to:", "{output_path}")) ''' ``` ### Technical Analysis The optional R exporter constructs executable R source code through direct Python f-string interpolation. Several attacker-controllable command-line values are placed inside quoted R string literals without escaping or val ...[truncated 2121 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not generate executable R source by directly interpolating untrusted strings. 2. Prefer a fixed R script that receives values through command-line arguments, environment variables, or a separate JSON/CSV configuration file. 3. If source generation is unavoidable, encode every string using a dedicated serializer that produces a valid R string literal. Do not rely on simple quote replacement. 4. Reject newline characters, carriage returns, NUL bytes, and other control characters in all text arguments. 5. Apply allowlist validation where possible: - Require column names to exist in the parsed dataset. - Validate colors with a strict accepted color format or a trusted color parser. - Restrict output extensions to supported formats. - Enforce reasonable length limits on labels and titles. 6. Resolve and validate file paths according to the intended workspace policy. 7. Add adversarial tests for double quotes, single quotes, backslashes, newlines, R comments, statement separators, and embedded function calls. 8. Clearly mark generated scripts as untrusted until all embedded values have been safely encoded. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unpinned Python and R Dependencies Create Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, lines 1-4; `SKILL.md`, lines 121-128 **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code `requirements.txt`: ```text pandas matplotlib seaborn numpy ``` `SKILL.md`: ```bash # Python dependencies pip install -r requirements.txt # R dependencies (if using R) install.packages(c("ggplot2", "dplyr", "ggrepel")) ``` ### Technical Analysis All Python dependencies are specified without versions or integrity hashes. The documented R installation command similarly installs packages by name without version constraints or a repository snapshot. As a result, installation resolves whichever package releases and transitive dependencies are available at installation time. The installed code can therefore differ between environments and over time, even when the audited project itself has not changed. The listed package names are conventional and the project does not specify a suspicious custom package index. No malicious dependency was identified during this static audit. The security weakness is the absence of deterministic version and integrity controls, which increases exposure to compromised upstream releases, malicious transitive dependencies, and unexpected incompatible updates. ### Attack Path 1. A user follows the documented setup instructions and runs `pip install -r requirements.txt` or the R `install.packages(...)` command. 2. The package manager queries its configured repository and resolves the latest packages and transitive dependencies that satisfy the unconstrained request. 3. An upstream compromise, repository compromise, poisoned transitive dependency, or unexpectedly unsafe future release is selected. 4. Package installation or later import/loading executes affected third-party code with the privileges of the user or build environment. 5. The compromised dependency can access resources available to that process. ### Impact As ...[truncated 601 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin reviewed Python dependency versions rather than using unconstrained package names. 2. Generate a lockfile that includes all transitive dependencies. 3. Use hash verification, such as pip requirements with `--require-hashes`, for release or CI installation. 4. Install packages only from explicitly approved HTTPS repositories. 5. Pin R package versions or use a reproducible environment manager such as `renv` with a committed lockfile. 6. Use a fixed, trusted R repository snapshot so dependency resolution remains reproducible. 7. Run dependency vulnerability and license scans in CI. 8. Review and test dependency upgrades through controlled pull requests rather than accepting new releases automatically. 9. Perform installation in an isolated, least-privileged environment without unnecessary credentials or access to sensitive files. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (9)

Ae1

High
Category
analysis-evasion
Content
python scripts/main.py --input deg_results.csv --output volcano_plot.png
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill describes capabilities that read input files and write output plots, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates ambiguous execution boundaries and increases the chance that an agent runtime grants broader file access than intended, especially if future script-generation behavior expands beyond simple plotting.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger/description is broad enough to match general visualization requests, which can cause the skill to activate outside its intended DEG/volcano-plot niche. Misrouting to a script-generating skill with file I/O increases the chance of unnecessary code generation or file handling in contexts where a safer, narrower skill should have been selected.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas
matplotlib
seaborn
numpy
Confidence
96% confidence
Finding
The dependency list leaves pandas unpinned, so builds may resolve to different versions over time, including vulnerable or breaking releases. This creates supply-chain risk and reduces reproducibility, even though the file itself does not prove a currently exploitable version is installed.

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
89% confidence
Finding
pandas has at least one known advisory, and because no version is pinned, it is impossible to verify whether deployment will install an affected release. In this context, the risk is somewhat limited because the file is only a dependency manifest for a data-visualization skill, but unresolved version ambiguity still leaves exposure open.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas
matplotlib
seaborn
numpy
Confidence
96% confidence
Finding
matplotlib is specified without a version, allowing unintended upgrades or dependency resolution drift. Unpinned packages increase the chance of pulling a compromised, incompatible, or newly vulnerable release during installation.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas
matplotlib
seaborn
numpy
Confidence
96% confidence
Finding
seaborn is unpinned, which makes the environment non-reproducible and exposes the project to supply-chain and regression risk when dependencies change. While not an immediate exploit by itself, this is a real weakness in dependency hygiene.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas
matplotlib
seaborn
numpy
Confidence
97% confidence
Finding
numpy is unpinned, so installations may resolve to unknown versions, including releases with security issues or behavior changes. For a scientific plotting skill, this is mainly a supply-chain and reproducibility concern rather than a direct code-execution flaw 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
91% confidence
Finding
numpy has multiple known advisories, and the absence of a pinned version means the installed package may or may not be vulnerable. Although the skill purpose is benign bioinformatics plotting, scientific workflows often process external data, so using an unverifiable dependency unnecessarily increases attack surface.

Static analysis

No suspicious patterns detected.