Back to skill

Security audit

memory-orchestrator

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed local memory prototype, but it can persist broad conversation text automatically and has CLI path traversal bugs that can write YAML files outside the intended memory folders.

Review this skill before installing. It does not show exfiltration or destructive intent, but it should be used only with a clearly scoped memory directory and trusted operators until slug validation, explicit write confirmation, provenance labeling, sensitive-data exclusions, and review/delete controls 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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/new_memory_object.py:49
Finding

Path Traversal in Memory Object Creation Permits Writes Outside the Intended Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/new_memory_object.py:49-59, 76-79; exposed through scripts/memory_cli.py:144-147, 181-187
Vulnerability Type: Unrestricted path construction from a user-controlled slug
Risk Level: High

Vulnerable Code

python
# scripts/new_memory_object.py:49-59
def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--type', required=True, choices=sorted(TYPE_DIR_MAP.keys()))
    ap.add_argument('--domain', required=True)
    ap.add_argument('--slug', required=True)
    ap.add_argument('--title', required=True)
    ap.add_argument('--summary', default='TODO: add summary')
    args = ap.parse_args()

    target_dir = os.path.join(OBJECTS_DIR, TYPE_DIR_MAP[args.type])
    os.makedirs(target_dir, exist_ok=True)
    path = os.path.join(target_dir, f'{args.slug}.yaml')
python
# scripts/new_memory_object.py:76-79
with open(path, 'w', encoding='utf-8') as f:
    f.write(dump_yaml(blob))
print(json.dumps({'ok': True, 'path': path}, ensure_ascii=False, indent=2))
python
# scripts/memory_cli.py:144-147
def cmd_new_object(args):
    argv = ['--type', args.type, '--domain', args.domain, '--slug', args.slug, '--title', args.title, '--summary', args.summary]
    run('new_memory_object.py', args=argv)
    run('generate_memory_index.py')
python
# scripts/memory_cli.py:181-187
p_obj = sub.add_parser('new-object')
p_obj.add_argument('--type', required=True)
p_obj.add_argument('--domain', required=True)
p_obj.add_argument('--slug', required=True)
p_obj.add_argument('--title', required=True)
p_obj.add_argument('--summary', required=True)
p_obj.set_defaults(func=cmd_new_object)

Technical Analysis

The --slug argument is incorporated directly into a filesystem path. The implementation does not reject absolute paths, directory separators, or .. traversal components, and it does not verify that the resolved destination remains under memory/objects/<type-directory>.

For ex ...[truncated 1964 chars]

Remediation
View remediation

Remediation Suggestions

  1. Restrict slugs to a conservative allowlist, for example:
python
import re

if not re.fullmatch(r'[a-z0-9][a-z0-9_-]{0,63}', args.slug):
    raise SystemExit('Invalid slug')
  1. Resolve and validate the destination before writing:
python
base = Path(target_dir).resolve()
destination = (base / f'{args.slug}.yaml').resolve()

if not destination.is_relative_to(base):
    raise SystemExit('Destination escapes the object directory')
  1. Explicitly reject absolute paths, .., /, backslashes, null characters, and platform-specific path separators.

  2. Consider symlink attacks. Validate resolved parent directories and ensure writable object directories are not controlled by untrusted users.

  3. Use exclusive creation mode to avoid time-of-check/time-of-use races:

python
with destination.open('x', encoding='utf-8') as f:
    f.write(dump_yaml(blob))
  1. Apply the same centralized slug-validation function to every object and topic creation entry point, including direct script invocation and memory_cli.py.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/new_topic_card.py:24
Finding

Path Traversal in Topic Creation Permits Writes Outside the Topics Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/new_topic_card.py:24-35; exposed through scripts/memory_cli.py:135-141, 174-178
Vulnerability Type: Unrestricted path construction from a user-controlled slug
Risk Level: High

Vulnerable Code

