Back to skill

Security audit

Secretary Memory

Security checks for vulnerabilities and agentic risk

Overview

This memory skill is not clearly malicious, but it broadly stores and reuses private conversation data and includes risky automation for prompt injection, remote LLM summarization, file restore, and generated skills.

Install only if you are comfortable with a persistent local memory system that profiles conversations and can recall that data into future prompts. Disable or avoid automatic skill generation, remote LLM summarization, daemon/watch modes, and restore operations unless you have reviewed the configuration and trust the memory directory contents. Prefer an isolated non-root memory directory and periodically inspect or delete stored profile, graph, index, and generated-skill files.

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
scripts/fts5_index.py:329
Finding
Private Memory Disclosure Through an Unrestricted LLM Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fts5_index.py:329-424`; default invocation paths in `scripts/session_search.py:128-166, 352-357` **Vulnerability Type**: Sensitive-data transmission to an unrestricted network destination **Risk Level**: High ### Vulnerable Code ```python # scripts/fts5_index.py LLM_API_URL = os.environ.get( "LLM_API_URL", "http://localhost:11434/api/generate" ) LLM_MODEL = os.environ.get("LLM_MODEL", "llama3") ``` ```python def summarize(self, results: List[Dict], query: str, max_context: int = 3000) -> str: """Use an LLM to summarize search results.""" if not results: return f"No memory related to {query} was found" context = self._build_context(results, max_context) prompt = f"""You are an assistant for a secretary-style memory system. Summarize information related to the user query: {query}. ## Search Results {context} """ summary = self._call_llm(prompt) return summary ``` The original prompt in the file is written in Chinese, but its interpolated sensitive fields are produced by the following code: ```python def _build_context(self, results: List[Dict], max_context: int) -> str: """Build LLM context.""" context_parts = [] total_len = 0 for i, r in enumerate(results, 1): part = f""" --- Result {i} --- File: {r['path']} Date: {r['date']} Partition: {r['partition']} Content: {r['content'][:1000]} """ part_len = len(part) if total_len + part_len > max_context: break context_parts.append(part) total_len += part_len return "\n".join(context_parts) ``` ```python def _call_llm(self, prompt: str) -> str: """Call the LLM API.""" import urllib.request import urllib.error if not self.api_url or self.api_url == "http://localhost:11434/api/generate": raise LLMSummarizationError( "LLM API is not configured or accessible.", hint="Set LLM_API_URL to your LLM ...[truncated 3310 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable LLM summarization by default and require an explicit option such as `--llm`. 2. Require informed confirmation before the first transmission to each endpoint. 3. Permit only explicitly configured HTTPS destinations on a trusted-host allowlist. 4. Reject HTTP, non-web schemes, embedded credentials, loopback rebinding targets, and cross-host redirects. 5. Remove absolute paths and unnecessary metadata before constructing the prompt. 6. Redact likely credentials, access tokens, private keys, email addresses, and other sensitive fields. 7. Display a preview of the exact information and destination before transmission. 8. Support a strictly local summarizer that cannot initiate network requests. 9. Add configurable partition exclusions so sensitive profile or relationship data is never sent remotely. 10. Document the data flow prominently rather than describing ordinary search as implicitly using an LLM. ]]>

T02 · Agent Memory Poisoning

