Back to skill

Security audit

Agent Brain

Security checks for vulnerabilities and agentic risk

Overview

This memory skill is coherent, but it silently builds long-lived user and project memory and can mirror full memory entries to a cloud endpoint when credentials are present.

Install only if you want an agent to maintain persistent memory about you and your projects. Review or disable automatic extraction, set AGENT_BRAIN_SUPERMEMORY_SYNC=off unless cloud mirroring is intentionally approved, avoid storing secrets or regulated data, and inspect exported memory regularly because superseded and logged data can remain durable.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/brain.py:222
Finding
Automatic cloud mirroring can disclose persistent memory without explicit per-write consent<![CDATA[ ## Vulnerability Details **File Location**: `scripts/brain.py:222-299`, `scripts/brain.py:748-778`, `scripts/memory.sh:49-77`, `_meta.json:11-14` **Vulnerability Type**: Automatic transmission of sensitive persistent data **Risk Level**: High ### Vulnerable Code ```python def _supermemory_mode() -> str: return os.environ.get("AGENT_BRAIN_SUPERMEMORY_SYNC", "auto").strip().lower() def _supermemory_api_key() -> str: return os.environ.get("SUPERMEMORY_API_KEY", "").strip() def _maybe_sync_supermemory(entry: dict): mode = _supermemory_mode() if _is_falsy(mode): return api_key = _supermemory_api_key() if not api_key: if mode != "auto" and _is_truthy(os.environ.get("AGENT_BRAIN_SUPERMEMORY_DEBUG", "")): print("SuperMemory sync warning: SUPERMEMORY_API_KEY is not set", file=sys.stderr) return memory_payload = { "origin": "agent-brain", "entry_id": entry.get("id"), "type": entry.get("type"), "content": entry.get("content"), "source": entry.get("source"), "source_url": entry.get("source_url"), "tags": entry.get("tags", []), "context": entry.get("context"), "created": entry.get("created"), "confidence": entry.get("confidence"), "session_id": entry.get("session_id"), "correction_meta": entry.get("correction_meta"), } tags = [_sanitize_supermemory_tag("agent-brain"), _sanitize_supermemory_tag(entry.get("type", "entry"))] for tag in entry.get("tags", []): if len(tags) >= 8: break tags.append(_sanitize_supermemory_tag(tag)) tags = list(dict.fromkeys(tags)) req_payload = { "content": json.dumps(memory_payload, ensure_ascii=False), "customId": f"agent_brain_{entry.get('id', uuid.uuid4())}", "containerTags": tags, } api_url = os.environ.get( "SUPERMEMORY_API_URL", "https://api.supermemory.ai/v3/documents", ...[truncated 4384 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change `AGENT_BRAIN_SUPERMEMORY_SYNC` to `off` by default. 2. Require explicit, informed user consent before enabling cloud synchronization. 3. Avoid automatically loading cloud credentials from a generic `.env` file. 4. Display the destination and data categories before the first outbound transfer. 5. Require HTTPS and restrict `SUPERMEMORY_API_URL` to an explicit allowlist. 6. Reject URLs containing user information and revalidate the final connection destination. 7. Minimize outbound payloads; omit correction history, context, session IDs, and source URLs unless individually required. 8. Apply comprehensive sensitive-data detection and redaction to every outbound field. 9. Provide a separate command for explicit synchronization instead of invoking it implicitly after every write. 10. Add tests proving that no network request occurs under the default configuration. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/brain.py:99
Finding
Incomplete and bypassable sensitive-data validation can expose secrets through storage and synchronization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/brain.py:99-118`, `scripts/brain.py:495-528`, `scripts/brain.py:963-980`, `scripts/brain.py:1086-1125` **Vulnerability Type**: Incomplete sensitive-data filtering and validation bypass **Risk Level**: High ### Vulnerable Code The sensitive-data detector uses a limited set of regular expressions: ```python def pii_mode() -> str: return os.environ.get("AGENT_BRAIN_PII_MODE", "strict").strip().lower() def is_sensitive_content(text: str) -> bool: if pii_mode() in {"off", "false", "0"}: return False patterns = [ r"-----BEGIN (RSA|EC|OPENSSH|PRIVATE) KEY-----", r"\bAKIA[0-9A-Z]{16}\b", r"\b(?:sk|rk|pk)_(?:live|test)?[A-Za-z0-9]{16,}\b", r"\bpassword\s*[:=]\s*\S+", r"\bapi[_-]?key\s*[:=]\s*\S+", r"\btoken\s*[:=]\s*\S+", r"\bsecret\s*[:=]\s*\S+", r"\bghp_[A-Za-z0-9]{20,}\b", ] return any(re.search(p, text, re.IGNORECASE) for p in patterns) ``` Validation is applied only to the main content during entry creation: ```python def build_entry( store, entry_type: str, content: str, source: str = "user", tags: list[str] | None = None, source_url: str | None = None, context: str | None = None, confidence: str | None = None, correction_meta: dict | None = None, ) -> dict: if is_sensitive_content(content): raise ValueError("Sensitive content detected; refusing to store.") ts = now_str() session = store.get_session() session_id = session["id"] if session else None entry_conf = confidence or ("sure" if source == "user" else "likely") return { "id": str(uuid.uuid4()), "type": entry_type, "memory_class": infer_memory_class(entry_type), "content": content, "source": source, "source_url": source_url if source_url else None, "tags": tags or [], "context": context if context else None, "se ...[truncated 4414 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Centralize validation in the storage layer so every insert and update passes through the same policy. 2. Validate and redact `content`, `context`, `source_url`, tags, correction metadata, session context, and log details. 3. Apply validation in `cmd_update()` before changing `content` or `context`. 4. Validate `wrong_claim`, `right_claim`, and `reason` before creating a correction. 5. Do not transmit the original incorrect claim unless it is strictly necessary and explicitly approved. 6. Expand detection to cover SSNs, JWTs, bearer tokens, authorization headers, database URLs, cloud credentials, private keys, and common secret formats. 7. Treat regex detection only as defense in depth; use outbound data minimization and explicit user confirmation as the primary controls. 8. Never record complete user-controlled replacement values in activity logs; log field names and entry identifiers instead. 9. Re-scan existing entries before cloud synchronization or remote embedding requests. 10. Add regression tests for update, correction metadata, context, source URL, SSNs, JWTs, connection strings, and generic bearer credentials. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
modules/ingest/SKILL.md:7
Finding
Prompt-only URL validation permits SSRF through DNS, redirects, and unhandled address ranges<![CDATA[ ## Vulnerability Details **File Location**: `modules/ingest/SKILL.md:7-33` **Vulnerability Type**: Server-Side Request Forgery protection bypass **Risk Level**: Medium ### Vulnerable Code ```markdown ## ⚠️ Security **Disabled by default.** To enable, the orchestrator must: 1. Only process URLs explicitly provided by the user in conversation 2. Never auto-fetch URLs found in text, documents, or memory 3. Validate URLs before fetching (see Validation below) ### URL Validation REJECT any URL matching: - `localhost`, `127.0.0.1`, `0.0.0.0`, `::1` - `file://`, `ftp://`, `gopher://` - Private IP ranges: `10.*`, `172.16-31.*`, `192.168.*` - Internal hostnames without dots ALLOW only: - `https://` URLs on public domains - `http://` only if user explicitly confirms ## How It Works ### Step 1: Fetch Use the runtime's web fetch capability to retrieve the URL content. ```bash # The agent runtime handles fetching — this module processes the result # Content arrives as text extracted from the page ``` ``` ### Technical Analysis The ingestion module correctly recognizes SSRF risk and is disabled by default, but its safeguards exist only as natural-language instructions. There is no dedicated validator that normalizes and resolves the URL before the runtime performs the request. The rejection list is incomplete. It does not explicitly address: - IPv4 link-local addresses such as `169.254.169.254`; - IPv6 private, link-local, unspecified, multicast, or mapped IPv4 addresses; - decimal, hexadecimal, octal, or mixed IP representations; - hostnames that resolve to loopback, private, link-local, or reserved addresses; - DNS rebinding; - redirects from an initially public URL to a prohibited destination; - URLs containing user-information or parser-confusion syntax; - additional reserved or special-purpose address ranges. Because the workflow delegates the actual fetch to an unspecified runtime capability, its safety depends on the agent interpreting the text ...[truncated 1359 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Implement URL validation in executable code rather than relying on prompt instructions. 2. Permit HTTPS only unless a narrowly scoped exception is explicitly approved. 3. Parse and normalize URLs with a standard URL parser; reject credentials, fragments where inappropriate, malformed ports, and ambiguous hosts. 4. Resolve every hostname and reject all loopback, private, link-local, multicast, reserved, unspecified, and special-purpose IPv4 and IPv6 addresses. 5. Revalidate every redirect target before following it. 6. Prevent DNS rebinding by connecting only to an already validated resolved address while preserving correct TLS hostname verification. 7. Apply outbound network allowlists or an isolated fetching proxy. 8. Block cloud metadata destinations at the network layer, including `169.254.169.254`. 9. Limit response size, content type, timeout, and redirect count. 10. Treat fetched content as untrusted data and prevent it from supplying agent instructions or tool commands. ]]>
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (37)

Tainted flow: 'request' from os.environ.get (line 280, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
},
            method="POST",
        )
        with urllib.request.urlopen(request, timeout=timeout) as response:
            if response.status >= 300 and debug:
                print(f"SuperMemory sync warning: HTTP {response.status}", file=sys.stderr)
    except urllib.error.HTTPError as exc:
Confidence
97% confidence
Finding
The SuperMemory sync path sends stored memory fields including content, context, source_url, session_id, and correction metadata to a network endpoint whose URL is controllable via environment variables. In a memory skill handling user-entered data, this creates a real exfiltration path and SSRF-like risk, especially because the PII screening is heuristic and incomplete and the feature is only loosely disclosed as 'optional'.

Tainted flow: 'req' from os.environ.get (line 427, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
timeout = float(os.environ.get("AGENT_BRAIN_EMBEDDING_TIMEOUT", "6"))
    req = urllib.request.Request(url, data=payload, headers=headers, method="POST")
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            body = json.loads(resp.read().decode("utf-8"))
            if isinstance(body, dict) and isinstance(body.get("embedding"), list):
                vec = body["embedding"]
Confidence
96% confidence
Finding
The remote embedding feature transmits query text and every active memory entry's content to an externally configured URL, also sourced from environment configuration. That is a genuine data-leakage channel and can also be abused for SSRF or sending sensitive conversation-derived content to an untrusted service without robust validation or disclosure.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a fairly advanced local-first memory system centered on SQLite storage plus higher-level orchestration and retrieval behaviors. This code chunk instead provides a legacy JSON-backed persistence layer. Its behavior is limited to CRUD-style storage operations, schema migration, session/meta tracking, activity logging, supersession markers, and decay hooks. Those are related to memory persistence, so the domain is adjacent, but the storage resource is materially different (JSON file vs SQLite), and the advertised advanced capabilities are not present in this code. Therefore the description does not accurately represent what this specific supplied code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a sophisticated persistent memory skill with storage, retrieval orchestration, and learning features. The provided code instead only validates metadata structure in a JSON file. Its primary purpose is entirely different and unrelated to the declared memory-system behavior. There is no evidence of SQLite usage, agent memory management, retrieval loops, contradiction checks, correction learning, or SuperMemory mirroring.

Ssd 3

High
Confidence
98% confidence
Finding
Directing the agent to extract and persist information from every user message creates a covert data collection channel. In context, the skill is specifically built to accumulate long-lived user, project, preference, and workflow data, so the risk is not incidental but core to operation and can capture secrets, internal architecture, and personal information over time.

Ssd 3

High
Confidence
99% confidence
Finding
The workflow explicitly says to scan every message, check conflicts, and then add user-derived content while keeping extraction silent. This creates sustained privacy and data-governance risk because users are not told that ordinary conversation is being converted into durable records, potentially including sensitive technical and behavioral data.

Ae1

High
Category
analysis-evasion
Content
All operations go through `scripts/memory.sh` → `scripts/brain.py` with a pluggable
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All operations go through `scripts/memory.sh` → `scripts/brain.py` with a pluggable
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

High
Confidence
99% confidence
Finding
The skill requires fact extraction from every user message but does not pair that requirement with a clear privacy warning or consent model. Systematic capture of user data from all messages materially increases privacy risk because users may disclose personal or organizational information incidentally, not expecting long-term retention.

Ssd 3

High
Confidence
99% confidence
Finding
This section operationalizes broad surveillance-style collection by directing the agent to actively scan every message, classify extractable personal and project information, and persist it silently. In the context of a persistent SQLite-backed memory system, this is especially dangerous because it normalizes covert profiling and retention of sensitive contextual data at scale.

Missing User Warnings

High
Confidence
98% confidence
Finding
The skill explicitly instructs the agent to store user-derived information silently and to avoid telling the user that retention is happening. That creates a privacy and consent problem, especially because the stored data includes preferences, identity, work, and project details that may persist across sessions without meaningful notice.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
### Frustrated
- Don't over-explain
- Don't add caveats or disclaimers
- Lead with the fix
- Skip "Great question!" pleasantries
Confidence
86% confidence
Finding
The instruction 'Don't add caveats or disclaimers' for frustrated users can suppress safety-relevant qualifiers precisely when nuanced limitations, risks, or uncertainty should be communicated. In practice this can bias the agent toward overly direct answers that omit warnings, constraints, or refusal language, increasing the chance of unsafe or non-compliant assistance.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
[[ -f "$MEMORY_DIR/memory.json" ]] && cp "$MEMORY_DIR/memory.json" "$BACKUP_JSON"

cleanup() {
  rm -f "$MEMORY_DIR/memory.db" "$MEMORY_DIR/memory.json" "$MEMORY_DIR/memory.json.bak"
  [[ -f "$BACKUP_DB" ]] && mv "$BACKUP_DB" "$MEMORY_DIR/memory.db"
  [[ -f "$BACKUP_JSON" ]] && mv "$BACKUP_JSON" "$MEMORY_DIR/memory.json"
}
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
[[ -f "$MEMORY_DIR/memory.json" ]] && cp "$MEMORY_DIR/memory.json" "$BACKUP_JSON"

cleanup() {
  rm -f "$MEMORY_DIR/memory.db" "$MEMORY_DIR/memory.json" "$MEMORY_DIR/memory.json.bak"
  [[ -f "$BACKUP_DB" ]] && mv "$BACKUP_DB" "$MEMORY_DIR/memory.db"
  [[ -f "$BACKUP_JSON" ]] && mv "$BACKUP_JSON" "$MEMORY_DIR/memory.json"
}
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Ssd 3

High
Confidence
95% confidence
Finding
User-provided memory content is packaged and sent in plain form to an external API, while the sensitive-content detector only covers a narrow set of patterns and misses many forms of PII, credentials, and proprietary text. In a long-lived memory system, even non-secret snippets can accumulate into highly sensitive profiles, making partial screening insufficient.

Ssd 3

High
Confidence
96% confidence
Finding
The embedding request sends raw input text for queries and memory entries to a remote endpoint, exposing conversation-derived semantics and potentially sensitive content. Because embeddings are computed over all active entries, the leakage scope can include the entire memory corpus, not just the current query.

Credential Access

High
Category
Privilege Escalation
Content
assert_export_field ".get('current_session')" "None" "init sets current_session to null"
meta_check=$("$SCRIPT_DIR/validate_meta.sh" 2>&1)
assert_contains "$meta_check" "Metadata valid" "metadata JSON validates"
if grep -q "../../supermemory/.env" "$MEMORY_SH"; then
  fail "memory.sh is standalone" "Found cross-skill env path ../../supermemory/.env"
else
  pass "memory.sh is standalone"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
assert_export_field ".get('current_session')" "None" "init sets current_session to null"
meta_check=$("$SCRIPT_DIR/validate_meta.sh" 2>&1)
assert_contains "$meta_check" "Metadata valid" "metadata JSON validates"
if grep -q "../../supermemory/.env" "$MEMORY_SH"; then
  fail "memory.sh is standalone" "Found cross-skill env path ../../supermemory/.env"
else
  pass "memory.sh is standalone"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill describes capabilities that require file access, environment-variable access, and optional network transmission, but it does not declare any tool scope or permissions boundaries. That omission increases the chance an agent runtime grants broader access than users expect, especially because the skill persists user data and may sync it externally.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill instructs the agent to retain and use user information without explicit, user-facing notice or consent. Silent memory capture undermines informed consent and can cause privacy harm, especially when users share sensitive project, personal, or operational details assuming only ephemeral processing.

Ssd 4

Medium
Confidence
93% confidence
Finding
The retrieve-then-silent-extract loop promotes progressive profiling: prior stored data silently shapes responses while new data is silently added each turn. That combination increases user trust while hiding the extent of retention, making long-term privacy harm and unanticipated secondary use more likely.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill describes optional cloud mirroring of stored memory but does not present a prominent warning or consent flow to users whose data may be sent to an external service. Because the memory system is designed to collect facts from normal conversation, external sync materially raises confidentiality and compliance risk.

External Transmission

Medium
Category
Data Exfiltration
Content
AGENT_BRAIN_SUPERMEMORY_SYNC=on    (force sync attempt)
AGENT_BRAIN_SUPERMEMORY_SYNC=off   (disable sync)
SUPERMEMORY_API_KEY=...            (required for auto/on)
SUPERMEMORY_API_URL=...            (optional; default https://api.supermemory.ai/v3/documents)
AGENT_BRAIN_SUPERMEMORY_TIMEOUT=8  (optional timeout seconds)
AGENT_BRAIN_SUPERMEMORY_DEBUG=1    (optional sync warnings to stderr)
```
Confidence
94% confidence
Finding
The documented default external endpoint enables transmission of retained memory to a third-party service when sync is enabled or auto-detected via API key. In this skill's context, the transmitted data may include accumulated conversational facts, preferences, workflows, and project details, so outbound transfer meaningfully expands the attack surface and confidentiality risk.

Ssd 3

Medium
Confidence
96% confidence
Finding
The instruction to silently apply retrieved memory without disclosing that it came from stored records makes the agent's personalization opaque. That undermines informed user control and can conceal the extent of historical profiling, especially when prior stored data influences responses in ways the user cannot audit or challenge.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrase "Summarize what you've read" is broad enough to match ordinary conversation rather than a clearly scoped ingest-memory command. In an agent skill context, that ambiguity can cause unintended invocation of the ingest module and disclosure or aggregation of previously ingested content without the user explicitly requesting this specific capability.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/test_memory.sh:80