Back to skill

Security audit

memory-doctor

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent memory-maintenance purpose, but its file handling can read or overwrite files outside the chosen memory directory through symlinks and its rollback claims are stronger than the implementation.

Review before installing. Use it only on memory directories you control, avoid directories containing symlinks, and do not rely on its undo as a complete rollback. Treat --fix and undo as local file-mutating commands, not as fully contained repairs.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (1)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/memory_doctor/lint.py:65
Finding

Symbolic-Link Directory Escape Enables External File Read and Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_doctor/adapters/auto_memory.py:83-95, 111-116; scripts/memory_doctor/lint.py:65-68; scripts/memory_doctor/snapshot.py:23-26, 38-41
Vulnerability Type: Symbolic-link following and path-boundary violation
Risk Level: High

Vulnerable Code

In scripts/memory_doctor/adapters/auto_memory.py:83-95, memory files are accepted using os.path.isfile(), which follows symbolic links:

python
for entry in sorted(os.listdir(root)):
    if not entry.endswith(".md") or entry == INDEX_FILE:
        continue
    path = os.path.join(root, entry)
    if not os.path.isfile(path):
        continue
    store.units.append(self._parse_file(path, entry))

if os.path.isfile(index_path):
    store.index = self._parse_index(index_path)
return store

In scripts/memory_doctor/adapters/auto_memory.py:111-116, the index is opened without verifying that it is a regular file contained within the selected directory:

python
def _parse_index(self, path: str) -> list[IndexEntry]:
    entries = []
    with open(path, encoding="utf-8") as f:
        for line in f:
            m = INDEX_LINE_RE.match(line.rstrip("\n"))

In scripts/memory_doctor/lint.py:65-68, MEMORY.md is opened directly for truncating writes:

python
def apply_index(store: MemoryStore, plan: IndexPlan) -> None:
    path = store.index_path or os.path.join(store.root, "MEMORY.md")
    with open(path, "w", encoding="utf-8") as f:
        f.write(render_index(plan))

In scripts/memory_doctor/snapshot.py:23-26, snapshot creation follows any Markdown symbolic link found in the memory directory:

python
for entry in os.listdir(root):
    if entry.endswith(".md"):
        shutil.copy2(os.path.join(root, entry), os.path.join(dest, entry))
return dest

In scripts/memory_doctor/snapshot.py:38-41, snapshot restoration similarly follows destination sym ...[truncated 4015 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reject symbolic links for all memory, index, snapshot, and restore entries. Use os.lstat() and require stat.S_ISREG(result.st_mode) rather than relying on os.path.isfile().

  2. Enforce canonical directory containment. Resolve both the trusted root and each candidate path, then verify containment before any read or write:

    python
    root_real = os.path.realpath(root)
    candidate_real = os.path.realpath(candidate)
    
    if os.path.commonpath([root_real, candidate_real]) != root_real:
        raise ValueError("path escapes the memory directory")
    

    Apply this validation to MEMORY.md, every loaded memory file, snapshot sources, snapshot entries, and restore destinations.

  3. Use no-follow file-opening semantics. On supported platforms, open files with os.open() and O_NOFOLLOW, validate the returned descriptor with os.fstat(), and then wrap it with os.fdopen(). This reduces symbolic-link and time-of-check/time-of-use risks.

  4. Write the index atomically. Create a temporary regular file inside the verified memory directory, flush and synchronize it, and then replace a verified non-link MEMORY.md using os.replace(). Ensure the destination is revalidated immediately before replacement.

  5. Harden snapshot restoration. Permit only expected regular Markdown files, reject nested paths and links, validate both source and destination boundaries, and avoid copying through existing destination links.

  6. Use restrictive permissions for snapshots. Create .memory-doctor and snapshot directories with permissions appropriate for potentially sensitive memory content.

  7. Add regression tests. Cover a linked MEMORY.md, linked ordinary Markdown files, linked snapshot entries, destination-link replacement before undo, external path targets, and concurrent path replacement.

Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (4)

Memory Manipulation

High
Category
Memory Poisoning
Confidence
85% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · SKILL.md (reported line 51)May include surrounding context.

md
Semantic fixes that need a model — true duplicate **merge**, contradiction **reconcile**,
oversized **compaction** — are *flagged* in v0.1 and *resolved* in v0.2 (no embeddings;
manual-apply, snapshot-backed). Never auto-edit memory content on the user's behalf without
confirmation.

## Safety

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill explicitly describes and instructs use of file-reading and file-writing operations (report, lint --fix, undo) but does not declare any tool scope or permission boundaries. This creates an authorization ambiguity where an agent may perform filesystem mutations without clear, least-privilege constraints, increasing the chance of overbroad access or unsafe invocation in a real environment.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 51)May include surrounding context.

md
Semantic fixes that need a model — true duplicate **merge**, contradiction **reconcile**,
oversized **compaction** — are *flagged* in v0.1 and *resolved* in v0.2 (no embeddings;
manual-apply, snapshot-backed). Never auto-edit memory content on the user's behalf without
confirmation.

## Safety

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The module claims reversible safety by snapshotting the whole memory directory and supporting undo, but the implementation only copies top-level .md files and restore only re-copies files from a chosen snapshot. In a memory-repair skill, this can create a false sense of safety: nested files, non-.md memory artifacts, and deleted files are not fully protected, so a user may approve destructive cleanup assuming rollback is complete when it is not.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.