Back to skill

Security audit

voice-agent-memory

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent voice-agent memory purpose, but its bridge exposes caller memory and paid AI access with weak or missing authentication controls.

Install only after reviewing the bridge code and hardening it: enforce token rejection, protect all history/admin endpoints, verify caller identity from the telephony provider, disable or gate automatic memory storage unless callers have consented, and avoid exposing the bridge publicly until those controls are fixed.

Vulnerability Patterns
  • 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
  • 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
Findings (8)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
bridge/server.py:81
Finding

Authentication Is Deliberately Fail-Open

Content
View full analysis
str: auth = request.headers.get("authorization", "") token = auth.replace("Bearer ", "").strip() # Also check x-api-key header (ElevenLabs sometimes uses this) x_api_key = request.headers.get("x-api-key", "") if not token and x_api_key: token = x_api_key.strip() if token and token != LLM_BRIDGE_TOKEN: logger.warning(f"Invalid auth token (accepting anyway): {token[:15]}...") return token or "anonymous" ``` The result is called but never checked: ```python raw_body = await request.body() body_str = raw_body.decode("utf-8", errors="replace") await verify_token(request) # Parse body body = {} ``` ### Technical Analysis `verify_token()` accepts all three authentication states: 1. A valid bridge token. 2. An invalid bridge token. 3. No token at all, represented as `"anonymous"`. The explicit `"accepting anyway"` branch makes the documented Bearer-token control nonfunctional. The caller does not inspect the returned identity or reject anonymous access. Because the bridge invokes Anthropic and BlueColumn with credentials held by the server, remote clients effectively gain indirect use of those privileged credentials even though the credentials themselves are not returned. The warning also logs the first 15 characters of an attacker-supplied token. Token values should not be logged because users may accidentally submit real credentials for another service. ### Attack Path 1. The operator exposes port 8013 through the documented Cloudflare or ngrok tunnel. 2. An attacker sends `POST /v1/chat/completions` with no `Authorization` header, or with any arbitrary Bearer token. 3. `verify_token()` returns `"anonymous"` or logs the mi ...[truncated 911 chars]
Remediation
View remediation
None: auth = request.headers.get("authorization", "") token = auth.removeprefix("Bearer ").strip() if not token: token = request.headers.get("x-api-key", "").strip() if not LLM_BRIDGE_TOKEN or not secrets.compare_digest(token, LLM_BRIDGE_TOKEN): raise HTTPException(status_code=401, detail="Invalid authentication") ``` ]]>

T01 · Skill Instruction Hijacking

Error
Location
bridge/server.py:179
Finding

Untrusted Request Content Is Injected into the System Prompt

Content
View full analysis
Remediation
View remediation

T02 · Agent Memory Poisoning

Error
Location
bridge/server.py:196
Finding

Automatic Transcript Storage Enables Persistent Memory Poisoning

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
bridge/server.py:216
Finding

Caller Identity Spoofing Defeats Memory Isolation and Contact Screening

Content
View full analysis
str: """Extract caller phone number from ElevenLabs request metadata.""" # Check top-level metadata metadata = body.get("metadata", {}) or {} caller = metadata.get("caller_number", "") or metadata.get("from_number", "") or metadata.get("phone", "") # Check system messages for caller info if not caller: for m in messages: if m.get("role") == "system": content = m.get("content", "") # ElevenLabs injects caller info in system message like: # "Caller number: +12065550123" import re match = re.search(r'(\+?\d[\d\-\(\)\s]{7,}\d)', content) if match: caller = match.group(1).strip() break # Clean up caller = caller.replace("-", "").replace("(", "").replace(")", "").replace(" ", "") return caller ``` That unverified identifier is used to form a memory query: ```python def _build_recall_query(caller_number: str) -> str: name = _lookup_caller_name(caller_number) if name: return f"past conversations with {name} ({caller_number}) history context preferences" return f"caller {caller_number} past conversations history context preferences" ``` The contacts file claims unlisted callers will be screened: ```json { "+12065550123": { "name": "Example Contact", "role": "Example user", "notes": "Replace with your real contacts. Callers not listed will be screened." } } ``` No screening or allowlist enforcement is implemented. ### Technical Analysis A caller id ...[truncated 1672 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
bridge/server.py:428
Finding

