Back to skill

Security audit

Museum Data Manager

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real museum database tool, but it gives agents broad, under-scoped database power that could expose credentials or alter data if used carelessly.

Install only if you control the database and can use a dedicated least-privilege account, preferably read-only for normal use. Avoid letting an agent run user-supplied SQL, do not grant GRANT ALL or wildcard-host access, and treat exports as sensitive database dumps. Rotate credentials if this tool has already run in an environment where process arguments may be logged or visible.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
museum.py:69
Finding

SQL Injection Through Unsanitized Museum Command Arguments

Content
View full analysis

Vulnerability Details

File Location: museum.py, lines 69-78, 90-100, and 146-161
Vulnerability Type: SQL injection
Risk Level: High

The structured list, get, and check commands interpolate user-controlled values directly into SQL statements.

python
def list_museums(args):
    """List museums"""
    where_clauses = []
    
    if args.status:
        where_clauses.append(f"status='{args.status}'")
    if args.location:
        where_clauses.append(f"location LIKE '%{args.location}%'")
    
    where_sql = "WHERE " + " AND ".join(where_clauses) if where_clauses else ""
python
def get_museum(args):
    """Get single museum details"""
    query = args.query
    
    # Check if ID (32 char hex) or name
    if len(query) == 32 and all(c in '0123456789abcdef' for c in query.lower()):
        sql = f"SELECT * FROM museums WHERE id='{query}';"
    else:
        sql = f"SELECT * FROM museums WHERE name LIKE '%{query}%' LIMIT 5;"
    
    result = run_sql(sql)
python
def check_data(args):
    """Check data integrity"""
    if args.id:
        # Check single
        sql = f"""
            SELECT 
                name,
                CASE WHEN introduction IS NULL OR introduction = '' THEN 'missing' ELSE 'ok' END as intro,
                CASE WHEN top3_artifacts IS NULL OR top3_artifacts = '[]' OR top3_artifacts = '["to be supplemented"]' THEN 'missing' ELSE 'ok' END as artifacts,
                CASE WHEN building_photo IS NULL THEN 'missing' ELSE 'ok' END as photo,
                status
            FROM museums 
            WHERE id='{args.id}';
        """

Technical Analysis

Values from args.status, args.location, args.query, and args.id are inserted into SQL using Python f-strings. No parameter binding, escaping, or adequate validation is performed before the resulting statement is passed to mycli -e.

The hexa ...[truncated 1574 chars]

Remediation
View remediation

Remediation Suggestions

  • Replace mycli subprocess execution with a maintained MySQL library that supports parameterized statements, such as mysql-connector-python or PyMySQL.
  • Bind every user-controlled value as a query parameter rather than performing manual quoting or string interpolation.
  • Restrict status to an explicit allowlist such as complete, partial, and pending.
  • Validate all IDs against a strict expression equivalent to ^[0-9a-fA-F]{32}$.
  • Enforce safe numeric bounds for limit and offset, including nonnegative offsets and a reasonable maximum result limit.
  • Run the structured read commands through a database account with SELECT permission only.
  • Add regression tests containing quotes, SQL comments, escape characters, and attempted boolean or stacked-statement payloads.

T09 · Insecure Skill Coding Practices

Error
Location
museum.py:44
Finding

Database Password Exposed in Child Process Arguments

Content
View full analysis

Vulnerability Details

File Location: museum.py, lines 44-53
Vulnerability Type: Sensitive credential exposure through process arguments
Risk Level: High

The database password is copied from MYSQL_PSWD into the argument vector used to launch mycli.

python
cmd = [
    mycli_cmd,
    '-h', DB_CONFIG['host'],
    '-u', DB_CONFIG['user'],
    '-p', DB_CONFIG['password'],
    DB_CONFIG['database'],
    '-e', sql
]

try:
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

Technical Analysis

Supplying a secret as a command-line argument makes it part of the child process argument vector. Depending on the operating system, process isolation settings, monitoring configuration, and logging infrastructure, command arguments may be visible through process inspection interfaces, diagnostic tools, audit logs, crash reports, or telemetry.

Using subprocess.run with an argument list avoids shell expansion, but it does not protect secrets contained in that list. The issue is particularly significant because this password may belong to an account with broad permissions, as the Skill documentation permits or recommends broad database grants.

Attack Path

  1. The Skill reads the database password from MYSQL_PSWD.
  2. run_sql adds the password to the mycli command-line argument vector.
  3. The mycli process remains observable while the database request is running.
  4. A local user, process-monitoring service, audit collector, or logging component captures the process arguments.
  5. The observer extracts the database password.
  6. The exposed credential is reused to connect to the configured MySQL host, subject to network reachability and account restrictions.

Impact Assessment

Exposure compromises the confidentiality of the configured database credential. An attacker who can reach the MySQL service may obtain every permission assigned to that account, ...[truncated 195 chars]

Remediation
View remediation

Remediation Suggestions

  • Prefer a native MySQL driver and pass credentials through its connection API instead of invoking a command-line client.
  • If a CLI is unavoidable, use a short-lived MySQL option file with restrictive permissions such as 0600; ensure secure creation and cleanup even when execution fails.
  • Do not place passwords in command arguments, command strings, logs, exception messages, or exported diagnostics.
  • Use a dedicated database account with only the permissions required by the invoked command.
  • Rotate any credential that may already have been exposed through process telemetry or logs.
  • Require encrypted MySQL transport and validate the database server certificate when connecting to a non-local host.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
museum.py:239
Finding

Unrestricted SQL Execution and Excessive Database Privilege Guidance

Content
View full analysis

Vulnerability Details

