Back to skill

Security audit

DB Smart Import

Security checks for vulnerabilities and agentic risk

Overview

This database import skill is mostly coherent, but it can run high-impact database changes with weak safeguards and insecure credential examples.

Review this skill before installing. Use it only on trusted CSV and SQL files, prefer a staging database, take verified backups, avoid root accounts, and do not put database passwords directly in command lines. Treat SQL dumps as executable code because they can alter or destroy data.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/execute_import.py:26
Finding

Unvalidated SQL Identifiers Allow SQL Injection During CSV Import

Content
View full analysis

Vulnerability Details

File Location: scripts/execute_import.py, lines 26-40 and 88-91
Vulnerability Type: SQL injection through dynamically interpolated identifiers
Risk Level: High

Technical Analysis

The destination table and column names are inserted directly into SQL statements using f-strings. Parameterized placeholders are used only for row values; they do not protect the interpolated table name or destination column names.

SQLite implementation:

python
cursor.execute(
    f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}'"
)
if not cursor.fetchone():
    raise ValueError(f"Table '{table_name}' does not exist")

inserted = 0
for row in reader:
    values = [row.get(src, "") for src in mapping.keys()]
    try:
        cursor.execute(
            f"INSERT INTO {table_name} ({', '.join(dest_columns)}) VALUES ({placeholders})",
            values,
        )

MySQL/MariaDB implementation:

python
for row in reader:
    values = [row.get(src, "") for src in mapping.keys()]
    try:
        cursor.execute(
            f"INSERT INTO {table_name} ({', '.join(dest_columns)}) VALUES ({placeholders})",
            values,
        )

table_name comes from the --table command-line argument, while dest_columns comes from the values of attacker-influenced --mapping JSON. Neither input is checked against the actual destination schema nor safely quoted as a database identifier.

Although database drivers commonly reject multiple statements in a single execute() call, that behavior does not make identifier interpolation safe. Crafted identifiers can alter the syntax and semantics of the existing statement without requiring a second statement. Exploitability and exact payloads depend on the selected database engine, schema, and connector settings.