python
# scripts/new_topic_card.py:24-35
def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--slug', required=True)
    ap.add_argument('--title', required=True)
    ap.add_argument('--summary', required=True)
    args = ap.parse_args()

    TOPICS_DIR.mkdir(parents=True, exist_ok=True)
    path = TOPICS_DIR / f'{args.slug}.yaml'
    if path.exists():
        raise SystemExit(f'Topic already exists: {path}')

    blob = {
python
# scripts/new_topic_card.py:34-35, 45-46
blob = {
    'id': args.slug,
    'name': args.title,
    'summary': args.summary,
    'subtopics': [],
    'recent_objects': [],
    'linked_topics': [],
    'stable_preferences': [],
    'priority_rules': [],
}
path.write_text(dump_yaml(blob), encoding='utf-8')
print(json.dumps({'ok': True, 'path': str(path)}, ensure_ascii=False, indent=2))
python
# scripts/memory_cli.py:135-141
def cmd_new_topic(args):
    run('new_topic_card.py', args=[
        '--slug', args.slug,
        '--title', args.title,
        '--summary', args.summary,
    ])
    run('generate_memory_index.py')
python
# scripts/memory_cli.py:174-178
p_topic = sub.add_parser('new-topic')
p_topic.add_argument('--slug', required=True)
p_topic.add_argument('--title', required=True)
p_topic.add_argument('--summary', required=True)
p_topic.set_defaults(func=cmd_new_topic)

Technical Analysis

The topic slug is interpolated into a Path without validation or containment checking. A slug containing .. and path separators can cause the generated .yaml file to be placed outside memory/topics.

Path.write_text() follows the constructed path and does not enforce confinement to TOPICS_DIR. The preceding path.exists() check ...[truncated 1342 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce a strict slug grammar that excludes path separators and traversal components.
  2. Resolve TOPICS_DIR and the candidate destination, then require the destination to remain beneath the resolved topics directory.
  3. Reject absolute paths and platform-specific separators before constructing the path.
  4. Use exclusive file creation instead of a separate existence check followed by write_text().
  5. Protect the memory directory from untrusted symlink creation and validate the resolved parent directory.
  6. Reuse one security-reviewed slug and destination-validation helper across topic and object creation.
  7. Add regression tests for slugs including ../x, ../../x, /absolute/x, backslash traversal, empty values, Unicode separator lookalikes, and excessively long names.

T02 · Agent Memory Poisoning

Warning
Location
scripts/apply_memory_events.py:66
Finding

Untrusted Conversation Content Can Be Persisted as Durable Agent Instructions

Content
View full analysis

Vulnerability Details

File Location: scripts/should_trigger_memory.py:9-10, 60-70; scripts/extract_memory_events.py:7-11, 31-50; scripts/apply_memory_events.py:66-95, 121-134; scripts/recall_memory.py:54-96; SKILL.md:80-94
Vulnerability Type: Persistent memory poisoning through untrusted text classification
Risk Level: Medium

Vulnerable Code

python
# scripts/should_trigger_memory.py:9-10
WRITE_WORDS = ['记住', '以后', '默认', '不要', '改成', '更新记忆', '写进记忆', '记一下']
RECALL_WORDS = ['上次', '之前', '还记得', '继续', '回到', '沿用', '翻一下记忆', '查一下记忆']
python
# scripts/should_trigger_memory.py:60-70
should_write = contains_any(text, WRITE_WORDS)
should_recall = contains_any(text, RECALL_WORDS)
should_reflect = contains_any(text, REFLECT_WORDS)
should_process = should_write or should_recall or should_reflect

result = {
    'should_process': should_process,
    'should_recall': should_recall,
    'should_write': should_write,
    'should_reflect': should_reflect,
    'reason': 'triggered-by-keywords' if should_process else 'no-explicit-memory-trigger'
}
python
# scripts/extract_memory_events.py:7-11
EVENT_PATTERNS = [
    ('preference_update', r'(以后|默认|不要|别|讨厌|喜欢).+'),
    ('decision_made', r'(决定|就用|改成|采用).+'),
    ('correction', r'(不是|不对|改一下|更正).+'),
    ('topic_activation', r'(技术|职业|投资|理财|论文|科研|日常|记忆).+'),
]
python
# scripts/extract_memory_events.py:31-50
def main():
    text = sys.stdin.read().strip()
    if not text:
        print(json.dumps({'events': []}, ensure_ascii=False))
        return
    events = []
    for event_type, pattern in EVENT_PATTERNS:
        if re.search(pattern, text):
            events.append({
                'event_type': event_type,
                'text': text,
                'domains': infer_domains(text),
                'timestamp': datetime.now().astimezone().isoformat(timespec='seconds')
            })
    if not events and infer_domains(text):
        events.append({
            'event_type': 't
...[truncated 5576 chars]
Remediation
View remediation

Remediation Suggestions

  1. Persist preferences only when the message is an explicit, first-party memory command rather than when a trigger appears anywhere in the text.
  2. Track provenance for every event, including source type, originating turn, whether content was quoted or retrieved, trust level, and confirmation status.
  3. Require user confirmation before promoting behavioral rules, security-sensitive preferences, or instruction-like content into durable memory.
  4. Parse commands structurally. For example, require a dedicated /remember command with a clearly delimited payload instead of broad substring matching.
  5. Quarantine text derived from documents, web pages, tools, or quoted passages and prevent it from becoming a preference automatically.
  6. Treat recalled memory as untrusted data. Wrap it in a clearly marked data boundary and instruct the host Agent not to execute instructions found inside remembered summaries.
  7. Store concise normalized facts rather than copying the complete message into summary and user_takeaways.
  8. Add expiration, review, update, and deletion controls for auto-captured memory.
  9. Apply confidence thresholds and source-aware promotion rules before moving events from daily logs into durable objects.
  10. Add adversarial tests covering quoted preference phrases, pasted prompt injections, tool output, multilingual triggers, negation, and requests to analyze malicious instructions.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (68)

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

The code implements a narrow persistence layer for memory events: it creates directories, loads/saves session state, appends conversation events to daily logs, and creates YAML files for preferences and decisions when explicitly labeled in the input. This partially aligns with the declared description around capturing preferences/decisions and storing long-term objects. However, several central claims in the description are not represented in this code chunk: it does not classify user input by domain, perform recall, provide summary-first retrieval, build cross-topic associations, or reflect/compress memory. Its actual purpose is materially narrower than the declared 'layered memory orchestration' description, so this chunk is a mismatch if judged against that broader declared capability set.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The code is related to memory handling, so it is not unrelated; it does classify domains and capture some preference/decision-style events, which partially aligns with the description. However, the declared purpose describes a substantially broader memory orchestration system with long-term knowledge organization, summary-first recall, and reflective compression. The supplied code only reads text from stdin, applies simple regex/keyword heuristics, and outputs extracted events as JSON. Its primary behavior is limited event detection, not full layered memory orchestration. Therefore the description materially overstates what the code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a full conversational memory orchestration system with runtime behaviors such as classification, preference capture, recall strategy, and reflective compression. The supplied code does not implement those behaviors. Instead, it performs an offline/utility task: traversing specific memory folders, loading YAML files, collecting metadata, and producing index outputs (JSON and README). While this may support a broader memory system, the chunk’s actual primary purpose is index generation, which is materially narrower and different from the declared end-user skill description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description promises a memory-system orchestration layer for OpenClaw conversations, with sophisticated behaviors around classification, memory capture, recall, and compression. The supplied code does none of that. It only provides generic YAML parsing/loading and fallback dumping helpers. While such serialization utilities could support a memory system, this chunk’s actual primary purpose is configuration/data parsing, not memory orchestration. It also reads files from disk via load_yaml_file, which is an implementation capability not reflected in the declared purpose. Therefore the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a broad conversational memory system with orchestration, classification, capture, recall, and compression behaviors. The supplied code only scaffolds creation of a memory object file on disk via CLI arguments. While this script is tangentially related to long-term memory organization, it does not implement the core described capabilities such as chat-time capture, retrieval strategy, progressive disclosure, or memory reflection/compression. Its primary purpose is materially narrower and different from the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The declared description presents a broad conversational memory orchestration capability, but the code chunk only implements a small administrative utility for creating a topic card YAML file. It does not classify input, store or retrieve conversational memory, perform recall strategies, or compress/reflect over memory. While the generated file schema references memory-related concepts, the actual behavior is limited to file creation and initialization, making the primary purpose materially narrower and different from the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents an active memory orchestration capability for conversations: classifying inputs, storing preferences/decisions, organizing reusable knowledge, recalling summaries, and reflecting/compressing memory. The supplied code only bootstraps a directory layout and seed files for such a system. It creates folders like topics/, objects/, reflections/, and indexes/, writes README guidance, initializes session-state.yaml, and creates default topic YAML files. While this setup is related to a memory system, it is only infrastructure initialization, not the described operational memory behavior. Therefore the description materially overstates what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The declared description promises a substantial memory architecture/orchestration capability: classification by domain, preference capture, long-term knowledge organization, recall strategy, and memory reflection/compression. The supplied code does none of those things directly. It only inspects raw input text for a few hardcoded commands and keywords, skips lightweight messages, and emits a JSON decision about whether memory-related processing should occur. While this trigger detection is adjacent to a memory system, it is materially narrower than the declared purpose and lacks the core behaviors described. Therefore the description does not accurately represent what the code chunk actually does.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 15)May include surrounding context.

python
def run(script, input_text=None, json_input=None, extra_env=None, args=None):
    cmd = [sys.executable, str(SCRIPTS / script)] + (args or [])
    env = os.environ.copy()
    env['MEMORY_ROOT'] = str(ROOT)
    if extra_env:
        env.update(extra_env)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 40)May include surrounding context.

python
input=args.text,
        text=True,
        capture_output=True,
        env={**os.environ, 'MEMORY_ROOT': str(ROOT)},
    )
    if events_json.returncode != 0:
        sys.stderr.write(events_json.stderr or events_json.stdout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 59)May include surrounding context.

python
input=args.text,
        text=True,
        capture_output=True,
        env={**os.environ, 'MEMORY_ROOT': str(ROOT)},
    )
    if events_json.returncode != 0:
        sys.stderr.write(events_json.stderr or events_json.stdout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 73)May include surrounding context.

