Back to skill

Security audit

Persistent Mind

Security checks for vulnerabilities and agentic risk

Overview

PersistentMind is a coherent local memory tool, but its examples and implementation can persist, export, and re-inject sensitive or untrusted memories without enough safeguards.

Review carefully before installing. Use it only for non-secret memories, keep .persistentmind and exports out of source control and shared folders, avoid importing memory files from untrusted sources, and use separate storage paths for different projects when confidentiality matters.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (3)

T01 · Skill Instruction Hijacking

Error
Location
persistentmind.py:237
Finding

Persistent prompt injection through untrusted imported memories

Content
View full analysis
str: """ Get a formatted context block of the most relevant memories. Designed to be injected into AI prompts. """ proj = project or self.project memories = [m for m in self.memories if not m.archived and ( m.scope == MemoryScope.GLOBAL or (m.scope == MemoryScope.PROJECT and m.project == proj) or (m.scope == MemoryScope.SESSION and m.session_id == self.session_id) )] # Sort: corrections first (most important), then by importance desc memories.sort(key=lambda m: ( 0 if m.memory_type == MemoryType.CORRECTION else 1, -m.importance )) lines = ["# Relevant Memory Context\n"] char_budget = max_tokens_estimate * 4 used = 0 for m in memories: type_icon = { "fact": "📌", "preference": "⚙️", "procedure": "📋", "context": "🗂️", "correction": "⚠️", "relationship": "🔗", "reminder": "🔔" }.get(m.memory_type, "•") line = f"{type_icon} [{m.memory_type.upper()}] {m.content}" if m.tags: line += f" (tags: {', '.join(m.tags)})" line += "\n" used += len(line) if used > char_budget: lines.append(f"... ({len(memories) - len(lines) + 1} more memories truncated)") break lines.append(line) return "\n".join(lines) ``` ```python def import_memories(self, input_file: str, overwrite_duplicates: bool = False): """Import memories from a JSON export file.""" with open(input_file) as f: data = json.load(f) existing_ids = {m.id for m in self.memories} imported = 0 skipped = 0 for item in data.get("memo ...[truncated 2513 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
persistentmind.py:171
Finding

Memory scope isolation is not enforced by general recall methods

