Back to skill

Security audit

memory-system

Security checks for vulnerabilities and agentic risk

Overview

This long-term memory skill is purpose-aligned, but it stores and searches sensitive persistent memory with unsafe SQL construction, broad retrieval behavior, and limited user control.

Review this before installing if your memory store may contain private, business, or credential-adjacent information. Use only with a dedicated low-privilege PostgreSQL role and trusted local memory files, and avoid running the scripts until SQL parameterization, result filtering, retention/deletion controls, and clearer disclosure of the local embedding service are added.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/memory_search.py:12
Finding

SQL Injection Through the Category Command-Line Argument

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_search.py, lines 12–19
Vulnerability Type: SQL injection caused by direct interpolation of untrusted command-line input
Risk Level: High

Vulnerable Code

python
def search_structured(query, category=None):
    """搜索结构化记忆"""
    where = ""
    if category:
        where = f"WHERE category = '{category}'"
    
    cmd = [
        "psql", "-h", "localhost", "-p", "5432",
        "-U", "damien", "-d", "postgres",
        "-c", f"SELECT title, content FROM memory_structured {where};"
    ]

Technical Analysis

The value supplied through the --category command-line option is inserted directly into a PostgreSQL statement without parameter binding, escaping, or allowlist validation. Although subprocess.run receives an argument list and therefore does not invoke a shell, that protection only prevents shell injection. It does not prevent injection into the SQL passed to psql -c.

PostgreSQL accepts multiple statements in the command string provided to psql -c. An attacker can terminate the category string, append another SQL statement, and comment out the remainder. For example, a category resembling:

text
x'; DELETE FROM memory_structured; --

produces a command containing:

sql
SELECT title, content
FROM memory_structured
WHERE category = 'x';
DELETE FROM memory_structured;
--';

Exploitation requires the ability to invoke this script or influence its command-line arguments.

Attack Path

  1. An attacker obtains the ability to invoke memory_search.py directly or control arguments passed to it by another process.
  2. The attacker supplies a crafted value after --category.
  3. search_structured() embeds the value into the SQL statement without validation.
  4. The statement is passed to the local psql client through its -c option.
  5. PostgreSQL executes the injected statements using ...[truncated 844 chars]
Remediation
View remediation

Remediation Suggestions

  • Replace the psql -c subprocess call with a PostgreSQL driver such as psycopg.

  • Use a parameterized query so the category is always handled as data:

    python
    import psycopg
    
    ALLOWED_CATEGORIES = {
        "goals",
        "decisions",
        "status",
        "preferences",
        "reference",
        "archived",
    }
    
    def search_structured(query, category=None):
        if category is not None and category not in ALLOWED_CATEGORIES:
            raise ValueError("Unsupported category")
    
        sql = "SELECT title, content FROM memory_structured"
        params = ()
    
        if category is not None:
            sql += " WHERE category = %s"
            params = (category,)
    
        with psycopg.connect(
            host="localhost",
            port=5432,
            user="memory_reader",
            dbname="postgres",
        ) as connection:
            with connection.cursor() as cursor:
                cursor.execute(sql, params)
                return cursor.fetchall()
    
  • Validate category names against a fixed allowlist even after parameterization.

  • Use a dedicated read-only database role for search operations.

  • Revoke unnecessary privileges from that role, including write, schema modification, and access to unrelated tables.

  • Add tests containing quotes, semicolons, comment markers, and Unicode edge cases to verify that input cannot change query structure.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/memory_manager.py:36
Finding

SQL Injection During Memory File Archival

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_manager.py, lines 36–51
Vulnerability Type: SQL injection through an untrusted filename or file content
Risk Level: High

Vulnerable Code