python
input=args.text,
        text=True,
        capture_output=True,
        env={**os.environ, 'MEMORY_ROOT': str(ROOT)},
    )
    if events_json.returncode != 0:
        sys.stderr.write(events_json.stderr or events_json.stdout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 83)May include surrounding context.

python
input=args.text,
        text=True,
        capture_output=True,
        env={**os.environ, 'MEMORY_ROOT': str(ROOT)},
    )
    if events_json.returncode != 0:
        sys.stderr.write(events_json.stderr or events_json.stdout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 96)May include surrounding context.

python
input=args.text,
        text=True,
        capture_output=True,
        env={**os.environ, 'MEMORY_ROOT': str(ROOT)},
    )
    if events_json.returncode != 0:
        sys.stderr.write(events_json.stderr or events_json.stdout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 108)May include surrounding context.

python
input=args.text,
        text=True,
        capture_output=True,
        env={**os.environ, 'MEMORY_ROOT': str(ROOT)},
    )
    if events_json.returncode != 0:
        sys.stderr.write(events_json.stderr or events_json.stdout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/memory_cli.py (reported line 120)May include surrounding context.

python
input=args.text,
        text=True,
        capture_output=True,
        env={**os.environ, 'MEMORY_ROOT': str(ROOT)},
    )
    if events_json.returncode != 0:
        sys.stderr.write(events_json.stderr or events_json.stdout)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The architecture explicitly describes persistent storage of conversation-derived preferences, decisions, logs, reflections, and indexes, but does not mention consent, disclosure, retention limits, or controls for sensitive data. In a memory-orchestration skill, this omission can lead to silent collection and long-term storage of user data, increasing privacy, compliance, and data-exposure risk if the feature is implemented as described.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
