Back to skill

Security audit

Session Memory Extractor

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it handles private session history with external AI processing, persistent memory writes, and permanent deletion without strong executable safeguards.

Review this skill carefully before installing. Use only on agents whose old sessions you are comfortable sending to MiniMax, avoid enabling Feishu notifications unless the target is trusted, prefer dry-run/preview review workflows, and keep backups because normal execution can permanently remove raw session history after writing summarized memories.

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

T09 · Insecure Skill Coding Practices

Error
Location
extract_session.py:143
Finding
Unredacted session and tool data is transmitted to an external AI service<![CDATA[ ## Vulnerability Details **File Location**: `extract_session.py:143-172`; additional exposure in `extract_session.py:273-292` **Vulnerability Type**: Sensitive data exposure through excessive external transmission **Risk Level**: High ### Vulnerable Code ```python def call_minimax_api(content: str, model: str) -> str: """Call MiniMax API for extraction.""" api_key = load_minimax_api_key() if not api_key: raise RuntimeError("MiniMax API key not found in auth-profiles.json") import urllib.request import urllib.error model_name = model.split("/")[-1] if "/" in model else model url = "https://api.minimaxi.com/v1/text/chatcompletion_v2" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } # v1.0.6: bumped from 8000 to 32000 to handle trajectory transcripts (which can be 25K+ chars for long sessions) truncated = content[:32000] if len(content) > 32000 else content payload = { "model": model_name, "messages": [ {"role": "system", "content": EXTRACTION_PROMPT}, {"role": "user", "content": f"Session transcript:\n{truncated}"}, ], "max_tokens": 1024, "temperature": 0.3, } req = urllib.request.Request( url, data=json.dumps(payload).encode(), headers=headers, method="POST" ) try: with urllib.request.urlopen(req, timeout=60) as resp: result = json.load(resp) ``` Trajectory processing also incorporates internal reasoning, tool arguments, and tool results: ```python elif btype == 'thinking': text = block.get('thinking', '').strip() if text: lines.append(f"[THINKING] {text[:500]}") elif btype == 'toolCall': name = block.get('name', block.get('toolName', '?')) args = block.get('arguments', block.get('input', {})) args_str = json.dumps(args, ensure_ascii=False)[:300] if args else '' lines.append(f"[TOOL_CALL] {name}( ...[truncated 2296 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply secret redaction before constructing the request, covering API keys, bearer tokens, passwords, private keys, cookies, authorization headers, and common credential formats. 2. Exclude thinking blocks, tool calls, and tool results by default. Expose them only through a separately documented opt-in option. 3. Minimize the payload by selecting only user and assistant conversational text relevant to memory extraction. 4. Require explicit consent that identifies the external provider before sending transcript content. 5. Add a local-only extraction mode for sensitive sessions. 6. Allow users to inspect the exact redacted payload before transmission. 7. Document the external endpoint, data categories, retention implications, and applicable provider policies. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
session-memory-extractor.sh:54
Finding
Permanent session deletion is not enforced by executable confirmation controls<![CDATA[ ## Vulnerability Details **File Location**: `session-memory-extractor.sh:54-60`, `session-memory-extractor.sh:199-208`, and `run_extractor.py:278-289` **Vulnerability Type**: Unprotected destructive operation **Risk Level**: High ### Vulnerable Code The nominal confirmation option is accepted but ignored: ```bash while [[ $# -gt 0 ]]; do case $1 in --agent) AGENT_ID="$2"; shift 2 ;; --preview) PREVIEW=true; shift ;; --dry-run) DRY_RUN=true; shift ;; --min-age) MIN_AGE_DAYS="$2"; shift 2 ;; --parallel) PARALLEL="$2"; shift 2 ;; --yes) shift ;; # 忽略,由调用者控制确认 --model) EXTRACTION_MODEL="$2"; shift 2 ;; *) echo "Unknown option: $1"; exit 1 ;; esac done ``` Normal execution invokes the destructive processor without validating confirmation: ```bash echo "===RUN_START===" EXTRACTION_MODEL="$EXTRACTION_MODEL" \ DRY_RUN="$DRY_RUN" \ MIN_AGE_DAYS="$MIN_AGE_DAYS" \ CUTOFF_DATE="$CUTOFF_DATE" \ CUTOFF_DISPLAY="$CUTOFF_DISPLAY" \ TODAY="$TODAY" \ AGENT_ID="$AGENT_ID" \ MEMORY_DIR="$MEMORY_DIR" \ SESSIONS_DIR="$SESSIONS_DIR" \ SESSIONS_JSON="$SESSIONS_JSON" \ EXTRACT_SCRIPT="${WORKDIR}/extract_session.py" \ REPORT_DIR="$REPORT_DIR" \ WORKDIR="$WORKDIR" \ CLEAN_TRAJECTORY="${CLEAN_TRAJECTORY:-true}" \ LOG_LEVEL="${LOG_LEVEL:-info}" \ PARALLEL="$PARALLEL" \ python3 "$RUNNER" < "$TMP_OLD" ``` The processor permanently removes source and related files: ```python # Delete files (check exists to avoid race condition) if os.path.exists(SESSION_FILE): os.remove(SESSION_FILE) freed = file_size if clean_trajectory: traj = SESSION_FILE.replace(".jsonl", ".trajectory.jsonl") if os.path.exists(traj): freed += os.path.getsize(traj) os.remove(traj) for v in glob.glob(f"{SESSION_FILE}.deleted.*"): if os.path.exists(v): os.remove(v) ``` ### Technical Analysis The documentation requires explicit uppercase `YES` confirmations before extraction and deletion, but th ...[truncated 1507 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make non-destructive extraction the default behavior. 2. Require an explicit destructive flag such as `--delete-originals` in addition to an enforced confirmation. 3. In interactive mode, prompt for the exact uppercase value `YES` immediately before deletion and reject all other input. 4. In non-interactive mode, require a short-lived confirmation token generated by the preview step and bound to the Agent, file list, cutoff, and execution time. 5. Separate extraction and deletion into distinct commands so extraction success cannot implicitly authorize cleanup. 6. Move files to a recoverable trash or quarantine directory first and delete them only after a configurable retention period. 7. Log the confirmed file manifest and verify that it has not changed between preview and deletion. 8. Reject or remove the current no-op `--yes` option so callers cannot mistake it for an enforced control. ]]>

T02 · Agent Memory Poisoning

Error
Location
run_extractor.py:220
Finding
Untrusted transcript content can poison persistent Agent memory<![CDATA[ ## Vulnerability Details **File Location**: `extract_session.py:32-58`, `extract_session.py:155-167`, and `run_extractor.py:220-257` **Vulnerability Type**: Persistent memory poisoning through indirect prompt injection **Risk Level**: High ### Vulnerable Code The extraction prompt does not expressly identify transcript instructions as untrusted or prohibit following them: ```python EXTRACTION_PROMPT = """You are extracting durable memories from an OpenClaw session transcript. Extract the following types of information: - DECISION: Explicit choices made, strategies agreed upon, tools/preferences chosen - PREFERENCE: User likes, dislikes, habits, communication style - FACT: Factual information established (names, dates, numbers, project context) - TODO: Action items, follow-ups, things promised For each entry provide: - Type tag [DECISION/PREFERENCE/FACT/TODO] - Content (what was said/decided) - Confidence: HIGH/MEDIUM/LOW (based on clarity) Rules: - If nothing valuable found, output: NO_MEMORIES - Keep each entry concise (1-2 sentences) - Preserve exact quotes for important decisions - If a session is mostly chitchat with no actionable content, output: NO_MEMORIES - Do NOT invent information not present in the transcript Output format: ``` ## {session-id} - **[TYPE]** Content here Confidence: HIGH/MEDIUM/LOW - **[TYPE]** Content here Confidence: HIGH/MEDIUM/LOW ``` """ ``` Transcript content is supplied directly as model input: ```python truncated = content[:32000] if len(content) > 32000 else content payload = { "model": model_name, "messages": [ {"role": "system", "content": EXTRACTION_PROMPT}, {"role": "user", "content": f"Session transcript:\n{truncated}"}, ], "max_tokens": 1024, "temperature": 0.3, } ``` Validation checks format rather than provenance or semantic safety, after which the output is persisted: ```python failure_reason = None if not extract_ok: failure_reason = f"no_marker (stdo ...[truncated 3208 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Modify the system prompt to state that transcript content is untrusted data and that all instructions, policy statements, role changes, and output-format requests inside it must be ignored. 2. Delimit transcript content with explicit data-only boundaries and reinforce that quoted text cannot override extraction instructions. 3. Detect and reject memory entries containing imperative instructions, role changes, credential requests, or Agent-control language. 4. Require supporting source quotations or message identifiers for every extracted entry. 5. Add a second verification pass that checks each proposed memory against the source transcript and rejects unsupported claims. 6. Store new entries in a review queue rather than directly in active memory, particularly for sessions containing external participants. 7. Preserve provenance and confidence metadata and ensure downstream Agents treat extracted entries as untrusted summaries rather than authoritative instructions. 8. Provide an approval workflow before promoting extracted entries into persistent memory. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
extract-trajectory.sh:280
Finding
Trajectory filenames are interpolated into executable Python source<![CDATA[ ## Vulnerability Details **File Location**: `extract-trajectory.sh:280-300` **Vulnerability Type**: Python code injection through unsafe shell interpolation **Risk Level**: Medium ### Vulnerable Code ```bash # Remove from sessions.json if [[ -f "$SESSIONS_JSON" ]]; then python3 -c " import json, sys try: with open('$SESSIONS_JSON') as f: data = json.load(f) keys_to_del = [] for k, v in list(data.items()): if v.get('sessionId') == '$base' or k == '$base': keys_to_del.append(k) for k in keys_to_del: del data[k] with open('$SESSIONS_JSON', 'w') as f: json.dump(data, f, indent=2) except Exception as e: pass " 2>/dev/null || true fi ``` ### Technical Analysis `$base` is derived from the filename of an orphan `.trajectory.jsonl` file, while `$SESSIONS_JSON` is also inserted directly into a Python program passed through `python3 -c`. Shell double-quote expansion substitutes these values before Python parses the source. A filename containing a single quote and valid Python syntax can terminate the intended string literal and inject additional statements. Shell quoting does not make a value safe for embedding in Python source. The broad exception handler and suppressed standard error can also conceal failed or maliciously altered execution. The attacker must be able to create or rename a trajectory file in the selected sessions directory, and processing must reach the successful cleanup path. ### Attack Path 1. A local attacker or compromised process with write access to the sessions directory creates an orphan trajectory whose basename contains crafted Python syntax. 2. The recovery script discovers and processes the file. 3. The trajectory passes extraction validation and reaches the `sessions.json` cleanup block. 4. The crafted basename is expanded into the `python3 -c` program. 5. Python parses the injected statements as code. 6. The statements execute with the same privileges as the user runni ...[truncated 460 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Pass all dynamic values as data rather than embedding them in Python source. For example: ```bash python3 - "$SESSIONS_JSON" "$base" <<'PY' import json import os import sys import tempfile sessions_json = sys.argv[1] session_id = sys.argv[2] with open(sessions_json, "r", encoding="utf-8") as source: data = json.load(source) for key, value in list(data.items()): if ( isinstance(value, dict) and (value.get("sessionId") == session_id or key == session_id) ): del data[key] directory = os.path.dirname(sessions_json) or "." fd, temporary_path = tempfile.mkstemp(dir=directory, prefix=".sessions-", text=True) try: with os.fdopen(fd, "w", encoding="utf-8") as destination: json.dump(data, destination, indent=2) destination.flush() os.fsync(destination.fileno()) os.replace(temporary_path, sessions_json) except Exception: try: os.unlink(temporary_path) except FileNotFoundError: pass raise PY ``` Additional hardening: 1. Validate trajectory basenames against a strict session-ID allowlist where compatible with the actual identifier format. 2. Avoid suppressing all Python errors; report cleanup failures to the audit log. 3. Use atomic replacement when updating `sessions.json`. 4. Test filenames containing quotes, newlines, spaces, Unicode characters, and shell metacharacters. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (33)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding indicates a larger description-behavior mismatch where code associated with the skill sends outbound Feishu messages, invokes external messaging subprocesses, and handles report formatting instead of performing the advertised extraction/cleanup flow. For a skill processing potentially sensitive session transcripts, undeclared outbound messaging and network communication materially increase exfiltration risk because summaries or snippets of private data may be transmitted to third parties without users understanding that this is part of the skill's operation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This finding indicates a larger description-behavior mismatch where code associated with the skill sends outbound Feishu messages, invokes external messaging subprocesses, and handles report formatting instead of performing the advertised extraction/cleanup flow. For a skill processing potentially sensitive session transcripts, undeclared outbound messaging and network communication materially increase exfiltration risk because summaries or snippets of private data may be transmitted to third parties without users understanding that this is part of the skill's operation.

Chaining Abuse

High
Category
Tool Misuse
Content
# Also clean up related files
    for suffix in ".jsonl" ".jsonl.lock" ".deleted."*; do
        local f="${traj%.trajectory.jsonl}${suffix}"
        [[ -e "$f" ]] && rm -f "$f"
    done

    # Remove from sessions.json
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Credential Access

High
Category
Privilege Escalation
Content
优先级:
    1. 环境变量 MINIMAX_API_KEY(推荐,OpenClaw 可以在调用前 export)
    2. 主 agent 的 auth-profiles.json(旧版字段 minimax:cn)
    3. 主 agent 的 openclaw-agent.sqlite(新版字段 minimax-portal:default,OAuth access token)
    """
    # 1) 环境变量
    env_key = os.environ.get("MINIMAX_API_KEY", "").strip()
Confidence
96% confidence
Finding
The code is designed to pull API keys or OAuth access tokens from the main agent's stored auth material, including SQLite-backed tokens. In a skill/plugin setting, this is dangerous because it reaches into a broader credential store than necessary, enabling unauthorized credential use and increasing the blast radius if the skill is compromised or misused.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill description emphasizes local scanning/appending and cleanup, but this code sends raw session transcript content to a third-party MiniMax API. That creates a material confidentiality and transparency risk because highly sensitive chat/session data may leave the host unexpectedly, including user content, tool outputs, and possibly embedded secrets from transcripts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill documents capabilities that imply shell execution, file read/write, environment access, and network use, but it does not declare any explicit tool scope or permissions boundaries. In a skill that reads session data, writes memory files, deletes originals, and can send notifications, missing scope declarations weakens reviewability and can allow operators or agents to invoke a much broader set of actions than users may realize.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The documentation gives contradictory meanings for preview and dry-run modes, which is dangerous in a skill that can write memory entries and permanently delete raw session files. Ambiguous safety semantics can cause a user or agent to run a destructive mode believing it is non-destructive, leading to accidental data loss or unintended processing of sensitive conversations.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The skill documentation says API keys are read from auth-profiles.json by default, while the changelog says that source was replaced by OAuth SQLite. Conflicting credential-source documentation can push operators to store secrets in obsolete locations, break extraction in unexpected ways, or cause the skill to read from undeclared secret stores, all of which undermine safe handling of sensitive authentication material.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The script extracts an API token from a local SQLite auth store and falls back to an environment variable, then passes that credential to another program. This expands the skill's privilege beyond simple file recovery/cleanup and is not clearly disclosed by the stated purpose, increasing the risk of silent credential use or later misuse if the helper script is modified or compromised.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script silently reads an API token from local storage or the environment and uses it without a user-facing warning. Even if intended for extraction, undisclosed credential consumption is dangerous because users may not realize the skill is accessing sensitive auth material or sending data to an external model provider.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
Recovered session content is appended into persistent memory files automatically, which can retain sensitive conversation data indefinitely. Because this happens without explicit warning or per-session approval, private information, secrets, or regulated data from old sessions may be consolidated into a long-lived location that is easier to reuse or exfiltrate later.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script deletes trajectory files and related session artifacts automatically once it believes extraction succeeded, without explicit user confirmation. This is risky because parsing or extraction errors, model mistakes, or path mismatches could lead to irreversible data loss while also destroying forensic evidence of what was originally stored.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The script rewrites sessions.json to remove recovered sessions, which is a state-changing side effect beyond memory extraction and file cleanup. Undisclosed metadata mutation can hide history, break tooling expectations, or make recovery harder if the deletion logic removes the wrong entries.

Ssd 3

Medium
Confidence
90% confidence
Finding
The prompt explicitly asks the external model to preserve exact quotes and extract facts/TODOs from raw transcripts, increasing the chance that sensitive user content is reproduced and retained by a third-party service. In this skill context, the source material is session history, which may include confidential decisions, identifiers, operational details, and copied secrets from prior interactions.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The extractor searches the main agent's auth JSON and SQLite stores for MiniMax credentials instead of requiring a narrowly scoped credential passed explicitly to this skill. This broad credential access expands the skill's trust boundary and allows it to leverage unrelated stored tokens, which is risky in a plugin/skill context and can normalize unauthorized credential reuse.

External Transmission

Medium
Category
Data Exfiltration
Content
model_name = model.split("/")[-1] if "/" in model else model

    url = "https://api.minimaxi.com/v1/text/chatcompletion_v2"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
Confidence
97% confidence
Finding
This is a real external transmission path to a third-party endpoint. External communication is not inherently malicious, but here it becomes security-relevant because the payload contains session transcript data, making confidentiality and compliance risks depend on user awareness, consent, and data minimization controls.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code transmits session content to an external API but this file contains no explicit user warning, consent flow, or confirmation at the point of export. In the context of session-memory extraction, transcripts can contain private conversations, internal project details, and secrets, so silent export materially increases privacy risk.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This Python file contains multiple hardcoded Chinese user-facing strings in the generated Feishu message, such as counts, status labels, and headings. Under the policy rule, forcing a specific language without user opt-in is a natural-language locale violation unless the file clearly documents that the skill is intentionally Chinese-only or region-specific.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
This file adds outbound Feishu messaging capability to a skill whose stated purpose is session extraction and cleanup. Even if the message content is operational, it exfiltrates locally derived data such as agent name, session counts, extraction snippets, memory file path, and disk state to an external service, expanding the trust boundary beyond the declared scope.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def send_via_openclaw(message: str, target: str) -> dict:
    """Send message via OpenClaw's message tool (feishu DM)."""
    result = subprocess.run(
        [
            "openclaw", "message", "send",
            "--channel", "feishu",
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Tainted flow: 'target' from os.environ.get (line 128, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
def send_via_openclaw(message: str, target: str) -> dict:
    """Send message via OpenClaw's message tool (feishu DM)."""
    result = subprocess.run(
        [
            "openclaw", "message", "send",
            "--channel", "feishu",
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.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The code reads notification destination and webhook credential material from environment variables, then performs external delivery of a report. In the context of a memory-extraction tool, this creates a covert data egress path for potentially sensitive summarized content without any strong scoping, approval, or redaction controls.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print(f"[AI] Extracting... {SESSION_ID}")
    try:
        result = subprocess.run(
            ["python3", extract_script,
             "--session-id", SESSION_ID,
             "--content", content,
Confidence
94% confidence
Finding
The code invokes an external Python script whose path is taken from configuration (`extract_script`) and executes it with `subprocess.run`. Although it avoids shell injection by using an argument list, it still grants arbitrary code-execution capability to whoever controls configuration or environment variables, which exceeds a narrowly scoped 'memory extraction' operation.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
This skill's core flow executes any Python file specified by configuration, effectively turning the skill into a generic launcher for arbitrary code. In the context of a session-cleanup skill that also deletes files, this is especially dangerous because a swapped extractor can exfiltrate session content, tamper with memory outputs, or trigger destructive side effects under the guise of normal processing.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script appends to memory, rewrites metadata, and deletes session and trajectory files automatically once its checks pass, without an explicit confirmation gate at the point of destruction. While this may be intended automation, it increases the risk of irreversible data loss or tampering if configuration is wrong, inputs are attacker-controlled, or extraction logic is compromised.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
extract_session.py:139