Call History Is Exposed Without Authentication

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/test-call.sh:7
Finding

Shell Scripts Unsafely Evaluate Dotenv Content

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/test-call.sh:12
Finding

Test Call Script Constructs JSON Through Unsafe String Interpolation

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Dependencies Are Installed Without Exact Version or Integrity Pinning

Content
View full analysis
=0.110.0 uvicorn>=0.27.0 httpx>=0.27.0 anthropic>=0.30.0 python-dotenv>=1.0.0 ``` The documentation instructs users to install these unconstrained dependencies: ```bash pip install -r requirements.txt ``` ### Technical Analysis Every dependency uses only a lower bound. A future installation can therefore select any later compatible release available from the configured package index. There is no lockfile or hash verification. This makes installations non-reproducible and increases exposure to future compromised releases, unexpected major-version behavior, or dependency-resolution changes. No typosquatted package or currently malicious package was identified in the audited file; the finding concerns the unsafe version and integrity policy. ### Attack Path 1. A user runs the documented `pip install -r requirements.txt`. 2. The resolver selects the newest releases satisfying the lower bounds at that time. 3. A future compromised, vulnerable, or incompatible release is downloaded. 4. Package installation or import executes code from that release. 5. The dependency code runs with the privileges of the user operating the bridge. ### Impact Assessment A compromised dependency could access: - Anthropic and BlueColumn API keys loaded into the process. - Caller phone numbers and conversation text. - Local transcript and cost-log directories. - Network access available to the bridge process. - Files available to the operating-system account. The current repository does not itself prove that any listed package version is malicious. ]]>
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (48)

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The skill requests read access to its .env file, which commonly contains API keys, auth tokens, and other secrets for Twilio, Anthropic, ElevenLabs, and BlueColumn. Granting runtime access to raw credential stores materially increases the blast radius of any compromise or misuse, especially in a skill that also performs network operations to multiple external services.

Content

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

md
- "https://api.anthropic.com"
  files:
    read:
      - "~/.openclaw/workspace/skills/voice-agent-memory/.env"
      - "~/.openclaw/workspace/skills/voice-agent-memory/contacts.json"
      - "~/.openclaw/workspace/memory/voice-calls/"
    write:

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code sends call transcripts to an external BlueColumn service after the call, but there is no visible consent check, notice mechanism, redaction step, or policy gate before transmitting potentially sensitive conversation data. In a voice-agent context, transcripts can contain personal, financial, health, or authentication information, so exporting them to a third party creates a significant privacy and compliance risk.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The authentication helper extracts a bearer or x-api-key token, but if the token is invalid it only logs a warning and still returns control to the caller. Because /v1/chat/completions and related routes proceed regardless of auth result, any unauthenticated remote party can invoke the bridge, trigger LLM calls, and access caller-linked memory behavior, making this an authentication bypass rather than a logging issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code writes call transcripts containing caller identifiers and message content to local storage in log_transcript, and elsewhere auto-stores conversation text to BlueColumn memory. While the module docstring mentions storing transcripts, there is no user-facing warning, confirmation, or privacy disclosure to callers about persistence of their conversation data.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
94% confidence
Finding

The bridge sends message content and caller-linked memory context to Anthropic and BlueColumn, which is a network transmission of potentially sensitive user and system data. The file contains implementation comments and startup output, but no user-facing disclosure or confirmation that external AI and memory services receive call content and caller metadata.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The /calls/history endpoint returns recent call records from costs.jsonl with no authentication or authorization checks. Those records include caller identifiers, timestamps, durations, and cost metadata, which exposes sensitive operational and caller data to anyone who can reach the service.

Content

No source excerpt is available for this finding.

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

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 · bridge/server.py (reported line 30)May include surrounding context.

python
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Check for .env
if [ ! -f "$SCRIPT_DIR/.env" ]; then
    echo "⚠️  No .env file found. Copy from .env.example:"
    echo "   cp $SCRIPT_DIR/.env.example $SCRIPT_DIR/.env"
    echo "   Then edit with your API keys."

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 · scripts/start.sh (reported line 9)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Check for .env
if [ ! -f "$SCRIPT_DIR/.env" ]; then
    echo "⚠️  No .env file found. Copy from .env.example:"
    echo "   cp $SCRIPT_DIR/.env.example $SCRIPT_DIR/.env"
    echo "   Then edit with your API keys."

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 · scripts/start.sh (reported line 11)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Check for .env
if [ ! -f "$SCRIPT_DIR/.env" ]; then
    echo "⚠️  No .env file found. Copy from .env.example:"
    echo "   cp $SCRIPT_DIR/.env.example $SCRIPT_DIR/.env"
    echo "   Then edit with your API keys."

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 · scripts/test-recall.sh (reported line 15)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Check for .env
if [ ! -f "$SCRIPT_DIR/.env" ]; then
    echo "⚠️  No .env file found. Copy from .env.example:"
    echo "   cp $SCRIPT_DIR/.env.example $SCRIPT_DIR/.env"
    echo "   Then edit with your API keys."

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 · scripts/test-recall.sh (reported line 16)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Check for .env
if [ ! -f "$SCRIPT_DIR/.env" ]; then
    echo "⚠️  No .env file found. Copy from .env.example:"
    echo "   cp $SCRIPT_DIR/.env.example $SCRIPT_DIR/.env"
    echo "   Then edit with your API keys."

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 · SKILL.md (reported line 142)May include surrounding context.

md
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

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 · scripts/start.sh (reported line 8)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

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 · scripts/start.sh (reported line 10)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

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 · scripts/test-call.sh (reported line 7)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

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 · scripts/test-recall.sh (reported line 13)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

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 · scripts/test-recall.sh (reported line 20)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The script loads and exports values from .env using export $(grep ... | xargs), which is an unsafe parsing pattern for secret-bearing files. Malformed entries, whitespace, shell-special characters, or attacker-controlled .env content can corrupt the environment or trigger unintended command behavior in later steps, and secrets are broadly exposed to subprocesses unnecessarily.

Content

Scanner excerpt · scripts/test-call.sh (reported line 8)May include surrounding context.

sh
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

Credential Access

High
Category
Privilege Escalation
Confidence
88% confidence
Finding

This line completes the unsafe bulk export of all .env values into the process environment, increasing secret exposure to child processes like curl and python3. If the .env file is modified by another party or contains unexpected syntax, the script may behave unpredictably and leak more configuration than intended.

Content

Scanner excerpt · scripts/test-call.sh (reported line 9)May include surrounding context.

sh
# Load .env
if [ -f "$SCRIPT_DIR/.env" ]; then
    export $(grep -v '^#' "$SCRIPT_DIR/.env" | xargs)
fi

MESSAGE="${1:-Hello, this is Joe calling about BlueColumn.}"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · scripts/test-call.sh (reported line 22)May include surrounding context.

sh
echo "🔗 Bridge: $BRIDGE_URL/v1/chat/completions"
echo ""

curl -s -X POST "$BRIDGE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $BRIDGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · scripts/test-recall.sh (reported line 27)May include surrounding context.

sh
echo "🔍 Recalling BlueColumn memory for caller: $PHONE"
echo ""

curl -s -X POST "https://xkjkwqbfvkswwdmbtndo.supabase.co/functions/v1/agent-recall" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BLUECOLUMN_API_KEY" \
  -d "{\"q\": \"What do I know about caller $PHONE?\"}" | python3 -c "

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The architecture explicitly describes automatic transcript storage and caller-ID-based memory recall tied to phone numbers, but it provides no indication of consent, notice, retention limits, or access controls. In a voice-agent context handling real callers, this creates a meaningful privacy and compliance risk because personal data and conversation history may be collected, linked, and reused without user awareness.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The call lifecycle states that post-call transcripts are sent to an external memory service, again without any explicit disclosure to users that their conversations are being stored and shared with a third-party system. This is dangerous because voice conversations often contain sensitive personal or business information, and undisclosed external storage increases privacy, legal, and data-governance exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill prominently advertises that it records, transcribes, stores, and recalls caller-linked conversation history, but it does not provide a clear user-facing warning about the privacy implications of automatic transcript retention and caller-identity correlation. In a live telephony context, this omission is risky because users may unknowingly disclose sensitive personal or business information that is then persisted to external memory services and local logs.

Content

No source excerpt is available for this finding.