75% confidence
Finding

Natural-language headings, inputs, and expected effects are presented only in Chinese, which can amount to forcing a specific language if the skill documentation does not offer alternatives or explicit locale scope. There is no indication in this file that the language is optional, user-selected, or justified as region-specific.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The write trigger list contains broad phrases like '以后', '默认', '不要', and '改成' that commonly appear in normal conversation and may be interpreted as instructions to persist data. In a system designed to write long-term memory, this can lead to accidental storage or mutation of user preferences, decisions, or other sensitive information without sufficiently clear intent.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The recall trigger list includes very common conversational phrases such as '上次', '之前', and '继续', which can cause memory recall to activate during ordinary dialogue rather than only when the user intends it. In a persistent memory skill, this increases the chance of unnecessary retrieval of stored context, including sensitive preferences or prior discussions, and can confuse boundaries between current-session context and long-term memory.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The README explains how memory is written, updated, recalled, and reflected across persistent files, but it does not present a clear user-facing warning or consent model for data retention, modification, and review before persistence. In the context of a long-term memory orchestration skill, that omission makes accidental collection and silent evolution of personal data more likely, especially because the system is explicitly intended to capture preferences and decisions over time.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill explicitly instructs use of filesystem reads/writes and shell-invoked scripts, but the manifest does not declare any tool scope or permission boundaries. In an agent environment, that mismatch weakens reviewability and can lead to over-broad execution against workspace data, especially because the skill handles persistent memory files containing user-derived content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructs the agent to immediately persist user-derived facts, preferences, corrections, and decisions before responding, but provides no requirement for user consent, notice, retention limits, or sensitivity filtering. In a memory skill, this is especially dangerous because it normalizes silent collection of durable personal data, increasing privacy, compliance, and prompt-injection persistence risks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code writes session state, daily logs, and durable preference/decision objects to disk, including event text derived from stdin payloads. There is no confirmation prompt, user-facing print/log before the write, or inline comment/docstring warning that the script persists conversation data, which is safety-relevant because it affects user data and creates durable records.

Content

No source excerpt is available for this finding.

Tainted flow: 'SESSION_PATH' from os.environ.get (line 9, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/apply_memory_events.py (reported line 30)May include surrounding context.

python
def save_session(data):
    with open(SESSION_PATH, 'w', encoding='utf-8') as f:
        if yaml is None:
            f.write(dump_yaml_fallback(data))
        else: