Back to skill

Security audit

memory-persistence

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its memory-management purpose, but review is needed because concrete implementation flaws can expose API keys or let imported memory IDs write outside the intended storage folder.

Install only after review or patching if you will store sensitive data. Prefer local or SQLite storage, avoid importing untrusted JSON, do not use custom LLM base URLs with auto-detected credentials, use private repositories and least-privilege tokens for remote backends, and review LLM-generated memories before saving them.

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
storage/local.py:19
Finding

Path Traversal in Local Memory Storage

Content
View full analysis
Path: return self.base_path / f"{entry_id}.json" # storage/local.py:23-51 def save(self, entry: MemoryEntry) -> bool: try: path = self._get_entry_path(entry.id) with open(path, 'w', encoding='utf-8') as f: json.dump(entry.to_dict(), f, ensure_ascii=False, indent=2) return True except Exception as e: print(f"Error saving entry {entry.id}: {e}") return False def load(self, entry_id: str) -> Optional[MemoryEntry]: try: path = self._get_entry_path(entry_id) if not path.exists(): return None with open(path, 'r', encoding='utf-8') as f: data = json.load(f) return MemoryEntry.from_dict(data) except Exception as e: print(f"Error loading entry {entry_id}: {e}") return None def delete(self, entry_id: str) -> bool: try: path = self._get_entry_path(entry_id) if path.exists(): path.unlink() return True return False except Exception as e: print(f"Error deleting entry {entry_id}: {e}") return False ``` ```python # storage/base.py:40-42 @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'MemoryEntry': return cls(**data) ``` ```python # memory_manager.py:545-558 if filepath: with open(filepath, 'r', encoding='utf-8') as f: data = json.load(f) elif json_str: data = json.loads(json_str) else: raise ValueError("Must provide filepath or json_str") count = 0 for entry_data in data.get('entries', []): entry = MemoryEntry.from_dict(entry_data) # Don't regenerate embedding during import self.s ...[truncated 1867 chars]
Remediation
View remediation
Path: if not re.fullmatch(r"[a-f0-9]{16}", entry_id): raise ValueError("Invalid memory entry ID") root = self.base_path.resolve() target = (root / f"{entry_id}.json").resolve() if target.parent != root: raise ValueError("Entry path escapes storage directory") return target ``` 4. Validate imported data against a strict schema before constructing `MemoryEntry`. 5. Consider opening files with protections against symbolic-link attacks when the storage directory may be writable by another principal. 6. Add regression tests for `../`, absolute paths, backslash traversal, encoded separators, and malicious imported IDs. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
summarizer.py:110
Finding

Auto-Discovered API Key Can Be Sent to a Caller-Controlled Endpoint

Content
View full analysis
Remediation
View remediation

T02 · Agent Memory Poisoning

Warning
Location
summarizer.py:302
Finding

Untrusted LLM Output Can Poison Persistent Agent Memory

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
storage/gitee.py:45
Finding

Gitee Access Token Exposed in Request URLs

Content
View full analysis
dict: """Make HTTP request to Gitee API""" import urllib.request import urllib.parse url = f"{self.api_base}{endpoint}" # Add token to all requests params = kwargs.get('params', {}) params['access_token'] = self.token if params: url = f"{url}?{urllib.parse.urlencode(params)}" ``` ### Technical Analysis Every Gitee request places the access token in the URL query string. HTTPS protects the request in transit from ordinary network observers, but URLs are commonly recorded by reverse proxies, gateways, monitoring products, request tracing, browser-like tooling, and exception diagnostics. A query-string credential has a broader accidental-disclosure surface than an authorization header. The behavior applies to all operations, including repository reads, writes, and deletions. The Base64 encoding used for memory content elsewhere in this backend is not itself a covert exfiltration mechanism: it is the representation required by the repository Contents API and targets the explicitly configured Gitee repository. Base64 provides no confidentiality, however, and repository visibility and permissions must still be configured appropriately. ### Attack Path 1. A user configures the Gitee backend with an access token. 2. Any backend operation calls `_request()`. 3. `_request()` appends the token as `access_token=` to the URL. 4. A proxy, monitoring service, gateway, or diagnostic facility records the complete URL. 5. A party with access to those logs retrieves the token. 6. The party uses the token against Gitee according to its granted scopes. ### Impact Assessment A disclosed token can permit unauthorized access to repositories and operations covere ...[truncated 254 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
Findings (37)

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 · .gitignore (reported line 8)May include surrounding context.

