Back to skill

Security audit

Deep Memory

Security checks for vulnerabilities and agentic risk

Overview

This skill is a legitimate-looking local memory system, but it starts persistent unauthenticated databases and host services with broad access risks.

Install only if you are comfortable running persistent local Docker services for agent memory. Before use, bind database ports to localhost, enable authentication for Neo4j and Qdrant, pin container/model versions, review what will be stored, and make sure you have a clear way to stop the containers and delete the memory volumes.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup.py:35
Finding

Unauthenticated memory databases exposed on host network interfaces

Content
View full analysis

Vulnerability Details

File Location: scripts/setup.py, lines 35-59
Vulnerability Type: Unauthenticated network service exposure
Risk Level: High

Vulnerable Code

python
DOCKER_COMPOSE_CONTENT = """version: '3.8'
services:
  qdrant:
    image: qdrant/qdrant:latest
    container_name: deep-memory-qdrant
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage
    restart: unless-stopped

  neo4j:
    image: neo4j:5-community
    container_name: deep-memory-neo4j
    ports:
      - "7474:7474"
      - "7687:7687"
    environment:
      - NEO4J_AUTH=none
      - NEO4J_PLUGINS=["apoc"]
    volumes:
      - neo4j_data:/data
    restart: unless-stopped

volumes:
  qdrant_data:
  neo4j_data:
"""

Technical Analysis

The generated Docker Compose configuration publishes Qdrant ports 6333 and 6334 and Neo4j ports 7474 and 7687 without specifying a loopback host address. Under normal Docker port-publishing behavior, these services are bound to all available host interfaces unless network or daemon configuration imposes an additional restriction.

Neo4j authentication is explicitly disabled through NEO4J_AUTH=none. Qdrant is also deployed without an API key, TLS, or another access-control mechanism. The databases contain persistent agent memories and expose APIs that support reading, inserting, updating, and deleting records.

Consequently, any party capable of reaching these ports can interact with the memory stores without presenting credentials. This creates both a confidentiality risk and a memory-poisoning path, because later searches can return content inserted or modified by an unauthorized party.

Attack Path

  1. A victim runs scripts/setup.py, which writes and launches the generated Docker Compose configuration.
  2. Docker publishes Qdrant and Neo4j on the host interfaces.
  3. An attacker on a rea ...[truncated 1362 chars]
Remediation
View remediation

Remediation Suggestions

  1. Bind every published service port exclusively to the loopback interface when remote access is unnecessary:

    yaml
    ports:
      - "127.0.0.1:6333:6333"
      - "127.0.0.1:6334:6334"
    

    Apply the same restriction to Neo4j ports 7474 and 7687.

  2. Enable Neo4j authentication by replacing NEO4J_AUTH=none with a securely generated credential. Do not commit or hardcode that credential in the skill; load it from a protected environment file or secret store.

  3. Configure Qdrant API-key authentication and require clients to send the key. Store the key with restrictive file permissions and outside version control.

  4. Use TLS for any database access that crosses a trusted local boundary.

  5. Restrict inbound access with host firewall rules and, where applicable, cloud security groups.

  6. Place the services on a private Docker network and avoid publishing database ports when only other containers require access.

  7. After service startup, verify that authentication is active and fail setup rather than reporting success when an insecure configuration remains accessible.

T08 · Insecure Dependencies

Warning
Location
scripts/setup.py:37
Finding

Mutable and unverified third-party dependencies permit supply-chain substitution

Content
View full analysis

Vulnerability Details

File Location: scripts/setup.py, lines 37, 47, 249-264
Vulnerability Type: Unpinned and unverified external dependencies
Risk Level: Medium

Vulnerable Code

yaml
qdrant:
  image: qdrant/qdrant:latest
yaml
neo4j:
  image: neo4j:5-community
python
def check_ollama():
    step(2, "Checking Ollama")
    out, err = run("which ollama")
    if err or not out:
        warn("Ollama not found. Installing via brew...")
        _, err = run("brew install ollama", capture=False, check=False)
        if err:
            fail("Could not install Ollama. Please install manually: https://ollama.ai")
    ok("Ollama found")
    # Pull model
    info("Pulling qwen3-embedding:8b (this may take a few minutes on first run)...")
    out, err = run("ollama list")
    if "qwen3-embedding" not in (out or ""):
        _, err = run("ollama pull qwen3-embedding:8b", capture=False, check=False)
    ok("qwen3-embedding:8b ready")

