Back to skill

Security audit

MySQL Natural Language Query Assistant

Security checks for vulnerabilities and agentic risk

Overview

This skill appears intended for MySQL analysis, but its read-only database safety guard is incomplete enough to warrant Review before installation.

Install only with a dedicated least-privilege MySQL account limited to SELECT and metadata access, with FILE, write, admin, routine execution, and privilege-management permissions revoked. Prefer pinned, reviewed drivers and verify TLS behavior before using this against remote or sensitive databases.

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/run_read_query.py:11
Finding
Read-Only SQL Validation Can Be Bypassed by Side-Effecting SELECT Operations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run_read_query.py`, lines 11-16 and 92-102 **Vulnerability Type**: Inadequate SQL validation and filesystem-capable database operations **Risk Level**: High ### Vulnerable Code ```python READ_ONLY_PREFIXES = ("select", "with", "show", "describe", "desc", "explain") BLOCKED_TOKENS = { "insert", "update", "delete", "replace", "alter", "drop", "truncate", "create", "grant", "revoke", "lock", "unlock", "set", "rename", "call", "load", "handler", "do", "prepare", "execute", "deallocate", } ``` ```python def _ensure_read_only(sql: str) -> None: normalized = _normalize_sql(sql) lowered = normalized.lower() if not lowered.startswith(READ_ONLY_PREFIXES): raise ValueError("Query must start with a read-only statement.") tokens = set(re.findall(r"[a-z_]+", lowered)) found = BLOCKED_TOKENS & tokens if found: raise ValueError(f"Blocked non-read-only token(s) found: {', '.join(sorted(found))}") ``` ### Technical Analysis The script attempts to enforce read-only behavior by checking the beginning of the SQL string and searching for a limited set of blocked lexical tokens. This is not equivalent to parsing the statement according to MySQL grammar. A statement beginning with `SELECT` is not necessarily free of side effects. In particular, MySQL supports `SELECT ... INTO OUTFILE` and `SELECT ... INTO DUMPFILE`, which can create files using the database server process and its privileges. Neither `outfile` nor `dumpfile` is blocked. The `LOAD_FILE()` function can also read files accessible to the database server. The tokenizer treats `LOAD_FILE` as the single token `load_file`, whereas the blocklist contains only `load`. Consequently, this function is not rejected. These operations remain subject to MySQL configuration, the database account's `FILE` privilege, filesystem permissions, and server-side path restrictions. Nevertheless, the script itself does not en ...[truncated 1414 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace prefix and regular-expression filtering with a MySQL-aware SQL parser. 2. Permit only a narrowly defined allowlist of statement AST forms, rather than trying to enumerate forbidden words. 3. Explicitly reject: - `INTO OUTFILE` - `INTO DUMPFILE` - `LOAD_FILE()` - locking clauses - stored procedure calls - user-defined functions with side effects - any construct that accesses the server filesystem 4. Execute queries with a dedicated database account that has only the minimum required `SELECT`, `SHOW VIEW`, and metadata permissions. 5. Revoke `FILE`, write, administrative, routine execution, and privilege-management permissions from that account. 6. Where supported, establish a transaction or session configured as read-only as an additional control. 7. Add tests containing side-effecting MySQL syntax, comments, unusual whitespace, common table expressions, quoted identifiers, and nested queries. 8. Treat SQL validation and database permissions as independent defense layers; client-side validation must not be the sole security boundary. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run_read_query.py:67
Finding
Configured TLS Mode Is Ignored When PyMySQL Is Used<![CDATA[ ## Vulnerability Details **File Location**: `scripts/introspect_schema.py`, lines 61-69; `scripts/run_read_query.py`, lines 67-75 **Vulnerability Type**: Missing transport security enforcement for database connections **Risk Level**: Medium ### Vulnerable Code In `scripts/introspect_schema.py`: ```python return driver.connect( host=cfg["host"], port=cfg["port"], user=cfg["user"], password=cfg["password"], database=cfg["database"], charset=cfg["charset"], cursorclass=driver.cursors.DictCursor, ) ``` In `scripts/run_read_query.py`: ```python return driver.connect( host=cfg["host"], port=cfg["port"], user=cfg["user"], password=cfg["password"], database=cfg["database"], charset=cfg["charset"], cursorclass=driver.cursors.DictCursor, ) ``` Both scripts parse the setting but do not use it in the PyMySQL branch: ```python "ssl_mode": os.environ.get("DB_SSL_MODE"), ``` ### Technical Analysis The scripts support both `mysql.connector` and PyMySQL. When PyMySQL is selected, the connection call does not receive an `ssl` configuration or another driver-specific TLS option. As a result, `DB_SSL_MODE` has no effect in this execution path. An operator may configure `DB_SSL_MODE` and reasonably expect the connection to require encrypted transport, but the PyMySQL branch silently fails to enforce that expectation. This creates a discrepancy between documented security configuration and actual runtime behavior. The `mysql.connector` branch enables SSL when a value is supplied, but it also does not clearly map documented modes to certificate and hostname-verification requirements. Transport encryption without peer authentication would not fully prevent an active interception attack. ### Attack Path 1. The environment contains database credentials and a configured `DB_SSL_MODE`. 2. `mysql.connector` is unavailable or fails to import, so `_load_driver()` selects PyMySQL. 3. The connection is created witho ...[truncated 937 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define explicit, documented TLS modes with consistent semantics across both supported drivers. 2. Map each mode to driver-specific settings rather than treating `DB_SSL_MODE` as a Boolean value. 3. For PyMySQL, pass an `ssl` configuration containing the trusted CA and certificate-verification requirements. 4. Require server certificate and hostname verification for remote database connections. 5. Fail closed if TLS is required but the selected driver cannot enforce the requested mode. 6. Reject unknown or unsupported `DB_SSL_MODE` values instead of silently weakening security. 7. Consider requiring TLS by default for non-loopback database hosts. 8. Add integration tests that verify encryption and peer validation independently for `mysql.connector` and PyMySQL. 9. Avoid including sensitive connection values in exceptions, logs, or diagnostic output. ]]>

T08 · Insecure Dependencies

Note
Location
references/connection-and-safety.md:17
Finding
Database Drivers Are Installed Without Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `references/connection-and-safety.md`, lines 17-27 **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Low ### Vulnerable Code ```bash pip install mysql-connector-python ``` or: ```bash pip install pymysql ``` ### Technical Analysis The installation guidance resolves the latest available release of either database driver at installation time. It provides no reviewed version constraint, lock file, package hash, or reproducible dependency manifest. This means two installations performed at different times can obtain different code. A future compromised, malicious, or unexpectedly incompatible upstream release would be installed and imported into the Skill's Python process. Database drivers execute with the same operating-system permissions as the invoking process and receive database credentials directly. No evidence was found that the currently named packages are intentionally malicious. The risk arises from mutable and unverified dependency resolution rather than from a confirmed malicious package. ### Attack Path 1. An operator follows the documented installation command. 2. `pip` resolves the current package release from its configured package index. 3. A compromised upstream release, index response, or package artifact is downloaded because no version or hash is constrained. 4. The package is installed into the Skill's Python environment. 5. The scripts import the driver and execute its initialization and connection logic. 6. Malicious dependency code could access process permissions, environment variables, database credentials, queries, and returned records. ### Impact Assessment A compromised driver executes locally with the privileges of the user running the Skill. It may access `DB_USER`, `DB_PASSWORD`, the database endpoint, query contents, query results, and any local resources available to that process. The potential impact of a successful supply-chain compromise ...[truncated 251 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin reviewed versions of `mysql-connector-python` and PyMySQL. 2. Provide a locked dependency manifest generated through a controlled review process. 3. Require package hashes during installation, such as through a hash-locked requirements file. 4. Install packages only from an explicitly trusted package index. 5. Regularly scan pinned dependencies for published vulnerabilities and update them through reviewed changes. 6. Avoid ad hoc package installation at runtime. 7. Use an isolated virtual environment or container with a prebuilt, reviewed dependency set. 8. Document the exact supported Python and driver versions so deployments remain reproducible. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Ae1

High
Category
analysis-evasion
Content
4. Execute only read-only SQL with `scripts/run_read_query.py`.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
4. Execute only read-only SQL with `scripts/run_read_query.py`.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
4. Execute only read-only SQL with `scripts/run_read_query.py`.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill explicitly uses environment variables for database connection details and invokes scripts that connect to a live MySQL database, but it declares no tool scope or permission boundary. That omission means an agent may be allowed broader-than-necessary access to secrets and networked resources without an explicit policy gate, increasing the risk of unintended data access or exfiltration.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
---
name: mysql-query-assistant
description: translate natural-language analytics requests into mysql queries, connect to a live mysql database, inspect schema and column comments, execute read-only sql, and validate query correctness against real results. use when chatgpt needs to work with mysql through direct connection details provided by environment variables, especially for ad hoc analysis, sql generation, schema discovery, query debugging, or cautious database workflows that must verify results before presenting them. also use for restricted write workflows that first generate a preview select and never auto-execute the write statement.
---

# Mysql Query Assistant
Confidence
85% 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
---
name: mysql-query-assistant
description: translate natural-language analytics requests into mysql queries, connect to a live mysql database, inspect schema and column comments, execute read-only sql, and validate query correctness against real results. use when chatgpt needs to work with mysql through direct connection details provided by environment variables, especially for ad hoc analysis, sql generation, schema discovery, query debugging, or cautious database workflows that must verify results before presenting them. also use for restricted write workflows that first generate a preview select and never auto-execute the write statement.
---

# Mysql Query Assistant
Confidence
85% 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

Low
Confidence
79% confidence
Finding
This code emits the full inspected schema payload, including table and column names, directly to standard output. While schema introspection is the script's purpose, there is no print/log/comment/docstring warning that potentially sensitive database structure metadata will be exposed in command output.

Static analysis

No suspicious patterns detected.