Back to skill

Security audit

本地知识库

Security checks for vulnerabilities and agentic risk

Overview

This is a local knowledge-base skill, but it stores sensitive content persistently and can delete or reuse memory without enough safeguards.

Install only if you are comfortable with a local, persistent, plaintext knowledge base. Do not store passwords, tokens, keys, or private personal data in it, and treat deletion and automatic context reuse carefully until confirmation, redaction, and safer scoping are added.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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)

T02 · Agent Memory Poisoning

Error
Location
src/index.py:302
Finding

Persistent Agent Memory Poisoning Through Untrusted Context Injection

Content
View full analysis

Vulnerability Details

File Location: src/index.py:302-365
Vulnerability Type: Persistent prompt injection through stored memory
Risk Level: High

Vulnerable Code

python
def kb_context(self, current_input: str, max_length: int = 4000) -> Dict:
    """为当前对话生成知识库上下文"""
    # 提取关键词
    keywords = self._extract_keywords(current_input)

    if not keywords:
        return {
            "success": True,
            "context": "",
            "relevant_entries": 0,
            "injected": False,
            "provider": "knowledge-spider"
        }

    # 用关键词搜索
    all_results = []
    for kw in keywords[:2]:
        results = self._semantic_search(kw, limit=3)
        all_results.extend(results)

    # 去重并排序
    seen_ids = set()
    unique_results = []
    for r in all_results:
        if r['id'] not in seen_ids:
            seen_ids.add(r['id'])
            unique_results.append(r)

    unique_results = unique_results[:5]

    if not unique_results:
        return {
            "success": True,
            "context": "",
            "relevant_entries": 0,
            "injected": False,
            "provider": "knowledge-spider"
        }

    context_parts = [
        "## 相关知识库 (Relevant Knowledge)",
        "以下是从历史记忆中提取的相关信息,请优先考虑:",
        ""
    ]

    for i, entry in enumerate(unique_results, 1):
        date = entry['timestamp'][:10] if entry['timestamp'] else "未知"
        emoji = {
            'preference': '⭐',
            'fact': '📖',
            'task': '✅',
            'important': '🔴',
            'general': '📝'
        }.get(entry['category'], '📝')

        context_parts.append(
            f"{i}. {emoji} [{entry['category']}] {date}: {entry['content'][:120]}"
        )

    context_parts.append("")
    context_parts.append("## 当前对话")

    context = "\n".join(context_parts)

    if len(context) > max_length:
        context = context[:max_length-3] + "..."

Technical Analysis

The memory_store interface acc ...[truncated 2127 chars]

Remediation
View remediation

Remediation Suggestions

  1. Treat every stored record as untrusted data rather than an instruction.
  2. Remove wording that tells the agent to prioritize retrieved content.
  3. Place retrieved records inside an explicit data-only boundary and state that instructions within the records must not be followed.
  4. Detect instruction-like content during storage and retrieval, and either reject it, quarantine it, or require explicit user approval before context injection.
  5. Preserve provenance, including who stored the record, when it was stored, and whether it has been reviewed.
  6. Separate trusted administrator-authored memories from ordinary user-authored content.
  7. Require explicit confirmation before injecting sensitive or instruction-like records into a new session.
  8. Add tests using payloads such as “ignore previous instructions” and verify that they are represented only as quoted data.

T09 · Insecure Skill Coding Practices

Error
Location
src/index.py:128
Finding

Passwords, Tokens, and Other Secrets Are Stored in Plaintext

Content
View full analysis

Vulnerability Details

File Location: src/index.py:128-143, src/index.py:508-514, and SKILL.md:43
Vulnerability Type: Plaintext storage of sensitive credentials
Risk Level: High

Vulnerable Code

python
cursor.execute('''
    INSERT INTO memories
    (id, content, category, source, timestamp, access_count, metadata)
    VALUES (?, ?, ?, ?, ?, ?, ?)
''', (
    entry_id,
    content,
    detected_category,
    source,
    datetime.now().isoformat(),
    0,
    json.dumps(metadata or {}, ensure_ascii=False)
))
python
important_patterns = [
    r'重要|关键|紧急|务必|必须|密码|密钥|token|secret',
    r'important|critical|urgent|password|key|secret|must',
    r'别忘了|切记|注意'
]
for p in important_patterns:
    if re.search(p, content_lower):
        return 'important'

The declared category table also explicitly identifies passwords and keys as important information intended for storage:

markdown
| important | 重要、关键、密码、密钥 | 关键信息 |

Technical Analysis

The implementation explicitly detects words associated with passwords, keys, tokens, and secrets, but only assigns the record to the important category. The complete original content is then inserted into the content column of a normal SQLite database.

No encryption, field-level protection, redaction, restrictive permission setup, operating-system credential-store integration, expiration policy, or secret-specific access control is present. Categorizing a credential as important does not provide confidentiality.

The database is stored by default at:

text
~/.openclaw/workspace/skills/knowledge-spider/data/memory.db

The code creates directories with default process permissions and does not explicitly restrict the database file to its owner.

Attack Path

  1. A user asks the Skill to remember a password, API token, private key, or similar credential.
  2. The classifier detects a secret-related keyword and labels the record as important.
  3. memory_store writes ...[truncated 871 chars]
Remediation
View remediation

