Back to skill

Security audit

Secretary Memory Hook

Security checks for vulnerabilities and agentic risk

Overview

This memory hook is mostly purpose-aligned, but it automatically persists conversation content and runs unsafe shell commands from event and filename data.

Install only if you are comfortable with a persistent memory hook that automatically logs parts of assistant replies, moves markdown files inside the memory directory, and runs local Python scripts. The implementation should be fixed before normal use: replace shell-based exec calls with argument-safe process APIs, validate or hash session keys before using them in commands or paths, document logging and retention clearly, and add user control for automatic file migration and indexing.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
handler.ts:12
Finding

Shell Command Injection Through an Untrusted Session Key

Content
View full analysis
{ const cmd = `python3 ${script} ${args.join(" ")}`; console.log(`[secretary-memory] Running: ${cmd}`); try { const { stdout, stderr } = await execAsync(cmd, { timeout: 60000 }); if (stdout) console.log(`[secretary-memory] stdout: ${stdout}`); if (stderr) console.error(`[secretary-memory] stderr: ${stderr}`); } catch (err: any) { console.error(`[secretary-memory] Error: ${err.message}`); } } ``` The affected call sites obtain an argument directly from the event: ```ts const sessionKey = event.sessionKey || "unknown"; await runPython(`${SKILL_SCRIPTS}/session_summary.py`, [ "--session-id", sessionKey, "--verbose" ]); ``` ```ts const sessionKey = event.sessionKey || "unknown"; await runPython(`${SKILL_SCRIPTS}/auto_loader.py`, [ "--session-id", sessionKey ]); ``` ### Technical Analysis `runPython` constructs a shell command by joining argument strings without quoting or escaping them. It then passes that command to `child_process.exec`, which invokes a shell. If an attacker can influence `event.sessionKey`, shell metacharacters in the value will be interpreted as command syntax rather than as part of a Python argument. Relevant metacharacters include command separators, command substitutions, pipes, and output redirections. For example, a session key structurally equivalent to: ```text legitimate-id; attacker-command ``` would produce a command structurally equivalent to: ```sh python3 /root/.openclaw/workspace/skills/secretary-memory/scripts/session_summary.py --session-id legitimate-id; attacker-command --verbose ``` The shell can consequently execute the injected command independently of the intended ...[truncated 1359 chars]
Remediation
View remediation
{ const { stdout, stderr } = await execFileAsync( "python3", [script, ...args], { timeout: 60000 } ); if (stdout) console.log(`[secretary-memory] stdout: ${stdout}`); if (stderr) console.error(`[secretary-memory] stderr: ${stderr}`); } ``` Apply defense in depth by validating `sessionKey` before use. If session identifiers have a defined format, enforce a restrictive allowlist such as letters, digits, underscores, and hyphens, together with a reasonable maximum length. Additional hardening should include: - Run the hook as a dedicated, unprivileged operating-system account. - Avoid logging complete commands containing untrusted or sensitive values. - Resolve and validate the Python script path against an explicit allowlist. - Set output-buffer and timeout limits appropriate to the expected scripts. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
handler.ts:34
Finding

Shell Command Injection Through Malicious Markdown Filenames