text
*.egg-info/
dist/
build/
.env
*.log

# Data directories (created on first use)

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The code broadly matches the declared purpose of a persistent multi-backend memory system with optional embeddings and maintenance tools. It clearly supports storing, retrieving, searching, tagging, grouping, deleting, and exporting/importing memories. However, the description claims capabilities for private/shared memories and conversation summarization, which are not evident in this code chunk. Instead, the chunk exposes grouping and bulk maintenance features plus JSON import/export, which are more specific than the description. Because the declared description includes important capabilities not represented here and omits some concrete behavior, this is a partial description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The supplied code chunk is only a template-definition utility. It contains dataclasses and class methods for predefined memory templates such as user preferences, tasks, decisions, meeting notes, and bug reports, plus simple generation of content strings and CLI help text. It does not implement the core declared capabilities of a persistent memory system: there is no database/backend interaction, no saving or fetching of memories, no private/shared access model, no embedding generation/search, no summarization, and no maintenance operations on stored memories. While template generation could be a supporting component of a memory system, this chunk by itself does not accurately represent the declared description's primary functionality.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
94% confidence
Finding

When delete_memories=True, delete_group() irreversibly removes every entry in the group through repeated self.storage.delete() calls. The function has no confirmation prompt, no warning log, and no stronger disclosure beyond the parameter name, which is insufficient for a destructive bulk operation.

Content

No source excerpt is available for this finding.

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% 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 · storage/gitee.py (reported line 148)May include surrounding context.

python
return False
            
            data = {
                "message": f"Delete memory: {entry_id}",
                "sha": sha,
                "branch": self.branch
            }

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% 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 · storage/github.py (reported line 142)May include surrounding context.

python
return False
            
            data = {
                "message": f"Delete memory: {entry_id}",
                "sha": sha,
                "branch": self.branch
            }

Credential Access

High
Category
Privilege Escalation
Confidence
74% confidence
Finding

This function accesses API credentials from ambient sources, which is a credential-scope expansion issue rather than direct theft. In the context of a summarization component that also performs network calls, such access is dangerous because it enables the module to act with existing secrets and route sensitive conversation data through privileged external accounts without a tight permission boundary.

Content

Scanner excerpt · summarizer.py (reported line 119)May include surrounding context.

python
self.base_url = base_url or (detected['base_url'] if detected else None)
    
    def _get_api_key(self) -> Optional[str]:
        """Get API key from environment or config"""
        if self.api_key:
            return self.api_key

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README explicitly promotes GitHub and Gitee as storage backends for persistent memories, which may include user preferences, conversation-derived summaries, and other potentially sensitive data, but it does not clearly warn that using these backends transmits memory contents to third-party services. In a memory-persistence skill, this omission is security-relevant because users and integrators may unknowingly store personal or confidential agent memory in remote repositories, increasing privacy, compliance, and data exposure risk.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill documents capabilities that involve environment variables, local file read/write, and remote network access (GitHub/Gitee), but it declares no tool scope or permissions boundary. In an agent environment, this can lead to over-broad execution privileges and make data exfiltration or unintended file modification easier if the skill is invoked without explicit guardrails.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill encourages persistent storage of user preferences, notes, and conversation-derived memories without a clear warning that sensitive personal or confidential data may be retained. Because the skill also supports shared memory across agents, users may unknowingly cause broader disclosure of private information than intended.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation advertises GitHub and Gitee backends but does not clearly warn that memory contents may be transmitted to third-party services and stored outside the local environment. If users store personal, proprietary, or secret data, remote syncing can create significant confidentiality and compliance risks, especially when combined with tokens from environment variables.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The delete command performs irreversible deletion immediately on a single identifier with no confirmation, preview, or soft-delete behavior. In a persistence skill handling long-lived agent memory, accidental invocation, prompt-induced misuse, or wrong-ID selection can permanently destroy important state and impair agent behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The shared-memory delete command deletes shared records without confirmation or authorization checks in the CLI flow. Because shared memory is cross-agent state, a mistaken or manipulated deletion can affect multiple agents and remove information others depend on.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The docstring at L0581 says this command 'requires user confirmation to delete', and the CLI help at L0876-L0878 says 'Merge similar memories' with an '--apply' option implying changes can be applied. However, the implementation only calls consolidate_similar, prints candidate pairs, and instructs the user to run a separate delete command; it never reads args.apply or performs any merge/delete action. This is an active intent mismatch between the documented behavior and the actual code path.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% 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 · cli.py (reported line 750)May include surrounding context.

