Back to skill

Security audit

wenshuangl/agent-mem

Security checks for vulnerabilities and agentic risk

Overview

This memory-and-dispatch skill is broadly purpose-aligned, but it needs review because it can persist and redistribute user memory across agents with weak scoping and has unsafe file and network handling paths.

Install only if you are comfortable with a local memory system that persists conversation-derived facts and may share them across agents. Review or patch agent ID validation, cross-agent sharing enforcement, gateway URL allowlisting, retention/purge controls, and the background bridge launch behavior before using it with private, business, financial, or credential-adjacent content.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
hot_cache.py:25
Finding
Arbitrary JSON File Access Through Agent ID Path Traversal## Vulnerability Details **File Location**: `hot_cache.py:25-105` **Vulnerability Type**: Path traversal leading to arbitrary file read, overwrite, and deletion **Risk Level**: High ```python def _file_path(agent_id: str) -> Path: return CACHE_DIR / f"{agent_id}.json" def _load(agent_id: str) -> list: path = _file_path(agent_id) if path.exists(): try: return json.load(open(path)) except: return [] return [] def _save(agent_id: str, entries: list): json.dump( entries, open(_file_path(agent_id), 'w'), indent=2, ensure_ascii=False ) def clear_agent(agent_id: str): """Clear the HOT cache for an agent.""" path = _file_path(agent_id) if path.exists(): path.unlink() return True return False ``` ### Technical Analysis The externally supplied `agent_id` is interpolated directly into a filesystem path. The code does not restrict path separators, traversal components such as `..`, or absolute paths. `pathlib` therefore permits the resulting path to escape `~/.agent-mem/hot_cache`. The unsafe path is used by three security-sensitive operations: - `_load()` reads and parses the selected JSON file. - `_save()` opens the selected file in write mode and replaces its contents. - `clear_agent()` deletes the selected file. Although the `.json` suffix limits the immediately reachable filenames, it does not prevent access to sensitive JSON files elsewhere in the account. The CLI exposes these operations through attacker-controlled `--agent` values. ### Attack Path 1. An attacker obtains the ability to invoke the HOT-cache CLI or an application endpoint that passes an agent identifier to these functions. 2. The attacker supplies an agent identifier containing an absolute path or traversal components, for example `../../other-directory/config`. 3. `_file_path()` const ...[truncated 820 chars]
Remediation
## Remediation Suggestions - Validate agent identifiers against a strict allowlist such as `^[A-Za-z0-9_-]{1,64}$`. - Reject absolute paths, path separators, `.` components, and `..` components. - Resolve the destination and verify containment before every operation: ```python import re AGENT_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]{1,64}$") def _file_path(agent_id: str) -> Path: if not AGENT_ID_PATTERN.fullmatch(agent_id): raise ValueError("Invalid agent ID") base = CACHE_DIR.resolve() path = (base / f"{agent_id}.json").resolve() if path.parent != base: raise ValueError("Cache path escapes the cache directory") return path ``` - Use atomic writes through a temporary file created inside `CACHE_DIR`, followed by `os.replace()`. - Create cache files with restrictive permissions such as `0600`. - Apply authorization checks so callers can access only their permitted agent IDs. - Add regression tests covering absolute paths, nested paths, URL-encoded separators, and `../` traversal.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
engine_v2.py:285
Finding
Cross-Agent Memory Isolation Bypass## Vulnerability Details **File Location**: `engine_v2.py:285-304` **Related Policy Location**: `multi_agent_share.py:33-40, 70-98` **Vulnerability Type**: Unauthorized cross-agent disclosure of persistent memory **Risk Level**: High ```python try: from agent_mem.core.dispatch_logger import sync_to_engine facts = self.state.get('last_facts', []) agents_list = self._get_active_agents() count = 0 if facts: important = [ f for f in facts if f.get('importance', 5) >= 7 ][:20] print( f' 🔥 Engine extraction: {len(facts)} items, ' f'important: {len(important)} items' ) # Write facts into every agent HOT cache. for fact in (important + facts[:10]): f_text = fact.get('text', '')[:200] f_imp = fact.get('importance', 5) f_cat = fact.get('category', 'general') for aid in agents_list: write_conversation( aid, 'internal', f'[{f_cat}] {f_text}', f_imp ) count += 1 print( f' Distributed to {len(agents_list)} agents ' f'(HOT cache)' ) ``` The separate sharing module defines isolation rules: ```python return { 'share_rules': {}, 'default_rules': { 'system_change': {'agents': ['*'], 'enabled': True}, 'tech': { 'agents': ['tech-expert', 'code-architect'], 'enabled': True }, 'work': { 'agents': ['work-assistant', 'advertising-agent'], 'enabled': True }, 'finance': { 'agents': ['finance-assistant'], 'enabled': True }, 'general': {'agents': ['*'], 'enabled': False}, }, ...[truncated 2123 chars]
Remediation
## Remediation Suggestions - Route every cross-agent transfer through one centralized authorization function. - Call `MultiAgentMemory.can_share(category, target_agent)` before writing a fact to a target cache. - Apply a default-deny policy for unknown and `general` categories. - Ensure the category vocabulary is consistent: the isolation rule uses `personal`, while the extractor defines `person`. - Never distribute `person`, `personal`, `preference`, credential-related, or explicitly private memories without affirmative user consent. - Track the originating agent and prevent transfers back into unrelated trust domains. - Store an auditable record of the policy decision for each transfer without duplicating sensitive content in logs. - Add tests proving that isolated categories cannot be distributed by `sync_hot_cache()`. - Consider per-agent encryption or separate filesystem permissions if agents operate under distinct security principals. Example enforcement: ```python for aid in agents_list: if not self.multi_agent_share.can_share(f_cat, aid): continue write_conversation( aid, 'internal', f'[{f_cat}] {f_text}', f_imp ) ```

T09 · Insecure Skill Coding Practices

Error
Location
dispatch.py:239
Finding
Server-Side Request Forgery and Task Disclosure Through Unrestricted Gateway URL## Vulnerability Details **File Location**: `dispatch.py:239-270` **Vulnerability Type**: Server-side request forgery and sensitive task exfiltration **Risk Level**: High ```python def auto_dispatch(self, result, gateway_url=''): """Automatically dispatch to a target agent.""" agent = result.get('agent') task = result.get('task', '') intent = result.get('intent', 'unknown') if not agent or agent == 'main': return {'ok': False, 'reason': 'No dispatch required'} if not gateway_url: return { 'ok': True, 'agent': agent, 'dispatched': False, 'message': 'No gateway URL configured' } import requests try: payload = { 'agentId': agent, 'message': ( f"## {intent} task\n\n{task}\n\n---\n" f"*Source: automatic dispatcher*" ), 'timeoutSeconds': 120 } r = requests.post( f'{gateway_url.rstrip("/")}/api/sessions/send', json=payload, timeout=10 ) if r.status_code == 200: return { 'ok': True, 'agent': agent, 'dispatched': True } return {'ok': False, 'error': f'HTTP {r.status_code}'} except Exception as e: return {'ok': False, 'error': str(e)} ``` ### Technical Analysis `auto_dispatch()` accepts a gateway URL and performs a server-side HTTP POST without validating: - The URL scheme. - The hostname or port. - Resolved IP addresses. - Loopback, private, link-local, or cloud metadata address ranges. - Redirect destinations. - Whether TLS is required. - Whether the destination is an approved dispatch gateway. The request body includes the complete task text. A malicious destination therefore receives potential ...[truncated 1769 chars]
Remediation
## Remediation Suggestions - Remove caller control over the gateway URL and load it from trusted administrator configuration. - Require HTTPS except for an explicitly enabled local-development mode. - Maintain an exact allowlist of approved hostnames and ports. - Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified addresses. - Repeat address validation after DNS resolution and for every redirect destination. - Disable redirects unless explicitly required. - Normalize and parse URLs with `urllib.parse` rather than validating through string prefixes. - Redact secrets and unnecessary user content from dispatch payloads. - Add explicit user consent before sending sensitive task content outside the local system. - Apply outbound firewall or proxy controls as defense in depth. - Return generic errors to untrusted callers to reduce internal-network enumeration. Example policy: ```python from urllib.parse import urlparse parsed = urlparse(gateway_url) if parsed.scheme != "https": raise ValueError("Only HTTPS gateways are allowed") if parsed.hostname not in APPROVED_GATEWAY_HOSTS: raise ValueError("Unapproved gateway host") r = requests.post( approved_endpoint, json=payload, timeout=10, allow_redirects=False ) ```

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unbounded and Unverified Third-Party Dependency Resolution## Vulnerability Details **File Location**: `requirements.txt:1` **Related Locations**: `setup.py:8-10`, `SKILL.md:10,38` **Vulnerability Type**: Non-reproducible dependency installation and supply-chain exposure **Risk Level**: Medium ```text chromadb>=0.4.0 ``` The package metadata contains the same open-ended constraint: ```python install_requires=[ "chromadb>=0.4.0", ], ``` Installation is documented as: ```bash pip install -e . ``` ### Technical Analysis The project accepts any future `chromadb` release satisfying the minimum version and does not provide: - An upper version bound. - A lock file. - Package hashes. - Pinned transitive dependency versions. - A documented dependency review or update process. This causes installations performed at different times to resolve different code. Because Python packages and their transitive dependencies execute within the application's process, a compromised, malicious, or unexpectedly incompatible future release could affect the confidentiality and integrity of locally stored memory. The reviewed evidence does not establish that the current `chromadb` package is malicious. The confirmed issue is unsafe dependency resolution and lack of reproducible verification. ### Attack Path 1. A user follows the documented `pip install -e .` command. 2. Pip queries the configured package index and resolves the newest dependency set satisfying `chromadb>=0.4.0`. 3. A future compromised release, compromised transitive dependency, or unsafe package-index configuration is selected. 4. Dependency code executes during installation, import, or normal vector-store operations with the privileges of the installing or running user. 5. Such code could access AgentMem files, memory databases, environment variables, and other resources available to that account. ### Impact Assessment If dependency resolution selects a compromised component, the comp ...[truncated 458 chars]
Remediation
## Remediation Suggestions - Pin `chromadb` to a reviewed exact version. - Generate a lock file that includes all transitive dependencies. - Require cryptographic hashes during deployment, for example through `pip-compile --generate-hashes` and `pip install --require-hashes`. - Test updates in an isolated environment before changing pins. - Use a trusted internal package mirror or repository allowlist. - Run dependency vulnerability and provenance checks in CI. - Install and run the package as a dedicated, unprivileged operating-system user. - Document an intentional dependency update process so security patches can be adopted without returning to unrestricted resolution.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (95)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared local persistence in files such as .memory-feedback.json and .memory-corrections.json is relevant because it stores potentially sensitive prompt or behavioral data outside clearly documented retention controls. Hidden persistence can surprise users, leak private content through backups or shared workspaces, and complicate secure deletion expectations.

