Back to skill

Security audit

Equity Scorer

Security checks for vulnerabilities and agentic risk

Overview

The skill appears intended for local genomics equity analysis, but it has review-worthy safety gaps around sensitive genomic outputs, report integrity, and generated shell commands.

Install only if you are comfortable running a local genomics analysis tool that writes persistent reports. Use it in a restricted workspace with non-sensitive or approved data, review generated markdown before sharing or executing any reproduced command, and treat the HEIM score and FST output as analytical aids rather than publication-ready conclusions without independent validation.

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

Warning
Location
equity_scorer.py:766
Finding
Shell Injection in Generated Reproducibility Command<![CDATA[ ## Vulnerability Details **File Location**: `equity_scorer.py:766-770`, with attacker-controlled interpolation values assigned at `equity_scorer.py:782` and `equity_scorer.py:815` **Vulnerability Type**: Shell command injection through an unsafe generated command **Risk Level**: Medium ### Vulnerable Code ```python ## Reproducibility ```bash # Re-run this analysis python equity_scorer.py --input %(input_name)s --output %(output_name)s ``` ``` The values interpolated into this command are derived from command-line-controlled paths without shell escaping: ```python "input_name": input_path.name, ``` ```python "output_name": output_dir.name, ``` ### Technical Analysis The generated Markdown report includes a shell command intended to reproduce the analysis. The input filename and output directory basename are inserted directly into that command without quoting or escaping them for the target shell. On operating systems that permit shell metacharacters in filenames, an attacker can provide a valid CSV or VCF whose filename contains command substitution, command separators, redirections, or other shell syntax. For example, a file named: ```text $(touch PWNED).csv ``` would result in a report containing a command resembling: ```bash python equity_scorer.py --input $(touch PWNED).csv --output equity_report ``` If a user copies or executes this documented command in a shell, the embedded command substitution is evaluated. The report-generation process does not itself execute the command, so successful exploitation requires the report recipient to run the reproducibility command. ### Attack Path 1. An attacker creates a syntactically valid ancestry CSV or VCF with a filename containing shell syntax. 2. The victim runs Equity Scorer against the attacker-provided file. 3. `generate_report()` takes `input_path.name` and inserts it directly into the Markdown shell block. 4. The resulting `report.md` appears to contain a legitimate reproducibility com ...[truncated 812 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Apply shell-safe quoting to every generated argument with `shlex.quote()`: ```python import shlex safe_input = shlex.quote(str(input_path)) safe_output = shlex.quote(str(output_dir)) ``` - Generate the command from safely quoted full paths rather than unquoted basenames. - Consider emitting a Python argument list instead of a shell command when practical. - Clearly state that generated commands should be reviewed before execution. - Add regression tests covering filenames and output directories containing: - Spaces - Single and double quotes - Semicolons - Dollar signs and command substitutions - Redirection characters - Newlines - Verify that copying the generated command results only in the intended arguments being passed to `equity_scorer.py`. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
equity_scorer.py:653
Finding
Unescaped Input Values Permit Markdown and Report-Structure Injection<![CDATA[ ## Vulnerability Details **File Location**: `equity_scorer.py:650`, `equity_scorer.py:653-664`, and `equity_scorer.py:669-675` **Vulnerability Type**: Markdown injection through untrusted population labels **Risk Level**: Low ### Vulnerable Code ```python # Build figure references fig_refs = {} for name, path in figures.items(): rel = path.relative_to(output_dir) fig_refs[name] = "![%s](%s)" % (name, rel) # Population table pop_rows = [] for pop in sorted(pop_counts.keys()): count = pop_counts[pop] pct = count / total * 100 global_pct = GLOBAL_PROPORTIONS.get(pop.upper(), 0) * 100 ratio = pct / global_pct if global_pct > 0 else float("inf") o_het = obs_het.get(pop, 0) e_het = exp_het.get(pop, 0) pop_rows.append( "| %s | %d | %.1f%% | %.1f%% | %.2fx | %.4f | %.4f |" % (pop, count, pct, global_pct, ratio, o_het, e_het) ) pop_table = "\n".join(pop_rows) # FST summary fst_section = "" if fst_df is not None: fst_rows = [] pops = sorted(pop_counts.keys()) for i, j in combinations(range(len(pops)), 2): val = fst_df.iloc[i, j] fst_rows.append("| %s vs %s | %.4f |" % (pops[i], pops[j], val)) ``` ### Technical Analysis Population labels originate from ancestry CSV fields, population-map data, or inferred sample identifiers. These values are inserted directly into Markdown table cells without escaping Markdown control characters or removing line breaks. An attacker-controlled label can include: - `|` characters to create additional table columns - Newlines to inject arbitrary report sections - Markdown image syntax that causes a renderer to request remote content - Links that direct the recipient to attacker-controlled sites - Raw HTML, if the Markdown renderer permits it For example, a population value containing a newline followed by: ```markdown ![tracking](https://attacker.example/track?id=123) ``` could cause a permissive Markdown viewer to request an attacker-cont ...[truncated 1192 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Escape Markdown metacharacters in all externally derived labels before interpolation. - At minimum, escape table delimiters such as `|`, backslashes, brackets, parentheses, backticks, and HTML-significant characters. - Remove or replace carriage returns, line feeds, null bytes, and other control characters. - Define and enforce an allowlist for population labels where possible, such as letters, digits, spaces, underscores, and hyphens. - Reject labels exceeding a reasonable maximum length. - Configure the report viewer to disable raw HTML and remote content. - Add tests with labels containing table delimiters, newlines, Markdown links, image syntax, and raw HTML. - Keep original labels in structured CSV/JSON output if needed, but use sanitized display values in Markdown. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:16
Finding
Third-Party Installation Dependencies Are Not Version- or Integrity-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:16-31` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Low ### Vulnerable Code ```yaml install: - kind: uv package: biopython bins: [] - kind: uv package: pandas bins: [] - kind: uv package: scikit-learn bins: [] - kind: uv package: matplotlib bins: [] - kind: uv package: numpy bins: [] ``` ### Technical Analysis The skill requests third-party packages by name without exact versions, a lockfile, or cryptographic hashes. Consequently, separate installations can resolve to different package versions over time. Although the listed package names are legitimate and no typosquatted dependency was identified, unconstrained resolution weakens reproducibility and increases exposure to supply-chain compromise. It also fails to enforce the minimum versions documented elsewhere in `SKILL.md`. A future compromised, malicious, or incompatible release could be selected automatically. Package code may execute during installation or when imported by `equity_scorer.py`. ### Attack Path 1. The skill is installed in a new environment. 2. The package manager resolves each dependency to the version currently available under the package name. 3. A compromised or otherwise unsafe release is selected because no reviewed version or hash is required. 4. Dependency code runs during installation or import. 5. The dependency executes with the privileges and data access available to the skill environment. This is a supply-chain hardening weakness rather than evidence that any currently named package is malicious. ### Impact Assessment A compromised dependency could obtain the same privileges as the process installing or running the skill. It could access local genomic inputs, analysis outputs, environment variables, and other files available to that user. The practical likelihood is reduced because all dependency names correspond to establishe ...[truncated 192 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin every dependency to an explicitly reviewed version. - Use a lockfile generated by the selected package manager. - Require cryptographic hashes for downloaded distributions where supported. - Enforce the minimum compatible versions documented by the project. - Install only from trusted package indexes over authenticated TLS. - Perform dependency vulnerability and provenance scanning in CI. - Update pinned versions through a controlled review process rather than resolving mutable latest versions at installation time. - Run installation and analysis under a dedicated, least-privileged environment with access limited to required input and output paths. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill describes generating reports, figures, tables, and reproducibility files, which implies filesystem write capability, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates a least-privilege gap: the runtime may grant broader write access than necessary, increasing the risk of unintended file modification or overwrite if the skill is misused or later extended unsafely.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The scoring system hard-codes a single set of 'global proportions' and uses it as a normative benchmark for representation and report narratives. In an equity-scoring tool, this can systematically bias results, mischaracterize datasets from specific regions or study designs, and create harmful or misleading fairness claims that users may interpret as objective security/compliance evidence.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill ingests genotype and ancestry data, computes population-level summaries, PCA coordinates, checksums, and writes persistent markdown/CSV/JSON outputs without any privacy notice, minimization controls, consent gating, or de-identification guidance. Even if sample IDs are not always printed directly, derived genomic and ancestry outputs can still be sensitive and may enable unintended disclosure or re-identification when shared or stored insecurely.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The report explicitly labels the metric as 'Hudson FST' while the implementation and comments describe a Nei/GST-style calculation. This is an integrity and scientific-misrepresentation issue: downstream users may make decisions or publish conclusions under a false assumption about the statistic used, which is especially sensitive in a genomics/equity context.

Static analysis

No suspicious patterns detected.