Content
View full analysis
List[MemorySearchResult]: candidates = [m for m in self.memories if not m.archived] if scope_filter: candidates = [m for m in candidates if m.scope == scope_filter] if type_filter: candidates = [m for m in candidates if m.memory_type == type_filter] if project_filter: candidates = [m for m in candidates if m.project == project_filter] if min_importance > 0: candidates = [m for m in candidates if m.importance >= min_importance] scored = [] query_terms = query.lower().split() for memory in candidates: score, matched = self._score_memory(memory, query_terms) if score > 0: scored.append(MemorySearchResult( memory=memory, relevance_score=score, matched_fields=matched )) scored.sort( key=lambda x: (x.relevance_score, x.memory.importance), reverse=True ) results = scored[:limit] for result in results: result.memory.access_count += 1 result.memory.last_accessed = datetime.now().isoformat() self._save_memories() return results def recall_by_type(self, memory_type: str, limit: int = 20) -> List[Memory]: """Get all memories of a specific type.""" results = [ m for m in self.memories if m.memory_type == memory_type and not m.archived ] return sorted(results, key=lambda m: m.importance, reverse=True)[:limit] def recall_by_tag(self, tag: str, limit: int = 20) -> List[Memory]: """Get all memories with a spe ...[truncated 1902 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
persistentmind.py:410
Finding

Sensitive memory content is persisted and exported in plaintext with default permissions

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (17)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 104)May include surrounding context.

⚠️ [CORRECTION] Never use wildcard imports — the linter will fail CI (tags: linting, ci) ⚙️ [PREFERENCE] User prefers concise responses under 200 words (tags: response-style) 📋 [PROCEDURE] To run migrations: poetry run alembic upgrade head (tags: alembic, migrations) 📌 [FACT] Database uses PostgreSQL 16. Connection string in .env as DATABASE_URL

text

---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · persistentmind.py (reported line 576)May include surrounding context.

python
⚠️ [CORRECTION] Never use wildcard imports — the linter will fail CI (tags: linting, ci)
⚙️ [PREFERENCE] User prefers concise responses under 200 words (tags: response-style)
📋 [PROCEDURE] To run migrations: poetry run alembic upgrade head (tags: alembic, migrations)
📌 [FACT] Database uses PostgreSQL 16. Connection string in .env as DATABASE_URL
```

---

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The explicit mention of an API key stored in an environment variable within persistent memory documentation is credential-handling guidance that can facilitate credential exposure. Even though the example does not reveal the actual key value, it teaches the system to retain and later surface credential-location information, which can aid discovery and accidental leakage.

Content

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

md
# Auto-tags extracted from content automatically if you don't specify
mm.remember(
    "The Stripe API key is in .env as STRIPE_SECRET_KEY",
    memory_type=MemoryType.FACT,
    scope=MemoryScope.PROJECT,
    importance=9.0

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The example encourages users to persist a secret-related memory and treats it as a normal retrievable fact, which is dangerous in a system whose core purpose is long-term recall and later prompt injection. This can lead to secrets being surfaced to the model, echoed in outputs, included in logs, or leaked through exports and team sharing workflows.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The formatted context example shows a secret-bearing memory being injected directly into prompts alongside user requests, demonstrating an unsafe pattern that could disclose credentials to the model or any downstream tooling handling prompts. In context-injection systems, this is especially dangerous because it operationalizes secret exfiltration as a default convenience feature rather than an exceptional failure.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
98% confidence
Finding

Displaying the API-key-related memory inside the generated prompt context demonstrates direct propagation of credential-related information into model input, increasing exposure to leakage via model outputs, transcripts, logs, or integrations. The surrounding skill context makes this more dangerous because prompt injection of recalled memories is presented as a routine workflow.

Content

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

⚠️ [CORRECTION] Never use wildcard imports — the linter will fail CI ⚙️ [PREFERENCE] User prefers code examples over long explanations 📌 [FACT] The Stripe API key is in .env as STRIPE_SECRET_KEY 📋 [PROCEDURE] Run migrations with: poetry run alembic upgrade head

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README explicitly promotes storing persistent memories across sessions and projects, including user preferences, corrections, and project facts, but it does not clearly warn users that sensitive or personal data may be retained longer than expected. In an agent-memory skill, this increases the risk of privacy leakage, accidental retention of secrets, and inappropriate reuse of data in later prompts or unrelated projects.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill explicitly demonstrates storing a secret-bearing fact ('The Stripe API key is in .env as STRIPE_SECRET_KEY') in persistent memory, normalizing retention of credential-related information without any warning or masking guidance. In a memory system designed to surface stored facts into future prompts, even indirect secret references can increase the chance of credential disclosure, prompt leakage, accidental export, or broader propagation across sessions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The export/import feature encourages sharing memory sets between users but does not warn that stored memories may include secrets, personal data, internal procedures, or sensitive project context. Because this skill is specifically built for persistent accumulation of context, exporting that corpus materially increases the risk of accidental disclosure or unauthorized redistribution.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest explicitly advertises persistent memory capture and direct prompt-context injection, but it does not define when these behaviors are allowed, what user action triggers them, or what boundaries limit inclusion of stored content in future prompts. In an agent-framework context, ambiguous activation rules increase the risk of over-collection, unintended retention, and prompt contamination from stale or sensitive memories.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill supports persistent local storage, deletion, export/import, and prompt-context injection, all of which can affect sensitive user data, yet the manifest only markets the feature set and 'local-only' behavior without an explicit warning about privacy, retention, accidental disclosure, or destructive operations. This is especially risky because exported memories and injected context may expose secrets, personal data, or prior instructions to other sessions, users, or downstream prompts.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The skill is explicitly designed to persist and resurface prior user and project information across sessions, which creates a real confidentiality risk if sensitive data is stored. In an agent context, long-lived memory materially increases the chance that secrets, internal project details, or personal information are later disclosed to prompts, logs, or other users unintentionally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The class description states that all data is stored locally in JSON files, but it does not warn that remembered content may include user preferences, project context, or other sensitive information that will persist on disk. Because this code is specifically designed to retain potentially sensitive agent memory across sessions, a user-facing disclosure is warranted before or alongside storage behavior.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

get_context() formats stored memory content verbatim for reinsertion into AI prompts, directly increasing the risk of sensitive information disclosure to downstream models, logs, or external systems. Because the feature is designed for prompt injection of historical content, any secret or confidential note stored earlier can be resurfaced without adequate filtering.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The export function writes the full memory set to an external file for sharing or backup, which can expose accumulated user and project data. Although it prints that an export occurred, it does not warn that the output may contain sensitive information or advise caution before sharing the file.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The example code normalizes storing secret-adjacent infrastructure information in persistent memory, teaching downstream users to retain locations of sensitive configuration such as .env-held connection details. Even though it does not store the credential value itself, it encourages practices that can increase discovery and later leakage of privileged configuration data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The markdown describes permanent deletion via mm.forget(..., permanent=True) but does not include any caution that this action is irreversible or may remove important stored context. For a persistence tool, destructive deletion affects user data and should be disclosed clearly.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.