exec() call detected

High
Category
Dangerous Code Execution
Content
}
        for attr, (cls_name, imp) in modules.items():
            try:
                exec(imp)
                klass = eval(cls_name)
                setattr(self, attr, klass(self.memory_dir))
                self.state[f'{attr}_loaded'] = True
Confidence
97% confidence
Finding
Using exec() to perform imports executes dynamically constructed Python code, which is dangerous because any compromise of the imported string or surrounding module-loading logic can lead to arbitrary code execution. In this skill, the engine loads many optional modules from local paths after modifying sys.path, making the dynamic execution surface larger and harder to audit.

eval() call detected

High
Category
Dangerous Code Execution
Content
for attr, (cls_name, imp) in modules.items():
            try:
                exec(imp)
                klass = eval(cls_name)
                setattr(self, attr, klass(self.memory_dir))
                self.state[f'{attr}_loaded'] = True
            except Exception as e:
Confidence
96% confidence
Finding
eval() is used to resolve a class name dynamically after exec-based importing. Even though the current strings are hard-coded, eval unnecessarily enables arbitrary expression execution if the mapping is ever influenced by external input or a compromised module-loading path.

Ssd 3

High
Confidence
97% confidence
Finding
Vector sync ingests substantial portions of memory files into a searchable store, making previously transient or less-accessible text easy to retrieve later through semantic queries. For conversation memory, this increases both retention and discoverability of sensitive content, which heightens disclosure risk even without external exfiltration.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
The skill launches an external dispatch-learning script in the background without explicit invocation, creating covert execution and expanding behavior beyond what a memory engine would normally require. Because this skill handles persistent memory and cross-agent dispatch, an external learner process can observe, transform, or redistribute sensitive memory data without clear control.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
engine_v2.py:75