Back to skill

Security audit

sqlite-reader

Security checks for vulnerabilities and agentic risk

Overview

This skill is a useful SQLite reader, but its query script can also change or damage writable databases despite being presented as read-oriented.

Install only if you are comfortable with a helper that can run arbitrary SQL against databases you point it at. Use it on database copies or read-only files, and do not let untrusted instructions provide SQL queries or output paths.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/read_sqlite.py:44
Finding

Unrestricted SQL Execution Against a Writable SQLite Database

Content
View full analysis

Vulnerability Details

File Location: scripts/read_sqlite.py, lines 44–48, 97, and 125
Vulnerability Type: Unrestricted state-changing SQL execution in a purported read-only utility
Risk Level: High

Vulnerable Code

python
def execute_query(conn, query, limit=None):
    """Execute a SQL query and return results"""
    cursor = conn.cursor()
    try:
        cursor.execute(query)
python
conn = sqlite3.connect(args.database)
python
elif args.query:
    result = execute_query(conn, args.query, args.limit)

Technical Analysis

The Skill is documented as a SQLite reader that supports SELECT queries, but the implementation passes the entire user- or agent-supplied --query value directly to cursor.execute() without enforcing a read-only SQL policy.

The database is opened through a normal sqlite3.connect(args.database) call rather than a read-only URI. Subject to filesystem permissions, this grants the connection write access. As a result, the query interface can execute state-changing statements such as INSERT, UPDATE, DELETE, DROP, and writable PRAGMA operations. The --limit option only controls how many result rows are fetched; it does not limit or neutralize SQL side effects.

Although cursor.execute() generally rejects multiple statements in one call, this does not mitigate the issue because a single destructive statement is sufficient to alter or destroy the database. Because the documentation specifically identifies OpenClaw's main.sqlite memory database as an intended target, the affected data may include sessions, messages, user details, and transcript paths.

Attack Path

  1. A user or untrusted instruction causes the Skill to inspect a writable SQLite database.
  2. The agent passes a state-changing statement through the --query argument, for example:
    sql
    DELETE FROM messages;
    
  3. The script opens the selected database using a writable SQLite connection.
  4. `cursor.execu ...[truncated 994 chars]
Remediation
View remediation

Remediation Suggestions

  1. Open the database in read-only mode using a SQLite URI:

    python
    from pathlib import Path
    
    database_uri = Path(args.database).resolve().as_uri() + "?mode=ro"
    conn = sqlite3.connect(database_uri, uri=True)
    
  2. Enforce read-only operations with SQLite's authorizer API. Deny insert, update, delete, schema modification, transaction manipulation, attach/detach, and unsafe PRAGMA actions:

    python
    def authorize(action, arg1, arg2, database_name, trigger_name):
        denied = {
            sqlite3.SQLITE_INSERT,
            sqlite3.SQLITE_UPDATE,
            sqlite3.SQLITE_DELETE,
            sqlite3.SQLITE_CREATE_TABLE,
            sqlite3.SQLITE_DROP_TABLE,
            sqlite3.SQLITE_ALTER_TABLE,
            sqlite3.SQLITE_ATTACH,
            sqlite3.SQLITE_DETACH,
        }
        return sqlite3.SQLITE_DENY if action in denied else sqlite3.SQLITE_OK
    
    conn.set_authorizer(authorize)
    
  3. Permit only read-only statements. Do not rely solely on a textual prefix check, because comments, PRAGMAs, and complex SQL syntax can bypass simplistic validation. Use the read-only connection and authorizer as the primary controls.

  4. Reject queries that return no result-set metadata and handle cursor.description is None safely.

  5. Add explicit confirmation and a separate administrative mode if write functionality is ever intentionally introduced. It should not share the reader's default execution path.

  6. Add security tests confirming that INSERT, UPDATE, DELETE, DROP, ALTER, ATTACH, DETACH, and writable PRAGMA statements are denied while legitimate SELECT and schema-inspection operations remain available.

  7. Update the documentation to accurately state and guarantee that arbitrary queries are constrained to read-only behavior.

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 (3)

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script presents itself as a SQLite reader, but execute_query passes arbitrary user-supplied SQL directly to cursor.execute(query) without restricting statements to read-only operations. In this skill context, that makes the capability more dangerous because a user or calling agent expecting safe inspection of .sqlite files could instead modify or delete data, create objects, or trigger side effects against the database file.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises only SQLite read/query functionality, but its content includes creating a Python script and exporting data to CSV/JSON, which implies file-write capability without any declared tool scope or permission boundary. This mismatch can enable unintended filesystem modification or data exfiltration paths, especially if an agent auto-generates and runs helper scripts based on the skill instructions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest focuses on reading/querying SQLite files and inspecting data, but this function writes query results out to an arbitrary filesystem path. While related to extraction, it extends behavior from database reading into local file creation, which is not stated in the manifest description.

Content

No source excerpt is available for this finding.