Error
Location
scripts/auto_loader.py:264
Finding
Persistent Cross-Session Prompt Injection Through Recalled Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/auto_loader.py:264-319`; persistence source in `scripts/session_summary.py:48-81` **Vulnerability Type**: Persistent memory poisoning and indirect prompt injection **Risk Level**: High ### Vulnerable Code Session watch mode stores observed content without establishing a trust boundary: ```python # scripts/session_summary.py def append_incremental(self, content: str, session_id: str = "") -> Tuple[bool, str]: """Append content incrementally to today's log.""" if not content.strip(): return False, "Empty content" try: today_file = self.get_today_file() timestamp = datetime.now().strftime("%H:%M") lines = [f"\n## {timestamp}"] if session_id: lines.append(f" [Session: {session_id}]") lines.append(" - Session Summary\n\n") lines.append(content.strip()) lines.append("\n") entry = ''.join(lines) with open(today_file, "a", encoding="utf-8") as f: f.write(entry) f.flush() os.fsync(f.fileno()) return True, f"Appended to {today_file.name}" except Exception as e: return False, f"Incremental write failed: {e}" ``` The stored content is later placed before the base prompt: ```python # scripts/auto_loader.py def inject_to_context( self, memories: List[Dict], base_prompt: str = "", use_llm_summary: bool = True ) -> str: """Format memories and inject them into the prompt.""" if not memories: return base_prompt sections = [] sections.append("\n\n<!-- Context memory recall -->\n") sections.append("## Related Memory\n") if use_llm_summary and len(memories) >= 2: try: summarizer = self._get_llm_summarizer() summary = summarizer.summarize( memories, query=self.state.get("last_topic", "") ) if summary: se ...[truncated 3211 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never concatenate raw recalled memory into a system message. 2. Place recalled records in a lower-trust user or data message. 3. Add an immutable instruction stating that recalled content is untrusted evidence and must never be followed as an instruction. 4. Use strong structured delimiters and escape role markers, XML-like control tags, Markdown comments, and tool-call syntax. 5. Detect and quarantine records containing instruction-hijacking phrases or simulated system messages. 6. Preserve source, author, timestamp, and trust level for every recalled record. 7. Require user confirmation before performing external actions based solely on recalled memory. 8. Summarize memory through a constrained extraction process that returns facts in a validated schema rather than free-form instructions. 9. Provide controls to delete, disable, or inspect suspicious memory entries. 10. Add regression tests covering persistent instructions, fake role messages, and cross-session activation. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/restore.py:117
Finding
Path Traversal and Arbitrary File Movement in Restore Operations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/restore.py:117-184` **Vulnerability Type**: Unvalidated path traversal through CLI input and mutable restore metadata **Risk Level**: High ### Vulnerable Code Restore destinations and sources are constructed directly from log values: ```python def restore_by_date(self, date: str) -> int: """Restore all archived files for a date.""" entries = self.find_archive_entries_by_date(date) if not entries: print(f"[Info] No archive records found for date {date}") return 0 count = 0 for entry in entries: source = ARCHIVE_DIR / entry["dest"] actual_source = ARCHIVE_DIR / entry["dest"] actual_dest = MEMORY_DIR / entry["source"] if self.restore_file(actual_source, actual_dest): self.log_operation( "restore", entry["dest"], entry["source"], entry.get("type", "") ) count += 1 return count ``` ```python def restore_by_topic(self, topic: str) -> int: """Restore all archived files for a topic.""" entries = self.find_archive_entries_by_topic(topic) if not entries: print(f"[Info] No archive records found for topic {topic}") return 0 count = 0 for entry in entries: actual_source = ARCHIVE_DIR / entry["dest"] actual_dest = MEMORY_DIR / entry["source"] if self.restore_file(actual_source, actual_dest): self.log_operation( "restore", entry["dest"], entry["source"], entry.get("type", "") ) count += 1 return count ``` CLI input is also joined directly to the archive root: ```python def restore_single_file(self, archive_path: str) -> bool: """Restore one archived file.""" source = ARCHIVE_DIR / archive_path for entry in self.restore_log: if ( entry.get(" ...[truncated 3333 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject absolute paths and any path containing `..`. 2. Resolve paths before use and verify containment: ```python def resolve_beneath(root: Path, value: str) -> Path: if Path(value).is_absolute(): raise ValueError("Absolute paths are not allowed") root = root.resolve(strict=True) candidate = (root / value).resolve(strict=False) if not candidate.is_relative_to(root): raise ValueError("Path escapes the allowed root") return candidate ``` 3. Validate archive records against a strict schema and known path prefixes. 4. Derive destinations from validated archive type and filename instead of trusting stored destination strings. 5. Reject symlinks in every source path component and recheck containment immediately before `rename()`. 6. Restrict restoration to expected `.md` files beneath approved archive partitions. 7. Add integrity protection or regenerate restore metadata from trusted directory state. 8. Require explicit confirmation showing canonical source and destination paths. 9. Avoid running memory-management scripts as `root`; use a dedicated account limited to the memory directory. 10. Add tests for absolute paths, nested traversal, symlink escapes, and malicious restore-log records. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/skill-creator/auto_skill_generator.py:416
Finding
Path and Python Source Injection in Automatic Skill Generation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/skill-creator/auto_skill_generator.py:416-547` **Vulnerability Type**: Generated-file path traversal and generated-code injection **Risk Level**: High ### Vulnerable Code Only the generated directory name is sanitized: ```python def generate_skill( self, name: str, pattern_type: str, description: str = "", steps: List[str] = None, triggers: List[str] = None ) -> Tuple[bool, str]: """Generate a new Skill.""" safe_name = re.sub(r'[^a-zA-Z0-9_-]', '-', name.lower()) skill_dir = self.skills_dir / safe_name if skill_dir.exists(): return False, f"Skill already exists: {safe_name}" try: skill_dir.mkdir(parents=True, exist_ok=True) (skill_dir / "scripts").mkdir(exist_ok=True) (skill_dir / "references").mkdir(exist_ok=True) self._generate_skill_md( skill_dir, name, pattern_type, description, steps, triggers ) self._generate_main_script( skill_dir, name, pattern_type, steps ) self._generate_readme( skill_dir, name, description, triggers ) self._save_skill_meta( skill_dir, name, pattern_type, triggers ) return True, str(skill_dir) except Exception as e: if skill_dir.exists(): import shutil shutil.rmtree(skill_dir) return False, f"Generation failed: {e}" ``` The unsanitized name is reused in persistent Skill instructions: ```python content = f"""# {name} Automatically generated Skill - {pattern_type} ## Functionality {description or f'Automatically process tasks related to {pattern_type}'} {steps_text} {triggers_text} ## Usage ```bash python3 scripts/{name}.py [arguments] ``` """ ``` It is also i ...[truncated 3507 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define one canonical identifier and use it for the directory, filename, metadata, and generated source. 2. Reject invalid identifiers instead of silently transforming them: ```python if not re.fullmatch(r"[a-z][a-z0-9_-]{0,63}", name): raise ValueError("Invalid Skill name") ``` 3. Resolve every generated path and verify that it remains beneath `SKILLS_DIR`. 4. Reject absolute paths, separators, traversal components, control characters, quotes, and newlines in identifiers. 5. Do not interpolate user-controlled values directly into Python templates. 6. Serialize display values with `repr()` or, preferably, generate code through an AST or a fixed template with validated data files. 7. Keep descriptions and steps in JSON metadata rather than executable Python wherever possible. 8. Mark generated Markdown as untrusted draft content and require human review before registration or activation. 9. Do not automatically register triggers until the generated Skill passes validation. 10. Run syntax checks and static security checks in a sandbox before exposing a generated script. 11. Execute generated Skills under a restricted account with no network access and write access limited to a dedicated workspace. 12. Add tests for absolute names, multi-level traversal, quote termination, triple-quote termination, newline injection, and Markdown instruction injection. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (90)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The combination of broad unimplemented claims and apparent incomplete integration suggests the skill may fail unpredictably while processing user memory data. Reliability problems in stateful memory systems can become security issues when users assume data was summarized, deleted, or retained correctly but it was not.

Missing User Warnings

High
Confidence
98% confidence
Finding
The skill describes automatic preference extraction, user graph construction, and cross-session recall without an explicit privacy warning or consent boundary. This is dangerous because it normalizes silent profiling and persistent storage of potentially sensitive personal data.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
Automatic creation of new skills is not necessary for a memory system and meaningfully increases attack surface, persistence, and privilege escalation opportunities. If abused, it can transform stored patterns or conversation content into newly deployable behaviors without adequate review.

Missing User Warnings

High
Confidence
97% confidence
Finding
Automatic end-of-session summarization and preference extraction imply silent transformation and storage of user conversations. That is privacy-sensitive processing, and failing to disclose it prevents informed consent and can retain data users did not expect to persist.

Missing User Warnings

High
Confidence
96% confidence
Finding
Automatically loading historical context into prompts can surface private data from prior sessions in unrelated future interactions and can amplify prompt-injection risks from stored content. Without warning or sanitization, the skill may reintroduce secrets, unsafe instructions, or sensitive context into the model input.

Ssd 3

High
Confidence
95% confidence
Finding
The contact patterns intentionally mine names, emails, and roles from arbitrary session text and later persist them. This creates a meaningful privacy risk, especially for third-party data mentioned in conversation, because it enables silent collection and long-term storage of personally identifiable information.

Ssd 3

High
Confidence
95% confidence
Finding
Forwarding full session text to a secondary profiling/relationship-graph component expands the trust boundary and compounds privacy exposure. In a memory-management skill, this context makes the issue more dangerous because the system is likely to process large volumes of intimate historical conversation data over time.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The manifest documents scripts that read and write files, invoke shell commands, use environment data, and potentially perform network-backed LLM summarization, but it does not declare any permissions or allowed-tools scope. This creates an overbroad and opaque trust boundary: a user or host may activate a skill with materially more capability than the manifest communicates.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
A memory-management skill that also generates unrelated new skills crosses a functional boundary into code or capability expansion. This scope drift is dangerous because users enabling a memory tool may unknowingly authorize persistence and creation of new executable behavior.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The trigger conditions are broad enough to overlap with ordinary conversation about memory, organization, search, or architecture. Over-broad activation increases the chance the skill will run in contexts where users did not intend persistent storage, recall, or profiling.

Ssd 3

Medium
Confidence
95% confidence
Finding
The documented design enables automatic collection, summarization, profiling, and reuse of user-provided conversation data across sessions without clear minimization limits. In context, this is especially dangerous because a memory assistant naturally accumulates intimate long-term behavioral and project data.

Ssd 3

Medium
Confidence
94% confidence
Finding
Historical-context auto-loading can expose prior private data in new prompts and may cause unrelated conversations to inherit sensitive context. Even absent malice, this cross-session leakage risk is inherent to prompt-level memory injection.

Static analysis

No suspicious patterns detected.