File Location: museum.py, lines 239-243 and 301-303; SKILL.md, lines 106-112 and 299-302
Vulnerability Type: Missing authorization boundary and violation of least privilege
Risk Level: High

The query command forwards arbitrary SQL directly to the database:

python
def query_sql(args):
    """Execute custom SQL"""
    result = run_sql(args.sql)
    if result:
        print(result)
python
# query command
query_parser = subparsers.add_parser('query', help='Execute SQL query')
query_parser.add_argument('sql', help='SQL statement')
query_parser.set_defaults(func=query_sql)

The Skill documentation declares unrestricted statements and recommends permissions broader than the primary read, check, statistics, and export operations require:

markdown
### 6. Custom Query

```bash
museum query "SQL_STATEMENT"

Example:
  museum query "SELECT name, location FROM museums WHERE status='complete';"
text

```markdown
Ensure your MySQL user has:
- SELECT, INSERT, UPDATE, DELETE on the database
- Or full privileges: `GRANT ALL ON museumcheck.* TO 'user'@'%';`

Technical Analysis

The method named query_sql performs no parsing, statement allowlisting, read-only enforcement, confirmation, or authorization check. Any SQL accepted by the database client can therefore be attempted with the configured account's privileges.

Custom SQL is documented functionality, so the presence of the feature is not hidden. However, combining it with GRANT ALL and an account permitted from the wildcard host % exceeds the minimum privileges necessary for the Skill's principal listing, statistics, integrity-checking, and export features. It also amplifies the consequences of Agent prompt manipulation, accidental commands, SQL injection elsewhere in the Skill, and unauthorized local invocation.

Attack Path

  1. A caller able to invoke the Skill supplies ...[truncated 1107 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove the unrestricted query command unless arbitrary SQL is essential to the declared workflow.
  • If retained, make it explicitly opt-in and restrict it to a read-only database account.
  • Enforce single-statement, read-only queries using a reliable SQL parser; a simple prefix check is insufficient because of comments, common table expressions, and dialect-specific syntax.
  • Separate credentials by operation: use a SELECT-only identity for listing, statistics, checks, exports, and custom reads, and narrowly scoped write identities only for explicit management operations.
  • Remove the recommendation for GRANT ALL ON museumcheck.* TO 'user'@'%'.
  • Restrict allowed connection origins to the specific host or network that runs the Skill.
  • Add explicit authorization and user confirmation before any operation capable of changing data.
  • Maintain database backups and audit logging for all write or schema-changing operations.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (11)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

Documenting a raw SQL execution feature without any warning or restriction is dangerous because users or agents may run arbitrary statements that modify, drop, or exfiltrate database contents. In this skill's context, the presence of broad database privileges, examples of direct SQL usage, and automation-oriented workflow integration make accidental or unsafe execution more likely and more damaging.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README explicitly advertises museum query for direct SQL execution and museum export for data extraction, but provides no warnings, safeguards, or usage constraints. In a skill that connects to a live MySQL database via user-supplied credentials, this normalizes potentially destructive queries and bulk data exfiltration, increasing the risk of accidental damage, privacy violations, or misuse.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill advertises database management features including export, integrity operations, and auto-creation of tables, but does not clearly warn users that the tool can write to or alter persistent data. In an agent context, this increases the chance that a user or autonomous workflow invokes state-changing operations under the assumption the skill is read-only, causing accidental schema changes, data overwrites, or destructive actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest describes the skill and command in very broad terms such as 'reading, querying, and managing museum database operations' and 'Museum data operations tool' without clearly limiting scope or intended use. In agent ecosystems, vague descriptions can cause over-invocation or invocation in contexts involving sensitive database actions, increasing the chance of unintended data access or modification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code accesses sensitive configuration, including the database password from MYSQL_PSWD, but there is no visible warning in user-facing output or comments that credentials from the environment will be consumed. For a code file, sensitive environment-variable access should have some disclosure unless it is clearly communicated elsewhere in the skill description, which is not present in this file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The run_sql function invokes the external mycli executable through subprocess.run, which is a safety-relevant operation for code files. Although errors are printed, there is no explicit disclosure in help text or comments that the skill will launch an external program to execute database commands.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · museum.py (reported line 56)May include surrounding context.

python
]
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode != 0:
            print(f"Error: {result.stderr}", file=sys.stderr)
            return None

Tainted flow: 'cmd' from os.environ.get (line 46, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
94% confidence
Finding

The subprocess command is partially derived from environment-controlled values, most importantly the executable selected by get_mycli_path, which returns the bare string 'mycli' and relies on PATH resolution. An attacker who can influence the environment or PATH could cause execution of a malicious binary or redirect database connections with attacker-supplied host/user/database values, leading to code execution or unauthorized data access.

Content

Scanner excerpt · museum.py (reported line 56)May include surrounding context.

python
]
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode != 0:
            print(f"Error: {result.stderr}", file=sys.stderr)
            return None

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The export command writes full database contents to an arbitrary user-supplied file path with no path restrictions or safety checks. In an agent or automation context, this can enable sensitive data exfiltration, overwriting of important files writable by the process, or accidental leakage to insecure locations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The query command exposes unrestricted custom SQL execution to the user, allowing arbitrary reads and modifications against the configured database. In the context of an agent skill presented as a general museum operations tool, this greatly expands the blast radius to data exfiltration, destructive writes, schema changes, or operational sabotage if the command is invoked by an untrusted or over-privileged caller.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The docstring Check data integrity and CLI help text suggest an integrity-checking capability, which normally implies validation of consistency, constraints, or corruption. In practice, this function just queries for missing introduction, artifacts, and photo fields, so the documented intent overstates what the code actually does.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.