python
def import_to_database(filepath):
    """将文件内容导入到数据库"""
    with open(filepath, 'r', encoding='utf-8') as f:
        content = f.read()
    
    filename = os.path.basename(filepath)
    
    # 使用 psql 导入
    cmd = [
        "psql", "-h", DB_CONFIG["host"], "-p", str(DB_CONFIG["port"]),
        "-U", DB_CONFIG["user"], "-d", DB_CONFIG["database"],
        "-c", f"INSERT INTO memory_structured (category, title, content) VALUES ('archived', '{filename}', {repr(content)});"
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode == 0

Technical Analysis

The filename and complete file contents are concatenated into an SQL statement. Neither value is bound as an SQL parameter. Python's repr() function is not a PostgreSQL escaping or serialization mechanism and must not be used to construct SQL literals.

A filename containing a quote can terminate the title literal and introduce additional values or statements. Because the script accepts Markdown files discovered beneath the memory directory, an attacker who can create or rename an eligible file can construct a name resembling:

text
x', 'y'); DELETE FROM memory_structured; --.md

The resulting SQL begins with the equivalent of:

sql
INSERT INTO memory_structured (category, title, content)
VALUES ('archived', 'x', 'y');
DELETE FROM memory_structured;
--.md', '...');

File content is also unsafe. Quotes, backslashes, encoding behavior, and PostgreSQL string-literal settings can cause malformed statements or potentially alter SQL structure. Parameter binding is required for both the filename and content.

Attack Path

  1. An attacker gains write or ...[truncated 1292 chars]
Remediation
View remediation

Remediation Suggestions

  • Use a PostgreSQL client library with bound parameters instead of constructing a psql -c command:

    python
    import os
    import psycopg
    
    def import_to_database(filepath):
        with open(filepath, "r", encoding="utf-8") as file:
            content = file.read()
    
        filename = os.path.basename(filepath)
    
        with psycopg.connect(
            host=DB_CONFIG["host"],
            port=DB_CONFIG["port"],
            user="memory_archiver",
            dbname=DB_CONFIG["database"],
        ) as connection:
            with connection.cursor() as cursor:
                cursor.execute(
                    """
                    INSERT INTO memory_structured (category, title, content)
                    VALUES (%s, %s, %s)
                    """,
                    ("archived", filename, content),
                )
    
        return True
    
  • Never use repr(), manual quote replacement, or string formatting as an SQL escaping mechanism.

  • Run archival under a dedicated least-privileged database role with only the required INSERT permission on memory_structured.

  • Restrict filesystem permissions on the memory directory so untrusted local users and processes cannot create or rename archive candidates.

  • Consider rejecting unexpected filenames and symbolic links, and confirm resolved paths remain under the intended memory directory.

  • Apply maximum file-size limits before reading entire files into memory.

  • Add regression tests using filenames and contents containing single quotes, semicolons, SQL comments, backslashes, and newline characters.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The documented purpose promises memory management and semantic search, but the finding indicates missing CRUD/search behavior and undeclared dependencies on local PostgreSQL and an embeddings endpoint. This can mislead operators into exposing local services and user data to a skill whose actual behavior and external touchpoints are not transparently declared.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The documented purpose promises memory management and semantic search, but the finding indicates missing CRUD/search behavior and undeclared dependencies on local PostgreSQL and an embeddings endpoint. This can mislead operators into exposing local services and user data to a skill whose actual behavior and external touchpoints are not transparently declared.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill exposes shell and file-reading capabilities in its documented usage but does not declare any tool scope or permission boundaries. That omission increases the chance an agent invokes filesystem or shell operations unexpectedly, especially because the skill also includes direct database command examples and local script execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill persistently stores user content in local files and PostgreSQL, but the description does not clearly warn users that updates and archival are durable. In a memory system, this context makes the issue more dangerous because users are especially likely to provide sensitive preferences, goals, or personal context without realizing it will be retained beyond the session.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The schema and surrounding documentation indicate session persistence of structured memory across time. In a long-term memory skill, persistent retention is inherently sensitive because it can accumulate personal, strategic, or confidential data and later expose it through searches, scripts, or local database access if not carefully governed.

Content

Scanner excerpt · SKILL.md (reported line 55)May include surrounding context.

memory_structured (结构化)

sql
CREATE TABLE memory_structured (
    id SERIAL PRIMARY KEY,
    category VARCHAR(50),  -- goals, decisions, status, preferences, reference
    title VARCHAR(200),

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The header comment states that files older than 7 days are 'moved' to PostgreSQL. In practice, the script reads file contents, inserts them into the database, and leaves the original files in place because deletion is only mentioned as an optional commented-out step.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script reads archived memory files and sends their contents into a PostgreSQL database without any explicit consent flow, warning, or boundary documentation. In a long-term memory system, these files may contain sensitive user context, preferences, or decisions, so silent transfer to another storage backend increases privacy and data-governance risk.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The manifest describes a long-term memory system providing structured memory, vector memory, and semantic search. While storing archived memory in a database is consistent with that purpose, spawning an external psql process is a separate execution capability that is not an obvious requirement of memory management and expands what the skill can do beyond normal in-process persistence.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The subprocess call itself is not the core issue, but here it is used to invoke psql with an SQL statement built from untrusted file-derived data (filename and content). Because shelling out to an external database client bypasses safer parameterization patterns and embeds attacker-controllable data directly into SQL, this creates a realistic SQL injection and data-handling risk.

Content

Scanner excerpt · scripts/memory_manager.py (reported line 54)May include surrounding context.

python
"-c", f"INSERT INTO memory_structured (category, title, content) VALUES ('archived', '{filename}', {repr(content)});"
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode == 0

def main():

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The structured search function ignores the user query and returns all memories, optionally filtered only by category. In a long-term memory system, this defeats access minimization and can expose unrelated sensitive stored data whenever the tool is used, making the skill context more dangerous because memory stores commonly contain personal preferences, past decisions, and private context.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The manifest frames this skill as a long-term memory management and semantic search component, but the implementation shells out to external binaries instead of using direct database or HTTP client libraries. Spawning subprocesses broadens operational capability beyond what is necessary for the stated purpose and is not declared in the skill description.

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 · scripts/memory_search.py (reported line 24)May include surrounding context.

python
"-c", f"SELECT title, content FROM memory_structured {where};"
    ]
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.stdout

def search_vector(query):

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This Python code executes external commands to query local PostgreSQL databases and sends the user's query to a local HTTP embedding service, but it provides no warning in comments, docstrings, or user-facing output that these actions will occur. Because the script transmits user input to another service and invokes subprocesses, it should disclose these behaviors explicitly rather than only printing search results.

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 · scripts/memory_search.py (reported line 35)May include surrounding context.

python
"-d", json.dumps({"model": "qllama/bge-large-zh-v1.5", "prompt": query})
    ]
    
    result = subprocess.run(embed_cmd, capture_output=True, text=True)
    try:
        emb = json.loads(result.stdout)["embedding"]
        emb_str = "[" + ",".join(map(str, emb)) + "]"

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 · scripts/memory_search.py (reported line 47)May include surrounding context.

python
"-c", f"SELECT content, (1 - (embedding <=> '{emb_str}')) AS similarity FROM longterm_memory ORDER BY embedding <=> '{emb_str}' LIMIT 3;"
        ]
        
        result = subprocess.run(search_cmd, capture_output=True, text=True)
        return result.stdout
    except:
        return "向量搜索失败"

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The file specifies '中文 embedding (bge-large-zh)', which indicates a Chinese-language constraint in the skill's memory search behavior. The documentation does not offer a language choice or explain that the skill is intentionally limited to Chinese for a justified regional or compliance reason.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The module docstring presents the skill description exclusively in Chinese, which can constitute a language-policy issue when no opt-in or locale justification is provided. There is no indication that this skill is intended only for a Chinese-speaking or region-specific context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The natural-language text in the module docstring, function docstrings, status messages, and example usage is entirely in Chinese, which imposes a specific language on users without any opt-in or alternative. Under the policy, language constraints should be optional or clearly justified.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.