Attack Path

  1. An attacker supplies or influences the import command, mapping JSON, ...[truncated 1478 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not concatenate user-controlled identifiers into SQL.
  • Retrieve the destination schema first and require exact membership of the requested table and destination columns.
  • Reject empty identifiers, unknown identifiers, duplicates, control characters, comments, quotes, whitespace, and SQL metacharacters.
  • Quote validated identifiers using database-specific rules:
    • SQLite: double-quote identifiers and escape embedded double quotes.
    • MySQL/MariaDB: use connector-supported identifier composition where available, or backtick only after strict schema validation and escaping.
  • Keep parameterized placeholders for all row values.
  • Verify that every source mapping key exists in the CSV and every destination mapping value exists in the selected table.
  • Avoid recommending an administrative account. Use a dedicated importer account restricted to INSERT and any narrowly required schema.
  • Add security tests using malicious table and column names, including quotes, comments, parentheses, commas, and SQL keywords.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/analyze_schema.py:125
Finding

Database Passwords Are Accepted and Documented as Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: scripts/analyze_schema.py, lines 125-139; scripts/execute_import.py, lines 154-169; SKILL.md, lines 43 and 61
Vulnerability Type: Sensitive credential exposure through process arguments and shell history
Risk Level: Medium

Technical Analysis

Both database scripts accept a plaintext password through --password:

python
parser.add_argument("--host", default="localhost", help="MySQL host")
parser.add_argument("--user", default="root", help="MySQL user")
parser.add_argument("--password", default="", help="MySQL password")
parser.add_argument("--port", type=int, default=3306, help="MySQL port")
parser.add_argument("--database", help="MySQL database name")

The documentation explicitly demonstrates placing the password on the command line:

bash
python scripts/analyze_schema.py mysql localhost --user root --password secret --database mydb
bash
python scripts/execute_import.py csv /path/to/data.csv --db-type mysql --host localhost --user root --password secret --database mydb --table clients --mapping '{"email": "email", "name": "client"}'

Command-line arguments can be retained in shell history, terminal logs, orchestration logs, agent transcripts, debugging output, and process inspection interfaces. Depending on the operating system and process-monitoring permissions, another local user or monitoring service may be able to read the password while the script runs.

The password is legitimately needed to connect to a user-selected MySQL/MariaDB server, and no evidence of deliberate credential exfiltration was found. However, command-line transmission exceeds the minimum secure exposure necessary because safer credential-input mechanisms are available.

Attack Path

  1. An operator follows the documented example and enters a privileged database password using --password.
  2. The shell records the complete command in history, or a lo ...[truncated 1044 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove plaintext passwords from all documentation examples.
  • Prompt securely with getpass.getpass() when a password was not otherwise provided.
  • Prefer a protected MySQL option file, connector configuration, operating-system credential store, or narrowly scoped secret manager.
  • If environment-variable support is added, document that environment variables may also be visible to privileged local processes and may leak through diagnostics.
  • Consider retaining --password only as a deprecated compatibility option and emit a warning describing its exposure risk.
  • Recommend a dedicated least-privilege database account rather than root.
  • Ensure errors and logs never print connection configuration containing the password.
  • Rotate credentials previously used in command lines where shell history or job logs may have retained them.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/execute_import.py:107
Finding

SQL Dump Execution Commits Partial and Potentially Destructive Imports

Content
View full analysis

Vulnerability Details

File Location: scripts/execute_import.py, lines 107-147
Vulnerability Type: Unsafe SQL dump parsing, unrestricted execution, and non-atomic error handling
Risk Level: High

Technical Analysis

The script reads an entire SQL dump, splits it on every semicolon, executes each fragment independently, suppresses individual errors, and commits all successful fragments:

python
with open(sql_path, "r", encoding="utf-8", errors="ignore") as f:
    sql_content = f.read()

statements = [
    s.strip()
    for s in sql_content.split(";")
    if s.strip() and not s.strip().startswith("--")
]

if db_type == "sqlite":
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    executed = 0
    for stmt in statements:
        try:
            cursor.execute(stmt)
            executed += 1
        except Exception as e:
            print(f"Warning: Skipping statement: {e}", file=sys.stderr)
    conn.commit()

The MySQL/MariaDB branch uses the same behavior:

python
for stmt in statements:
    try:
        cursor.execute(stmt)
        executed += 1
    except Exception as e:
        print(f"Warning: Skipping statement: {e}", file=sys.stderr)
conn.commit()

Splitting SQL on ; is not a valid general SQL parser. Semicolons can occur in quoted strings, comments, stored routines, and trigger bodies. Consequently, legitimate statements can be split incorrectly.

More importantly, an error does not trigger rollback or abort processing. Destructive or privilege-changing statements that precede a malformed statement may succeed and be committed, while dependent statements fail and are skipped. The final message reports only a count of successful statements rather than clearly declaring that the import was partial.

Executing SQL dumps is part of the declared functionality, so arbitrary SQL execution against the selected database is not hidden behav ...[truncated 1752 chars]

Remediation
View remediation

Remediation Suggestions

  • Treat SQL dumps as executable code and require explicit confirmation that the source is trusted.
  • Display a pre-execution summary of statement categories and prominently identify destructive or administrative operations.
  • Reject or require an additional explicit opt-in for statements such as DROP, TRUNCATE, GRANT, REVOKE, account management, and database-level DDL.
  • Do not parse general SQL by splitting on semicolons. Use a database-aware parser or the database vendor's supported import facility.
  • Run the import transactionally where the database and statement types permit it.
  • On the first failure, roll back and return a nonzero exit status by default.
  • If partial import is intentionally supported, require an explicit --continue-on-error option and report that the result is incomplete.
  • Recognize that some MySQL DDL causes implicit commits; create and restore from a verified backup or import into a staging database before promotion.
  • Use a dedicated account with only the permissions required by the reviewed dump.
  • Close connections in finally blocks or context managers and preserve detailed failure reporting without exposing credentials or sensitive row data.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The supplied script is an analyzer, not an importer. It inspects SQLite and MySQL/MariaDB schemas, reads CSV headers and a few sample rows, and extracts basic table/column info from SQL dump text. This partially aligns with the 'analyzes schemas' and 'parses SQL dumps' portions of the description, but the core declared purpose emphasizes intelligent importing, smart column correlation, and executing transfers into databases. None of those behaviors are implemented here. Therefore the description materially overstates the actual functionality and the primary purpose does not match.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code partially matches the description because it does import CSV data and execute SQL dumps for SQLite and MySQL-compatible databases. However, the declared purpose significantly overstates the behavior: there is no intelligence layer, no schema analysis, no automatic or suggested column correlation, and no direct migration workflow between databases. The code requires a pre-specified mapping JSON and only performs straightforward inserts into an existing table or executes semicolon-split SQL statements. This is a material description-behavior mismatch because several headline capabilities in the description are absent from the implementation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a broad database ingestion and migration tool with parsing, schema analysis, smart correlation, and actual transfer execution across multiple database systems. The supplied code chunk only implements a narrow helper: matching column names by textual similarity from provided lists. Although column mapping is one small supporting aspect of the declared purpose, the code lacks the core advertised behaviors and infrastructure, making the description materially broader than what this code actually does.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly instructs users to execute SQL dumps against target databases without clearly warning that a dump may contain arbitrary destructive statements such as DROP, DELETE, ALTER, privilege changes, triggers, or data-modifying routines. In a database-import skill, this context makes the omission more dangerous because users are primed to treat dump execution as routine, increasing the chance of accidental data loss or execution of untrusted SQL.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The CSV analyzer intentionally includes up to five full data rows in its JSON output, which can expose sensitive content from imported files such as personal data, credentials, tokens, or business records. In this skill context, users are likely to analyze arbitrary third-party or production data during database import workflows, so emitting raw sample rows increases the risk of unintended data disclosure through logs, downstream tools, or agent responses.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code reads a SQL dump and executes each statement directly against SQLite or MySQL, which can create, alter, delete, or overwrite data. Although it prints a success count afterward, there is no confirmation prompt or explicit warning message before performing these potentially irreversible database changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

Accepting the MySQL password via a command-line argument can expose credentials through shell history, process listings, job control logs, and system monitoring tools visible to other local users. In an automation/import skill that may be run on shared developer or server environments, this creates unnecessary credential disclosure risk.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.