Technical Analysis

The setup retrieves multiple external artifacts without pinning them to immutable cryptographic identities:

  • qdrant/qdrant:latest is explicitly mutable and may resolve to different images over time.
  • neo4j:5-community is a moving major-version tag rather than an immutable image digest.
  • brew install ollama installs whichever package version the current Homebrew metadata selects.
  • ollama pull qwen3-embedding:8b retrieves a named model without validating an expected digest or signature in the setup script.

A mutable name does not independently prove that an upstream artifact is malicious. However, it means the reviewed source does not uniquely determine the code and model artifacts that will be installed. An upstream compromise, registry-account compromise, malicious mirror, or incompatible future update could therefore alter effective runtime behavior after this skill has been reviewed.

The c ...[truncated 1808 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin each container image to a tested version and immutable SHA-256 digest:

    yaml
    image: qdrant/qdrant:<tested-version>@sha256:<expected-digest>
    image: neo4j:<tested-version>-community@sha256:<expected-digest>
    
  2. Maintain approved dependency versions in a reviewed lock or manifest file, and update them through an explicit security-review process.

  3. Verify image provenance with an appropriate signing mechanism such as Sigstore/Cosign where publisher support is available.

  4. Pin the Ollama package to an approved version where the package manager supports it. Verify repository provenance and downloaded artifact checksums or signatures before execution.

  5. Record and validate the expected Ollama model digest rather than relying only on the mutable model name.

  6. Configure automated vulnerability scanning for container images and host packages before deployment.

  7. Fail closed when dependency installation, pulling, or integrity verification fails. The current calls using check=False should not allow setup to continue and report the dependency as ready after a failed retrieval.

Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (18)

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

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


name: deep-memory version: 1.0.0 description: "One-click clone of a production-grade semantic memory system: HOT/WARM/COLD tiered storage + Qdrant vector DB + Neo4j graph DB + qwen3-embedding. Enables cross-session semantic retrieval and entity relationship memory for AI agents." author: DeepEye tags: [memory, vector-db, neo4j, qdrant, embedding, semantic-search, agent-memory] requires: [docker, ollama]

Deep Memory Skill 🧠

A production-grade semantic memory system for AI agents. Combines tiered file storage with vector search and graph relationships.

Architecture

text
┌─────────────�

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Using subprocess.run with shell=True creates a command-injection sink. Even though the present commands are mostly static, this helper centralizes unsafe execution and is used with string interpolation for docker compose, so the agent-skill context makes it more dangerous if any path or future parameter becomes attacker-influenced.

Content

Scanner excerpt · scripts/setup.py (reported line 237)May include surrounding context.

python
'''

def run(cmd, capture=True, check=True):
    result = subprocess.run(cmd, shell=True, capture_output=capture, text=True)
    if check and result.returncode != 0:
        return None, result.stderr
    return result.stdout.strip() if capture else None, None

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises and instructs use of capabilities that can write files, invoke shell commands, and access networked local services, but it declares no explicit permission or tool-scope boundaries in the manifest. That increases the risk of overbroad execution during setup or runtime, especially because it launches containers, pulls models, and copies code into the workspace.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The skill is explicitly designed for cross-session memory and stores user preferences and prior context persistently, which can create privacy and security risk if sensitive data is retained longer than expected or later retrieved out of context. In an agent environment, persistent memory can also be poisoned or abused to influence future behavior across sessions.

Content

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

python3 ~/.openclaw/workspace/skills/deep-memory/scripts/setup.py

text

### Write a memory
```python
from deep_memory import MemorySystem
mem = MemorySystem()

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
98% confidence
Finding

Neo4j is started with NEO4J_AUTH=none while exposing ports 7474 and 7687, which leaves the graph database unauthenticated to any local or network-accessible client depending on Docker networking and host exposure. Because this skill is specifically designed to store long-lived semantic memory, the unsafe default directly risks unauthorized reading, tampering, and extraction of potentially sensitive memory data.

Content

Scanner excerpt · scripts/setup.py (reported line 53)May include surrounding context.

python
- "7474:7474"
      - "7687:7687"
    environment:
      - NEO4J_AUTH=none
      - NEO4J_PLUGINS=["apoc"]
    volumes:
      - neo4j_data:/data

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The generated client sends user-provided memory content and queries over local HTTP to Ollama, Qdrant, and Neo4j, but the setup and usage messaging do not clearly disclose this data flow. In a memory system, users may submit sensitive data, so undisclosed transmission to multiple services meaningfully increases privacy and exposure risk.

Content

No source excerpt is available for this finding.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · scripts/setup.py (reported line 84)May include surrounding context.

python
def get_embedding(text: str) -> list:
    payload = json.dumps({"model": EMBED_MODEL, "prompt": text}).encode()
    req = urllib.request.Request(
        "http://localhost:11434/api/embeddings",
        data=payload,
        headers={"Content-Type": "application/json"},

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · scripts/setup.py (reported line 285)May include surrounding context.

python
def get_embedding(text: str) -> list:
    payload = json.dumps({"model": EMBED_MODEL, "prompt": text}).encode()
    req = urllib.request.Request(
        "http://localhost:11434/api/embeddings",
        data=payload,
        headers={"Content-Type": "application/json"},

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · scripts/setup.py (reported line 311)May include surrounding context.

python
def get_embedding(text: str) -> list:
    payload = json.dumps({"model": EMBED_MODEL, "prompt": text}).encode()
    req = urllib.request.Request(
        "http://localhost:11434/api/embeddings",
        data=payload,
        headers={"Content-Type": "application/json"},

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
94% confidence
Finding

The helper executes shell commands with shell=True, which is dangerous because it turns command strings into shell-interpreted input. In this file the current callers use hardcoded commands, but the function is generic and also interpolates a filesystem path into one command, so future changes or attacker-controlled path content could turn this into command injection.

Content

Scanner excerpt · scripts/setup.py (reported line 237)May include surrounding context.

python
'''

def run(cmd, capture=True, check=True):
    result = subprocess.run(cmd, shell=True, capture_output=capture, text=True)
    if check and result.returncode != 0:
        return None, result.stderr
    return result.stdout.strip() if capture else None, None

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The setup script can install Ollama via Homebrew and start Docker containers immediately, but it does not ask for confirmation before performing those actions. In an automated skill context, launching software and background services without explicit consent increases the chance of unintended host changes and trust-boundary violations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The setup script installs software via Homebrew and manages Docker services automatically, which grants the skill broad host-modification capability beyond simple application configuration. In an agent skill context this is risky because one-click execution can change the system state, pull remote artifacts, and start privileged background services without granular consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script writes files under the user's workspace, creates persistent memory directories, writes a docker-compose file, and installs an executable toolkit without any up-front warning or confirmation gate. Silent system modification is risky in agent-operated environments because users may not realize the persistence and executable artifacts being created.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The skill explicitly creates durable memory structures and database constraints to support cross-session persistence. Persistent storage is the intended feature here, but in an agent setting it is still security-relevant because it can retain sensitive user or session data beyond the current interaction and make later retrieval possible.

Content

Scanner excerpt · scripts/setup.py (reported line 305)May include surrounding context.

python
step(5, "Setting up Neo4j constraints")
    import urllib.request
    queries = [
        "CREATE CONSTRAINT IF NOT EXISTS FOR (m:Memory) REQUIRE m.key IS UNIQUE",
        "CREATE CONSTRAINT IF NOT EXISTS FOR (e:Entity) REQUIRE e.id IS UNIQUE"
    ]
    payload = json.dumps({

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · scripts/setup.py (reported line 356)May include surrounding context.

python
import urllib.request
    # Test Qdrant
    try:
        req = urllib.request.Request("http://localhost:6333/collections/semantic_memories")
        urllib.request.urlopen(req, timeout=5)
        ok("Qdrant reachable ✓")
    except Exception:

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · scripts/setup.py (reported line 363)May include surrounding context.

python
import urllib.request
    # Test Qdrant
    try:
        req = urllib.request.Request("http://localhost:6333/collections/semantic_memories")
        urllib.request.urlopen(req, timeout=5)
        ok("Qdrant reachable ✓")
    except Exception:

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · scripts/setup.py (reported line 370)May include surrounding context.

python
import urllib.request
    # Test Qdrant
    try:
        req = urllib.request.Request("http://localhost:6333/collections/semantic_memories")
        urllib.request.urlopen(req, timeout=5)
        ok("Qdrant reachable ✓")
    except Exception:

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
71% confidence
Finding

The manifest emphasizes cross-session semantic retrieval and entity relationship memory, but this file writes out a client that can insert and update records in both Qdrant and Neo4j. Write support may be a reasonable part of a memory system, but it is not expressly described in the manifest language here.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.