Content
View full analysis
/dev/null` ).toString().trim().split("\n").filter(Boolean); ``` Each discovered path is subsequently interpolated into a shell command: ```ts for (const file of rootMdFiles) { const filename = file.split("/").pop(); // Skip hidden files if (filename.startsWith(".")) continue; const dest = join(projectsDir, filename); try { execSync(`mv "${file}" "${dest}"`); console.log(`[secretary-memory] Migrated: ${filename} -> projects/`); } catch (e) { console.error(`[secretary-memory] Migration failed: ${filename}`); } } ``` ### Technical Analysis File paths returned by `find` are treated as trusted shell fragments. Although the source and destination are surrounded by double quotes, double quotes do not suppress every form of shell evaluation. In particular, command substitution using constructs such as `$(...)` or backticks remains active inside double-quoted shell text. A specially named Markdown file can therefore cause the generated `mv` command to contain executable shell syntax. Because TypeScript inserts the literal filename into the command string before the shell parses it, the shell evaluates that syntax when `execSync` runs. The newline-based parsing of `find` output is also unsafe. Unix filenames may contain newline characters, so splitting on `"\n"` can corrupt path boundaries and create additional malformed shell input. ### Attack Path 1. An attacker, compromised component, or lower-trust process gains the ability to create a `.md` file in `/root/.openclaw/workspace/memory`. 2. The file is assigned a name containing shell command-substitution syntax. 3. A `session:compact:before` event triggers `ensureRootMdFilesIndexed`. 4 ...[truncated 998 chars]
Remediation
View remediation
entry.isFile() && !entry.name.startsWith(".") && extname(entry.name) === ".md" ) .map((entry) => join(MEMORY_DIR, entry.name)); for (const file of rootMdFiles) { const filename = basename(file); const dest = join(projectsDir, filename); renameSync(file, dest); } ``` Further hardening should include: - Define safe behavior when a destination file already exists; do not overwrite silently. - Confirm that each source is a regular file immediately before moving it. - Consider rejecting symbolic links and checking for race conditions. - Apply restrictive ownership and permissions to the memory directory. - If an external command is unavoidable, use `execFile("mv", ["--", file, dest])` rather than constructing a shell command. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
handler.ts:96
Finding

Untrusted Session Key Used in Incremental Log Path

Content
View full analysis
{ console.log("[secretary-memory] message:sent triggered"); const sessionKey = event.sessionKey || "unknown"; const content = event.context?.content || ""; if (content) { const logPath = `${MEMORY_DIR}/daily/.增量日志_${sessionKey}.mdl`; const logLine = `\n${new Date().toISOString()} | ${content.substring(0, 200)}`; try { const { exec: execSync } = require("child_process"); require("fs").appendFileSync(logPath, logLine); console.log(`[secretary-memory] Incremental log written: ${logLine.substring(0, 50)}...`); } catch (err: any) { console.error(`[secretary-memory] Incremental log failed: ${err.message}`); } } }; ``` ### Technical Analysis The event's `sessionKey` is inserted directly into a filesystem path without validation, canonicalization, or containment checking. Path separators and traversal components can therefore alter the intended destination. The fixed filename prefix means that straightforward `../../target` input does not automatically escape the directory: the first traversal component is attached to the prefix. Exploitation may consequently require a crafted session key that references existing or attacker-created intermediate directories. Nevertheless, the code does not enforce that the resolved path remains under `MEMORY_DIR/daily`, so a malicious session key can direct the append operation to an unintended nested or external path when a suitable directory structure exists. The appended data is derived from sent message content. This creates both an integrity concern and a confidentiality concern because response content may be written somewhere other than the documented incremental-log location. The unused declaration below does not mitigate th ...[truncated 1475 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The documented behavior says this hook only handles session compaction summary, preference extraction, and context recall, but the analysis indicates additional undeclared capabilities such as message-triggered incremental logging, disabled advertised features, and Markdown/FTS index maintenance. In a memory hook with broad access to conversation history, undocumented data collection or file/index manipulation materially increases the risk of covert persistence, privacy violations, and operator misunderstanding about what the skill actually does.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The hook declares and documents behavior on message:sent that expands data collection beyond compact-time summarization into continuous capture of assistant reply content. That scope expansion increases privacy and data-retention risk because users may not expect every sent message to be logged, especially when the skill is described primarily as summary/recall automation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Automatically migrating markdown files and rebuilding indexes modifies user-managed content without an explicit warning or confirmation step. Even if intended as self-healing, unattended file moves can break workflows, invalidate references, or cause unintended data processing of files the user did not expect the skill to touch.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill states that reply content is automatically appended to per-session log files, but it does not present an explicit privacy warning, consent mechanism, or retention policy. Even truncated content can contain secrets, personal data, or confidential outputs, making silent logging a meaningful privacy and data-exposure risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README states that the hook automatically performs session summarization, preference extraction, and cross-session context loading, but it does not warn users that potentially sensitive conversation content may be persisted, mined for preferences, or reused across sessions. In a memory-oriented skill, this omission materially affects informed consent and safe deployment because users may enable it without understanding the privacy and data-handling implications.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The primary natural-language description and introductory instructions are written entirely in Chinese, with no indication that language is selectable or that the skill is intentionally limited to a Chinese-speaking context. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code reorganizes markdown files under the memory directory and rebuilds indexes, but this behavior is not reflected in the stated purpose. Undisclosed file movement can affect integrity and availability of stored memory data, and in a memory-management skill it increases risk because users may not expect automatic mutation of their stored notes.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest says the hook implements 功能4(偏好提取)automatically on compaction. In the actual code, the preference extraction script invocation is commented out and marked as temporarily disabled, so the implemented behavior does not match the declared capability.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill description says it automates compaction-time memory tasks, but the handler also registers a message:sent hook that persistently logs message content on every reply. This is a capability expansion beyond the declared behavior and creates an undisclosed data collection path, which is especially sensitive in a memory-related skill handling conversational content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The handler writes message content to a persistent per-session log without any user-facing notice or consent mechanism. Because conversational replies may contain sensitive user-derived data, this creates a silent privacy risk and expands retention beyond what users would reasonably expect from a compaction hook.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Persistently recording reply content into daily session logs creates a durable data-retention channel for potentially sensitive prompts, outputs, or derived personal information. In the context of a cross-session memory skill, this is more dangerous because the whole purpose of the component is to retain and reuse conversational context, amplifying privacy and leakage risks.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language instructions and operational description are presented only in Chinese, which effectively forces a specific language on users without opt-in or explanation. Under the stated policy, language constraints should either offer user choice or be clearly documented as a justified locale-specific limitation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The file's natural-language comments and several emitted log strings are written in Chinese with no indication that language choice is configurable or intentionally limited to a Chinese-only deployment. This can violate locale policy when a skill implicitly forces a specific language context without user opt-in.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
handler.ts:33