Remediation Suggestions

  1. Refuse to store passwords, private keys, access tokens, recovery codes, and similar credentials by default.
  2. Add secret-pattern detection that redacts or blocks likely credentials instead of merely classifying them as important.
  3. If credential storage is an explicit requirement, use an operating-system-backed secret manager rather than SQLite.
  4. Encrypt sensitive fields using keys that are not stored alongside the database.
  5. Create the data directory and database with owner-only permissions and verify existing permissions during startup.
  6. Add record expiration and secure-deletion policies for sensitive data.
  7. Avoid returning secret values in previews, search results, statistics, logs, or generated contexts.
  8. Update SKILL.md to warn users not to place credentials in the general knowledge base.

T09 · Insecure Skill Coding Practices

Error
Location
src/index.py:206
Finding

Missing Delete Target Causes Unconfirmed Deletion of All Memories

Content
View full analysis

Vulnerability Details

File Location: src/index.py:206-224 and src/index.py:792-794
Vulnerability Type: Unsafe destructive operation caused by an empty target
Risk Level: High

Vulnerable Code

python
def memory_forget(self, target: str) -> Dict:
    """
    OpenClaw 标准接口:删除记忆
    完全兼容原 memory-core 的 memory_forget 工具
    """
    conn = sqlite3.connect(str(self.db_path))
    cursor = conn.cursor()

    try:
        # 支持 ID 精确删除或内容模糊删除
        cursor.execute(
            "DELETE FROM memories WHERE id = ? OR content LIKE ?",
            (target, f"%{target}%")
        )
        deleted = cursor.rowcount

        # 清理使用历史
        if deleted > 0:
            cursor.execute("DELETE FROM usage_log WHERE memory_id = ?", (target,))

        conn.commit()

The action router supplies an empty string when the target parameter is absent:

python
"memory_forget": lambda: kb.memory_forget(
    params.get("target", "")
),

Technical Analysis

When target is missing, the router passes an empty string to memory_forget. The fuzzy-search expression then becomes:

sql
content LIKE '%%'

That predicate matches every non-null content value. As a result, a malformed or incomplete memory_forget request deletes all records and immediately commits the transaction.

The implementation does not reject empty or whitespace-only targets, preview matching records, cap the number of deletions, request confirmation, or implement soft deletion. This also contradicts the declared behavior in SKILL.md, which states that deletion must be confirmed first.

Even with a non-empty target, unrestricted substring matching can delete multiple unintended records.

Attack Path

  1. A caller invokes the memory_forget action without a target parameter, or intent extraction produces an empty target.
  2. The router substitutes the default value "".
  3. memory_forget constructs the pattern %%.
  4. SQLite matches every row in the `memor ...[truncated 780 chars]
Remediation
View remediation

Remediation Suggestions

  1. Reject missing, empty, or whitespace-only deletion targets before executing SQL.
  2. Require an exact memory ID for normal deletion operations.
  3. If fuzzy deletion is needed, first return matching records and require explicit confirmation using a short-lived confirmation token.
  4. Refuse multi-record deletion unless the caller explicitly requests and confirms a bulk operation.
  5. Use a soft-delete column or recovery table so accidental deletions can be restored.
  6. Perform destructive operations inside a transaction that is committed only after all validation and confirmation checks succeed.
  7. Add tests proving that omitted, empty, whitespace-only, wildcard-like, and very broad targets cannot delete records.
  8. Delete associated usage_log entries using the actual IDs of deleted records rather than only the caller-provided target.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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 (10)

Ssd 3

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

kb_context builds prompt context by directly embedding prior stored memory snippets and explicitly tells the model to 'prioritize' them, without checking whether the historical content is sensitive, relevant, or within the same user/session scope. This can leak secrets, personal data, or prior confidential instructions into unrelated future interactions and amplifies prompt-injection risk from stored content.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill declares broad mandatory triggers around saving, querying, deleting, and counting knowledge-base content whenever certain phrases appear. Because the conditions are not tightly scoped to explicit user confirmation or a dedicated command format, normal conversation can unintentionally invoke persistence or retrieval actions, creating privacy and integrity risks for stored user data.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The deletion trigger includes ambiguous natural-language phrases like '忘掉这条记录' that may overlap with ordinary conversational requests rather than a deliberate delete operation. This raises the risk of accidental data loss if the assistant interprets informal language as an instruction to remove records.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The trigger list contains generic phrases such as '知识库中' and '我的知识库' that can match normal conversation rather than an explicit invocation. This can cause unintended activation of a skill that stores, queries, deletes, or summarizes user knowledge, increasing the risk of privacy-affecting actions being invoked without clear user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The natural-language description states 完全替代 memory-core 插件, and the file consistently presents user-facing messages and documentation in Chinese without offering a language choice. This indicates an implicit locale constraint that is not presented as optional or justified as region-specific.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill persistently stores arbitrary user-provided content, including potentially sensitive material, with no sensitivity classification, consent boundary, retention policy, or scope enforcement. In a shared or long-lived agent environment, this creates a real risk of retaining secrets or personal data that may later be surfaced outside the original context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill exposes destructive deletion through memory_forget and deletes either by exact ID or broad content substring without any confirmation, authorization, or safety guard. In an agent setting, ambiguous or manipulated instructions could trigger irreversible loss of stored memories, including legitimate user data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

All user-facing instructions and examples are presented only in Chinese, and the file does not indicate that language choice is optional or region-specific. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The natural-language description forces a single language presentation, which may violate language or locale choice expectations when no user opt-in or regional justification is provided. There is no indication that the skill is intentionally limited to a Chinese-language environment.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The docstring at L382 says '语义搜索 + 优先级排序', which implies semantic retrieval, but the implementation at L387-L398 uses content LIKE ? and only later computes a simple Jaccard overlap on whitespace-split words at L424-L439. This is an active mismatch between the documented intent and the actual behavior, not merely an omitted implementation detail.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.