Back to skill

Security audit

zwjh-skill

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real local memory skill, but it exposes persistent memories to other tools without enforcing the access controls it promises.

Review carefully before installing. Avoid enabling the MCP server for lower-trust skills until manifest and namespace checks are enforced, and avoid the web UI with untrusted deposited content until stored values are rendered safely. Treat Baidu backup, external LLM adapters, CDN loading, and model/tokenizer downloads as separate data-sharing or network trust decisions.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/mcp_server.py:447
Finding

MCP Memory Operations Bypass HMAC Authorization and Namespace Isolation

Content
View full analysis

Vulnerability Details

File Location: scripts/mcp_server.py:177-187, scripts/mcp_server.py:326-419, scripts/mcp_server.py:447-460, scripts/store.py:273-305, scripts/memory_access.py:174-203
Vulnerability Type: Missing authorization and namespace enforcement
Risk Level: High

Complete Code Snippets

The deposit handler accepts a caller-selected namespace and writes to it without validating a signed manifest:

python
def _handle_zwjh_deposit(arguments: dict) -> dict:
    """Process a zwjh_deposit tool call."""
    text = arguments.get("text", "")
    if not text:
        return _tool_result("Error: missing text parameter", is_error=True)
    source = arguments.get("source", "conversation")
    namespace = arguments.get("namespace", "public")

    r = deposit.deposit_text(text, source=source, namespace=namespace)

All MCP tool calls are dispatched directly to their handlers without an authorization check:

python
def _handle_tools_call(request_id: Any, params: dict) -> dict:
    """Process a tools/call request."""
    tool_name = params.get("name", "")
    arguments = params.get("arguments", {})

    handler = TOOL_HANDLERS.get(tool_name)
    if not handler:
        return _rpc_error(request_id, -32601, "Unknown tool: %s" % tool_name)

    try:
        result = handler(arguments)
        return _rpc_result(request_id, result)
    except Exception as e:
        return _rpc_error(request_id, ERR_INTERNAL, "%s" % str(e))

The export handler reads memories without a namespace restriction:

python
rows = store.list_memories(
    day_from=day_from,
    day_to=day_to,
    limit=100000,
    offset=0,
)

The underlying storage functions likewise query every namespace:

python
def list_memories(day_from: str | None = None, day_to: str | None = None,
                  limit: int = 200, offset: int = 0) -> list[dict]:
    conn = get_conn()
    sql = "SELECT * FROM memories WHERE 1=1"
    args: list = []
    if day_from:
     
...[truncated 3638 chars]
Remediation
View remediation

Remediation Suggestions

  1. Add a signed manifest and explicit namespace to every namespace-sensitive MCP tool schema.
  2. Before dispatching an operation, call memory_access.check_namespace_permission() with the required permission:
    • read for query, timeline, graph lookup, health details, and export.
    • write for deposit.
    • admin for forget, restore, or other management operations.
  3. Derive the effective namespace from the validated manifest rather than trusting a caller-provided namespace.
  4. Add namespace parameters to storage and retrieval functions and enforce them directly in SQL:
    sql
    SELECT * FROM memories WHERE namespace = ?
    
  5. Ensure semantic, keyword, time, archive, deduplication, and export queries all apply the same namespace predicate.
  6. Include the namespace in exact-duplicate and near-duplicate checks so content in one namespace cannot affect records in another.
  7. Apply namespace and permission checks to archive records and restore operations.
  8. Add negative tests proving that:
    • Missing manifests cannot write or delete.
    • Public manifests cannot access private namespaces.
    • A manifest for one private namespace cannot access another.
    • Invalid, expired, or modified manifests are rejected.

T09 · Insecure Skill Coding Practices

Error
Location
web_ui/js/app.js:225
Finding

Stored Cross-Site Scripting Through Deposited Knowledge-Graph Content

Content
View full analysis

Vulnerability Details

File Location: scripts/mcp_server.py:177-187, scripts/deposit.py:34-38, scripts/deposit.py:130-208, scripts/web_server.py:125-137, web_ui/js/app.js:225-271
Vulnerability Type: Stored cross-site scripting
Risk Level: High

Complete Code Snippets

The fact extraction pattern permits HTML metacharacters in the captured value:

python
_FACT_PATTERNS = [
    # X's Y is Z
    re.compile(r"([一-龥]{1,15})的([一-龥]{1,8})[是为::]\s*([^\n。;;]{1,40})"),
    re.compile(r"([一-龥]{1,15})(?:其实是|实际上|确实|其实)\s*(?:在|来到|去[了到]|加入)\s*([一-龥]{1,15})(?:工作|上班|任职)?"),
    re.compile(r"([一-龥]{1,15})(?:现在|目前)?\s*(?:在|来到|去[了到]|加入|跳槽到|转到)\s*([一-龥]{1,15})(?:工作|上班|任职|入职)?"),
]

Extracted values are stored as facts without HTML encoding or sanitization:

python
res = store.add_fact(eid, pred, val, source_memory_id=memory_id)
out.append({
    "entity": ent_name,
    "predicate": pred,
    "value": val,
    "conflict": res["conflict"],
})

The Web API returns these stored facts verbatim:

python
def get_entity(self, entity_id: int) -> tuple:
    conn = store.get_conn()
    row = conn.execute(
        "SELECT * FROM entities WHERE id=?", (entity_id,)
    ).fetchone()
    if not row:
        return self._err("Entity does not exist", 404)
    e = dict(row)
    e["aliases"] = json.loads(e.get("aliases_json", "[]"))
    e["relations"] = store.relations_of(entity_id, direction="both")
    e["facts"] = store.current_facts(entity_id)
    return self._json_response(e)

The browser interpolates stored values into HTML and writes the result using innerHTML:

javascript
function renderEntityDetail(e) {
    const container = document.getElementById('detailContent');
    document.getElementById('detailTitle').textContent = e.name;

    let html = `
        <div class="entity-name">${e.name}</div>
        <span class="entity-type">${e.type}</span>
    `;

    if (e.facts && e.facts.length > 0) {
        html += '<div class="section-title">📋 A
...[truncated 3845 chars]
Remediation
View remediation

Remediation Suggestions

  1. Stop constructing UI fragments by concatenating untrusted values into HTML.
  2. Create elements with DOM APIs and assign all stored values through textContent.
  3. Remove inline onclick attributes. Register event handlers using addEventListener() and retain entity IDs in safe data attributes.
  4. If rich HTML is an intentional feature, process it with a strict allowlist sanitizer before insertion. Do not rely on regular expressions for HTML sanitization.
  5. Apply context-appropriate encoding to every stored field, including:
    • Entity names and types.
    • Fact predicates and values.
    • Relation names and linked entity names.
    • Timeline dates and descriptions.
    • Task-status output.
  6. Add a restrictive Content Security Policy that disallows inline scripts and inline event handlers as defense in depth.
  7. Implement the MCP authentication and namespace controls described in the first finding to prevent unauthorized Skills from depositing content.
  8. Add regression tests containing HTML tags, quoted attributes, event handlers, and malformed markup, and verify that they render only as literal text.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (103)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Although framed as local/offline memory infrastructure, the skill also documents version/update checking and optional remote interactions. That difference matters because users may rely on the 'purely local' positioning when deciding whether to install the skill, and any outbound connectivity expands privacy and supply-chain exposure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Although framed as local/offline memory infrastructure, the skill also documents version/update checking and optional remote interactions. That difference matters because users may rely on the 'purely local' positioning when deciding whether to install the skill, and any outbound connectivity expands privacy and supply-chain exposure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Although framed as local/offline memory infrastructure, the skill also documents version/update checking and optional remote interactions. That difference matters because users may rely on the 'purely local' positioning when deciding whether to install the skill, and any outbound connectivity expands privacy and supply-chain exposure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Although framed as local/offline memory infrastructure, the skill also documents version/update checking and optional remote interactions. That difference matters because users may rely on the 'purely local' positioning when deciding whether to install the skill, and any outbound connectivity expands privacy and supply-chain exposure.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The activation phrase '帮我配置 + 设置定时任务' is broad, natural-language, and tied to automatic execution of configuration and scheduled-task creation. In a conversational agent, such a trigger can be invoked accidentally, socially engineered, or embedded in quoted content, leading to unintended system modification and persistence.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The CLI is presented as 'pure local' and 'zero-key', yet its documented commands include remote update checks and cloud backup operations. That mismatch can mislead users into exposing metadata or memory data to external services under the assumption that the tool never communicates off-host, which is a security-relevant trust and data-handling issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The forget command performs destructive mutation of persistent memory data immediately on invocation, with no confirmation barrier despite handling potentially sensitive long-term records. Although it archives before deletion in many cases, operator mistakes, scripted misuse, or confusion over IDs/dates can still cause unintended removal of memory entries and integrity loss.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The export handler directly serializes and returns raw raw_text from stored memories in JSON, Markdown, and CSV formats, enabling straightforward bulk disclosure of all remembered inputs. Because this server exposes tools cross-skill over MCP, a compromised or overprivileged client could exfiltrate large volumes of sensitive historical data with minimal friction.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module is marketed as 'pure local, zero-key', but it is explicitly designed to support external LLM adapters and even references OpenAI/Claude. In a memory system handling persistent personal data, this mismatch can cause users to send sensitive memory content to third-party services under false privacy assumptions.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

This path sends person-centric memories, facts, and relationship data to the LLM, which is especially sensitive because it can expose social graphs, interaction history, and private details about identifiable individuals. In a memory product, such data is more dangerous than generic text because it enables privacy violations affecting both the user and third parties.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

Using shell=True for a system-administration command that establishes persistence is a risky tool-parameter pattern. Even if current inputs are mostly fixed, this design is fragile: future changes or attacker influence over derived paths/environment can turn it into command injection or unintended command execution, and the resulting task persists beyond the initial run.

Content

Scanner excerpt · scripts/setup.py (reported line 49)May include surrounding context.

python
f'/sc daily /st {trigger} /f /rl LIMITED'
        )
        try:
            proc = subprocess.run(schtasks, shell=True, capture_output=True, text=True)
            ok = proc.returncode == 0
            return {"ok": ok, "platform": "windows", "command": schtasks,
                    "output": (proc.stdout or proc.stderr)[:300], "plan": plan["tier"]}

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

This code can delete a scheduled task by name, altering persistent system state. In context, the skill already creates persistence, so removal is part of the same system-administration surface; if exposed to untrusted triggering or hidden from the user, it enables unauthorized tampering with task configuration and reinforces that the skill has broader OS control than its stated domain suggests.

Content

Scanner excerpt · scripts/setup.py (reported line 95)May include surrounding context.

python
def remove() -> dict:
    if sys.platform.startswith("win"):
        proc = subprocess.run(f'schtasks /delete /tn "{TASK_NAME}" /f',
                              shell=True, capture_output=True, text=True)
        return {"ok": proc.returncode == 0, "platform": "windows",
                "output": (proc.stdout or proc.stderr)[:200]}

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

This code can delete a scheduled task by name, altering persistent system state. In context, the skill already creates persistence, so removal is part of the same system-administration surface; if exposed to untrusted triggering or hidden from the user, it enables unauthorized tampering with task configuration and reinforces that the skill has broader OS control than its stated domain suggests.

Content

Scanner excerpt · scripts/setup.py (reported line 95)May include surrounding context.

python
def remove() -> dict:
    if sys.platform.startswith("win"):
        proc = subprocess.run(f'schtasks /delete /tn "{TASK_NAME}" /f',
                              shell=True, capture_output=True, text=True)
        return {"ok": proc.returncode == 0, "platform": "windows",
                "output": (proc.stdout or proc.stderr)[:200]}

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/setup.py (reported line 113)May include surrounding context.

python
def status() -> dict:
    if sys.platform.startswith("win"):
        proc = subprocess.run(f'schtasks /query /tn "{TASK_NAME}"',
                              shell=True, capture_output=True, text=True)
        return {"exists": proc.returncode == 0, "platform": "windows",
                "output": (proc.stdout or proc.stderr)[:200]}

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises and documents capabilities that can read/write files, invoke shell commands, register scheduled tasks, and optionally access networked services, yet it declares no explicit tool scope or permissions boundary. In an agent environment, this creates excessive implicit authority: normal conversational use could trigger privileged operations without a clear consent model or sandbox restriction.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

Telling users they 'usually only need to say the first sentence' encourages implicit triggering without clear boundaries around when the skill activates or what side effects follow. That design reduces informed consent and makes it easier for ordinary conversation to launch automation flows that perform persistent system changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documentation promotes autonomous scheduled-task registration and recurring execution ('autopilot') without a strong upfront warning that the skill will create persistence and continue modifying local state over time. Persistence mechanisms like schtasks/cron materially raise the security impact because they can survive the initiating session and repeatedly process sensitive data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The support section explicitly states "中文界面" and the entire invocation guidance is presented as Chinese-only, with no opt-in or alternative locale choice described. This can constitute a language/locale policy issue when the skill imposes one language without documenting user choice or a justified regional constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module description and implementation are explicitly Chinese-only, and the cold-compression extractor at L227 only matches Chinese-character patterns. This imposes a locale-specific behavior without offering language choice or documenting a justified region-specific constraint, which fits the language/locale policy violation category.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The restore path clears existing derived memory state and then copies backup logs over local files without any confirmation, dry-run mode, or integrity checks. If a user points this function at an unintended or tampered backup directory, it can cause destructive local data overwrite and corruption of the application's memory base.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest describes a local-first persistent memory and backup system, optionally connectable to Baidu Netdisk. While remote backup itself is declared, this implementation achieves it by spawning an external bypy process, which introduces general command-execution capability not otherwise justified by the skill's purpose. This is broader than a typical in-process backup adapter and materially changes the trust model.

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/backup.py (reported line 167)May include surrounding context.

python
import subprocess

    try:
        proc = subprocess.run(
            ["bypy", "upload", local_path, "/zwjh_backup/"],
            capture_output=True, text=True, timeout=120,
        )

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The module docstring and command descriptions are written entirely in Chinese, and the CLI emits Chinese-only messages throughout the file. This imposes a fixed language/locale on all users without any visible opt-in, fallback, or configuration for language selection.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The import handler reads a JSON file and then inserts entities, relations, and facts into the local store, which is a file/database-modifying operation. In this function there is no confirmation prompt and no user-facing warning that running the command will change persisted knowledge data before the writes occur.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

Exposing an MCP server creates a service boundary and potentially makes stored memory accessible across skills or processes, which is broader than a simple local memory CLI. In a memory system handling persistent personal data, adding a server interface increases attack surface, especially if authentication, binding scope, or transport protections are weak or undocumented.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.