python
# batch-delete
    p_batch_delete = subparsers.add_parser('batch-delete', help='Delete multiple memories')
    p_batch_delete.add_argument('ids', help='Comma-separated memory IDs')
    p_batch_delete.add_argument('--force', '-f', action='store_true', help='Skip confirmation')
    
    # batch-add-tags
    p_batch_add_tags = subparsers.add_parser('batch-add-tags', help='Add tags to multiple memories')

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

expand_summary() sends raw memory content and optional conversation context to an LLM via _call_llm() without any built-in consent, disclosure, redaction, or provider-boundary controls. In a persistence skill, memories may contain sensitive personal, operational, or secret data, so this can cause unintended exfiltration to external model services and broaden data exposure beyond the local memory store.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

After receiving an expanded response, the code overwrites memory.content at L336 and only then sets memory.metadata['original_content'] = memory.content[:100] at L339. This contradicts the intent implied by the metadata field name and the function's purpose of expanding an existing memory while retaining the original.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

suggest_tags_llm() transmits raw memory content to an LLM for categorization without user-facing disclosure or any sensitivity checks. Because this skill is specifically designed for persistent memory, the content may include confidential user preferences, work data, credentials, or conversation artifacts, making unauthorized third-party processing a meaningful privacy and data-governance risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language prompt requires tags in Chinese, including examples and a constraint of '1-3 Chinese characters each'. This forces a specific language/locale behavior without offering user choice or documenting why a Chinese-only output is required.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manager automatically sends memory content into the embedding pipeline when embeddings are enabled, and this file provides no consent gate, disclosure, or locality check before doing so. In a persistence skill, memories can contain highly sensitive conversation data, so using a remote embedding backend could cause unintended exfiltration of private information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The batch_delete() method deletes multiple memory entries directly via self.storage.delete() and returns results, but provides no confirmation prompt or visible warning about the destructive action. The same file only labels the method as 'Delete multiple memory entries,' which does not meaningfully warn a user before irreversible deletion occurs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The export function writes the full memory corpus, including potentially sensitive content and metadata, to an arbitrary file path with no restrictions, warnings, or minimization. In a persistence system for AI agents, this can create cleartext dumps of private memories that are easy to mishandle, overexpose, or write into insecure locations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The backend sends request parameters and JSON-encoded memory content over HTTP to the Gitee API, including in save/load/list operations. The file identifies itself as a storage backend, but it does not include any explicit user-facing warning or comment that memory data is transmitted to an external service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code saves and deletes memory entries by issuing POST and DELETE requests to the remote Gitee repository, which changes persisted user data. Although errors are printed, there is no confirmation prompt or explicit user-facing disclosure before these modifying operations, and the docstrings do not warn that data will be written to or removed from a remote repo.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The delete method performs a permanent DELETE against the SQLite database and immediately commits the change. While the docstring names the operation, there is no user-facing confirmation, warning, or logging around this destructive action in the code shown.

Content

No source excerpt is available for this finding.