Back to skill

Security audit

Total Recall

Security checks for vulnerabilities and agentic risk

Overview

This memory skill is coherent in purpose, but it needs review because it persistently monitors private transcripts, sends them to LLM endpoints, and includes unsafe shell and file operations.

Install only if you are comfortable with a background memory system reading OpenClaw transcripts and sending summaries to your configured LLM provider. Prefer a trusted HTTPS or local endpoint, review watcher and cron persistence before enabling them, and avoid workspaces with sensitive .env contents until the dotenv parsing, Dream Cycle path handling, and rollback behavior are fixed.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/observer-agent.sh:35
Finding
Unsafe Shell Execution Through Workspace .env Loading<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:50-54`; `scripts/observer-agent.sh:35-39`; `scripts/observer-watcher.sh:29-33`; `scripts/reflector-agent.sh:29-33`; `scripts/backfill-importance.sh:15-18`; `scripts/dream-cycle.sh:10-14` **Vulnerability Type**: Command injection through `eval` and unrestricted shell sourcing **Risk Level**: High ### Vulnerable Code From `scripts/observer-agent.sh`: ```bash # Source env if available (grep-guard: only export KEY=VALUE lines) if [ -f "$WORKSPACE/.env" ]; then set -a # Load provider config + backward compatible OPENROUTER key eval "$(grep -E '^(LLM_BASE_URL|LLM_API_KEY|LLM_MODEL|OPENROUTER_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true set +a fi ``` The same unsafe `eval` pattern appears in setup, watcher, reflector, and backfill scripts. From `scripts/dream-cycle.sh`: ```bash # Load environment if present if [ -f "$OPENCLAW_WORKSPACE/.env" ]; then set -a # shellcheck disable=SC1090 source "$OPENCLAW_WORKSPACE/.env" set +a fi ``` ### Technical Analysis The scripts treat `.env` as executable shell code rather than as a data file. Restricting the variable names with `grep` does not neutralize shell syntax in the corresponding values. For example, a matching assignment can contain command substitution: ```bash LLM_MODEL=$(malicious-command) ``` When passed to `eval`, the command substitution is executed. Quoting, semicolons, redirections, and other shell constructs can similarly alter execution. The Dream Cycle implementation is broader because it directly sources the entire `.env` file. Any shell statement in that file executes under the account running the scheduled Dream Cycle. This issue is particularly significant because observer and Dream Cycle operations are intended to run through persistent services or scheduled jobs. A malicious `.env` modification can therefore result in delayed and recurring command execution. ### Attack Path 1. An attacker, compromise ...[truncated 956 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove every use of `eval` and `source` for dotenv configuration. - Parse `.env` as data using a dedicated parser or a strict Bash routine. - Allow only known variable names. - Require values to conform to field-specific validation rules: - URLs must parse as valid permitted HTTP endpoints. - Model names should use a restricted character set. - API keys should be handled as opaque strings. - Reject command substitutions, backticks, shell operators, redirections, multiline values, and unexpected quoting. - Prefer a permission-restricted configuration file, such as mode `0600`, with a non-shell format such as JSON. - Ensure persistent services use explicitly declared environment values or a validated `EnvironmentFile`, rather than evaluating workspace-controlled shell code. ]]>

T02 · Agent Memory Poisoning

Error
Location
scripts/session-recovery.sh:62
Finding
Persistent Agent Memory Poisoning Through Untrusted Transcript Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/observer-agent.sh:146-170, 203-245, 307-324`; `scripts/session-recovery.sh:62-85`; `prompts/observer-system.txt:63-69`; `SKILL.md:74-79` **Vulnerability Type**: Persistent indirect prompt injection and unvalidated memory writes **Risk Level**: High ### Vulnerable Code The observer extracts untrusted conversation text: ```bash tail -150 "$transcript" 2>/dev/null | jq -r --arg cutoff "$CUTOFF_ISO" ' select(.timestamp != null and (.timestamp > $cutoff)) | select(.message.role == "user" or .message.role == "assistant") | .message as $m | (if $m.role == "user" then "USER" else "ASSISTANT" end) as $who | ( if ($m.content | type) == "array" then [$m.content[] | select(.type == "text") | .text] | join(" ") elif ($m.content | type) == "string" then $m.content else "" end ) as $text | select($text != "" and ($text | length) > 5) | "[\($time)] \($who): \($text[0:500])" ' ``` It passes that content to an LLM and appends the returned text to persistent memory: ```bash PAYLOAD=$(jq -n \ --arg system "$SYSTEM_PROMPT" \ --arg messages "Today is $TODAY. Compress these recent messages into observations:\n\n$RECENT_MESSAGES$DEDUP_CONTEXT" \ '{ model: "placeholder", messages: [ {role: "system", content: $system}, {role: "user", content: $messages} ], max_tokens: 2000, temperature: 0.3 }') ``` ```bash if grep -q "^Date: $TODAY" "$OBSERVATIONS_FILE"; then echo "" >> "$OBSERVATIONS_FILE" echo "$OBSERVATION" | sed "/^Date: $TODAY$/d" >> "$OBSERVATIONS_FILE" else echo "" >> "$OBSERVATIONS_FILE" echo "$OBSERVATION" >> "$OBSERVATIONS_FILE" fi ``` If observer recovery fails, raw transcript content is written directly: ```bash if [ -n "$RECENT_MESSAGES" ]; then echo "" >> "$OBSERVATIONS_FILE" echo "<!-- Session Recovery Capture: $(date '+%Y-%m-%d %H:%M') -->" >> "$OBSERVATIONS_FILE" echo "$RECENT_MESSAGES" >> "$OBSERVA ...[truncated 2226 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the raw transcript fallback; failed processing must fail closed rather than copying conversation text into startup-loaded memory. - Treat transcript content as untrusted quoted data in every model prompt. - Explicitly instruct the observer never to preserve executable instructions, system-prompt text, tool commands, credential requests, or policy overrides from transcripts. - Use a strict JSON output schema with allowlisted types and bounded fields. - Parse and validate output before converting it to Markdown. - Reject or quarantine observations containing imperative tool instructions, shell commands, prompt-role markers, or suspicious URLs. - Require human approval before adding durable `rule`, `goal`, `habit`, or high-impact `preference` records. - Load memories as clearly delimited reference data, with a system-level instruction that memory content cannot override current policies or authorize actions. - Preserve provenance so future agents can distinguish direct user statements, model summaries, and recovery artifacts. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/observer-agent.sh:203
Finding
Unrestricted Transmission of Conversation Transcripts and Persistent Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/observer-agent.sh:117-170, 203-246`; `scripts/reflector-agent.sh:87-116`; `scripts/backfill-importance.sh:38-107` **Vulnerability Type**: Sensitive-data exposure to configurable external LLM endpoints **Risk Level**: High ### Vulnerable Code The observer selects up to ten recently modified transcript files and extracts up to 150 recent lines: ```bash done < <(find "$SESSIONS_DIR" -name "*.jsonl" -mmin "-${FIND_MIN}" -type f 2>/dev/null | head -10) ``` ```bash RECENT_MESSAGES=$(cat "$TMPMSGS" | grep -v "^$" | head -150 || true) ``` It sends those excerpts, along with existing memory context, to a configurable endpoint: ```bash RESPONSE=$(curl -s --max-time 60 "$LLM_BASE_URL/chat/completions" \ -H "Authorization: Bearer $LLM_API_KEY" \ -H "Content-Type: application/json" \ -d "$ATTEMPT_PAYLOAD") log "DEBUG: LLM Response (first 500 chars): ${RESPONSE:0:500}" ``` The reflector transmits the complete observations file: ```bash CURRENT_OBS=$(cat "$OBSERVATIONS_FILE") SYSTEM_PROMPT=$(cat "$REFLECTOR_PROMPT") TODAY=$(date '+%Y-%m-%d') PAYLOAD=$(jq -n \ --arg system "$SYSTEM_PROMPT" \ --arg obs "Today is $TODAY. Here is the current observation log to consolidate:\n\n$CURRENT_OBS" \ --arg model "$OBSERVER_MODEL" \ '{ model: $model, messages: [ {role: "system", content: $system}, {role: "user", content: $obs} ], max_tokens: 4000, temperature: 0.2 }') ``` ```bash RESPONSE=$(curl -s --max-time 120 "$LLM_BASE_URL/chat/completions" \ -H "Authorization: Bearer $LLM_API_KEY" \ -H "Content-Type: application/json" \ -d "$PAYLOAD" 2>/dev/null) ``` ### Technical Analysis The core functionality requires some LLM processing, and the documentation discloses that transcripts are sent to an LLM. However, the implementation does not apply secret redaction, data classification, endpoint allowlisting, TLS enforcement, or per-session exclusion controls. `LLM_BASE_ ...[truncated 1762 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require explicit informed opt-in before enabling remote transcript processing. - Default to a local model endpoint where practical. - Require HTTPS for non-loopback destinations. - Maintain an allowlist of approved endpoint hosts and reject embedded credentials, redirects, and unexpected ports. - Add redaction for API keys, passwords, private keys, cookies, authorization headers, identity data, and other configured sensitive patterns. - Allow users to exclude sessions, message types, directories, and sensitive topics. - Minimize request scope instead of sending up to 150 excerpts or the complete memory file. - Do not log provider response bodies in routine operation; use metadata-only debug logs. - Set restrictive permissions on logs, memory, backups, and transcript-derived files. - Document provider retention, data handling, and cross-border processing implications before activation. ]]>

T06 · System Persistence

Warning
Location
scripts/setup.sh:96
Finding
Setup Automatically Installs and Enables a Persistent Transcript Watcher<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:96-119` **Vulnerability Type**: Automatic user-service persistence exceeding minimum functional requirements **Risk Level**: Medium ### Vulnerable Code ```bash # --- Install systemd watcher service (Linux only) --- if has_inotify && has_systemd_user; then echo "" echo "Installing reactive watcher service..." SYSTEMD_DIR="$HOME/.config/systemd/user" mkdir -p "$SYSTEMD_DIR" cat > "$SYSTEMD_DIR/total-recall-watcher.service" << EOF [Unit] Description=Total Recall — Reactive Observer Watcher After=default.target [Service] Type=simple ExecStart=$SKILL_DIR/scripts/observer-watcher.sh Restart=on-failure RestartSec=30 Environment=OPENCLAW_WORKSPACE=$WORKSPACE [Install] WantedBy=default.target EOF systemctl --user daemon-reload systemctl --user enable total-recall-watcher.service systemctl --user start total-recall-watcher.service 2>/dev/null || true echo "✅ Watcher service installed and started" fi ``` ### Technical Analysis Running the general setup script automatically writes a systemd user unit, enables it for future logins, and starts it immediately whenever inotify and a systemd user session are available. The service continuously monitors OpenClaw session transcript modifications and launches observer processing after an activity threshold. It is configured to restart after failure. This persistence survives the setup process and future session resets. Persistent observation is relevant to the declared autonomous-memory function. However, the project also states that the cron observer provides full coverage without the watcher. The reactive watcher is therefore optional redundancy rather than the minimum privilege or persistence required for the feature. Automatically enabling it without a dedicated opt-in exceeds least-privilege expectations. ### Attack Path 1. The user runs `scripts/setup.sh` to create the memory directory structure. 2. The script detects systemd ...[truncated 717 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not install or enable the watcher during ordinary setup. - Require an explicit option such as `setup.sh --install-watcher`. - Display the monitored directory, trigger behavior, network destination, and persistence effects before requesting confirmation. - Default to cron-only or manual operation. - Provide a documented uninstall command that stops, disables, and removes the unit. - Add systemd hardening where compatible, including restrictive filesystem access, `NoNewPrivileges=true`, `PrivateTmp=true`, and an explicit environment file. - Ensure setup is idempotent and does not overwrite a user-modified service without approval. - Clearly distinguish manually suggested cron entries from automatically installed services in all documentation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/dream-cycle.sh:111
Finding
Dream Cycle Helper Allows Path Traversal and Over-Broad File Writes<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dream-cycle.sh:111-146, 151-266, 268-292`; `prompts/dream-cycle-prompt.md:7-12, 345-399` **Vulnerability Type**: Missing path-containment validation in LLM-accessible file operations **Risk Level**: High ### Vulnerable Code The archive command constructs a destination by concatenating an unvalidated argument: ```bash cmd_archive() { local archive_file="${1:-}" local json_arg="${2:-}" [ -n "$archive_file" ] || { err "Usage: dream-cycle.sh archive <archive-file> <json-data?>"; exit 1; } ensure_dirs local archive_path="$OPENCLAW_WORKSPACE/$archive_file" mkdir -p "$(dirname "$archive_path")" local payload payload="$(json_input_or_arg "$json_arg")" printf '%s\n' "$payload" | jq -e . >/dev/null 2>&1 || { err "Archive payload is not valid JSON" exit 1 } local tmp="${archive_path}.tmp" { local today today="$(ISO_DATE_UTC)" echo "# Archived Observations — $today" echo echo "Archived by Dream Cycle nightly run." echo echo "---" echo printf '%s\n' "$payload" | jq -r ' if type == "array" then . else .items // [] end | to_entries[] | .value as $o | "## \($o.id)", "**Original date**: \($o.original_date)", "**Impact**: \($o.impact)", "**Archived reason**: \($o.archived_reason)", "\($o.full_text)", "", "---", "" ' } > "$tmp" [ -s "$tmp" ] || { rm -f "$tmp"; err "Generated archive file is empty"; exit 1; } mv "$tmp" "$archive_path" } ``` The update command similarly accepts an unrestricted source path: ```bash cmd_update_observations() { local new_file="${1:-}" [ -n "$new_file" ] || { err "Usage: dream-cycle.sh update-observations <new-observations-file>"; exit 1; } local source_path="$OPENCLAW_WORKSPACE/$new_file" [ -f "$source_path" ] || { err "New observations file not found: $source_path"; exit 1; } require_file "$OBSERVATIONS_FILE" ...[truncated 3263 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Canonicalize every source and destination using `realpath` or an equivalent safe routine before access. - Enforce exact containment under command-specific allowlisted directories: - Archives only under `memory/archive/observations/`. - Chunks only under `memory/archive/chunks/`. - Logs only under `memory/dream-logs/`. - Metrics only under the designated metrics directory. - Observation updates only from a dedicated temporary directory. - Reject absolute paths, `..` components, unexpected extensions, symbolic-link escapes, and paths resolving outside the allowlisted directory. - Open destination files safely and refuse to follow symlinks. - Change the Dream Cycle default to `READ_ONLY_MODE=true`. - Require explicit user approval before archive creation or replacement of `observations.md`. - Validate replacement observations against a strict schema and size limit. - Avoid automatically committing model-generated or externally sourced content until it has passed validation and review. - Apply the existing staging containment strategy consistently to every file-operation subcommand. ]]>
Vulnerability Patterns
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (83)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill explicitly processes local session transcripts and sends them to an external LLM provider, but that network/data-flow risk is understated by the 'no database, no vectors' framing and absent permission declarations. In a memory skill, transcript access is highly sensitive because it may include credentials, personal data, and proprietary information from prior conversations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill explicitly processes local session transcripts and sends them to an external LLM provider, but that network/data-flow risk is understated by the 'no database, no vectors' framing and absent permission declarations. In a memory skill, transcript access is highly sensitive because it may include credentials, personal data, and proprietary information from prior conversations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill explicitly processes local session transcripts and sends them to an external LLM provider, but that network/data-flow risk is understated by the 'no database, no vectors' framing and absent permission declarations. In a memory skill, transcript access is highly sensitive because it may include credentials, personal data, and proprietary information from prior conversations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill explicitly processes local session transcripts and sends them to an external LLM provider, but that network/data-flow risk is understated by the 'no database, no vectors' framing and absent permission declarations. In a memory skill, transcript access is highly sensitive because it may include credentials, personal data, and proprietary information from prior conversations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill explicitly processes local session transcripts and sends them to an external LLM provider, but that network/data-flow risk is understated by the 'no database, no vectors' framing and absent permission declarations. In a memory skill, transcript access is highly sensitive because it may include credentials, personal data, and proprietary information from prior conversations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill explicitly processes local session transcripts and sends them to an external LLM provider, but that network/data-flow risk is understated by the 'no database, no vectors' framing and absent permission declarations. In a memory skill, transcript access is highly sensitive because it may include credentials, personal data, and proprietary information from prior conversations.

Ae1

High
Category
analysis-evasion
Content
| `scripts/dream-cycle.sh` | Shell helper: preflight, archive, update-observations, write-log, write-metrics, validate, rollback |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
A conversation memory skill normally needs access to conversation data, not periodic bash execution of a workspace script. Scheduling an agent to run shell commands every 15 minutes increases the attack surface substantially: if the script path, workspace contents, or downstream prompt/script logic are modified or influenced, the system can repeatedly execute unintended code without user intervention.

Credential Access

High
Category
Privilege Escalation
Content
BACKUP_FILE="${OBSERVATIONS_FILE}.pre-backfill.bak"

# Source env
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  eval "$(grep -E '^(ANTHROPIC_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
Confidence
98% confidence
Finding
The script uses eval on data extracted from .env, which can execute arbitrary shell code if the .env file is maliciously modified or untrusted. This turns credential loading into a code-execution primitive, and because the script may run in a shared workspace, the risk is more serious than ordinary environment-variable parsing.

Credential Access

High
Category
Privilege Escalation
Content
# Source env
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  eval "$(grep -E '^(ANTHROPIC_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
fi
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
# Source env
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  eval "$(grep -E '^(ANTHROPIC_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
fi
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
# Source env
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  eval "$(grep -E '^(ANTHROPIC_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
fi
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
# Source env
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  eval "$(grep -E '^(ANTHROPIC_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
fi
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Intent-Code Divergence

High
Confidence
96% confidence
Finding
The top-level documentation frames the helper as safe, yet the rollback path forcibly rewinds repository history with 'git reset --hard HEAD~1', which can discard unrelated workspace changes. This is an active contradiction between the file's stated safety intent and actual behavior.

Credential Access

High
Category
Privilege Escalation
Content
SKILL_DIR="$OPENCLAW_WORKSPACE/skills/total-recall"

# Load environment if present
if [ -f "$OPENCLAW_WORKSPACE/.env" ]; then
  set -a
  # shellcheck disable=SC1090
  source "$OPENCLAW_WORKSPACE/.env"
Confidence
98% confidence
Finding
Loading .env at startup gives the script access to arbitrary workspace credentials and tokens that are not obviously required for local memory compaction. Even if this script does not exfiltrate them directly, expanding secret access for an autonomous helper increases blast radius and enables credential exposure through later code, subprocesses, or accidental logging.

Credential Access

High
Category
Privilege Escalation
Content
if [ -f "$OPENCLAW_WORKSPACE/.env" ]; then
  set -a
  # shellcheck disable=SC1090
  source "$OPENCLAW_WORKSPACE/.env"
  set +a
fi
Confidence
99% confidence
Finding
Using source on .env is especially dangerous because it treats the file as executable shell, allowing command execution in addition to secret loading. In a workspace-integrated skill, a poisoned .env can become an execution vector and compromise both local data and any credentials present in the environment.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The rollback command performs git reset --hard HEAD~1 on the entire workspace, which destructively discards tracked changes outside the skill's own files. In this skill context, a memory helper has no clear need for repository-wide history-rewriting capability, so misuse or accidental invocation can cause significant data loss and sabotage unrelated work.

Credential Access

High
Category
Privilege Escalation
Content
LOCK_FILE="/tmp/total-recall-reflector-$(id -u).lock"

# Source env if available (grep-guard: only export KEY=VALUE lines)
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  # Load provider config + backward compatible OPENROUTER key
  eval "$(grep -E '^(LLM_BASE_URL|LLM_API_KEY|LLM_MODEL|OPENROUTER_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
Confidence
91% confidence
Finding
The script uses eval on lines extracted from .env, which creates a code execution primitive if an attacker can modify that file. Even though grep restricts matched keys, shell expansions or command substitutions in the value can still be evaluated, turning configuration loading into arbitrary command execution and secret compromise.

Ssd 3

High
Confidence
98% confidence
Finding
The script intentionally aggregates recent user and assistant messages, including existing observation context, and forwards them to an external LLM for compression. Since there is no content classification, secret stripping, or sensitivity gating, any confidential material present in conversations can be disclosed to a third party and potentially retained or logged upstream.

Credential Access

High
Category
Privilege Escalation
Content
ACCUMULATED_LINES=0

# Safe env loading
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  # Only load OPENROUTER_API_KEY (minimal credential exposure)
  eval "$(grep -E '^OPENROUTER_API_KEY=' "$WORKSPACE/.env" 2>/dev/null)" || true
Confidence
98% confidence
Finding
The script reads $WORKSPACE/.env and uses eval on content derived from it, which turns a configuration file into executable shell code. If an attacker can modify .env, they can achieve arbitrary command execution in the context of the watcher whenever it starts, and the fact that this skill runs automatically/reactively makes that context more dangerous.

Credential Access

High
Category
Privilege Escalation
Content
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  # Only load OPENROUTER_API_KEY (minimal credential exposure)
  eval "$(grep -E '^OPENROUTER_API_KEY=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
fi
Confidence
96% confidence
Finding
Although the comment says only OPENROUTER_API_KEY is loaded, the mechanism still relies on eval of .env-derived text, so the risk is code execution rather than mere credential access. In this skill's context, an always-on observer/watcher increases exposure because a poisoned workspace file can be executed automatically during routine operation, potentially exposing the API key and compromising the user environment.

Credential Access

High
Category
Privilege Escalation
Content
LOCK_FILE="/tmp/total-recall-reflector-$(id -u).lock"

# Safe env loading
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  # Load provider config + backward compatible OPENROUTER key
  eval "$(grep -E '^(LLM_BASE_URL|LLM_API_KEY|LLM_MODEL|OPENROUTER_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
Confidence
95% confidence
Finding
The script uses eval on content extracted from .env, which enables command execution if an attacker can modify that file. A malicious value such as command substitution in an apparently allowed variable assignment would run with the privileges of the script, leading to code execution and likely credential exposure or workspace compromise.

Credential Access

High
Category
Privilege Escalation
Content
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  # Load provider config + backward compatible OPENROUTER key
  eval "$(grep -E '^(LLM_BASE_URL|LLM_API_KEY|LLM_MODEL|OPENROUTER_API_KEY)=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
fi
Confidence
95% confidence
Finding
This finding refers to the same unsafe .env loading block: values from .env are evaluated in the shell, allowing attacker-controlled code execution during environment setup. Because the script handles API credentials and writes to workspace memory files, exploitation could expose secrets, redirect exfiltration to attacker-controlled endpoints, or tamper with stored observations.

Credential Access

High
Category
Privilege Escalation
Content
fi

# --- Check API key ---
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  # Only load OPENROUTER_API_KEY (minimal credential exposure)
  eval "$(grep -E '^OPENROUTER_API_KEY=' "$WORKSPACE/.env" 2>/dev/null)" || true
Confidence
93% confidence
Finding
The setup script reads credentials from $WORKSPACE/.env and later uses eval on content derived from that file. Even though the grep is intended to restrict loading to OPENROUTER_API_KEY, using eval on attacker-controlled or malformed .env content can lead to shell code execution during setup if the workspace or .env file is compromised. In a memory/observer skill that is meant to run automatically, this becomes more dangerous because setup is likely run in a trusted local environment and the same workspace may be shared across agent components.

Credential Access

High
Category
Privilege Escalation
Content
if [ -f "$WORKSPACE/.env" ]; then
  set -a
  # Only load OPENROUTER_API_KEY (minimal credential exposure)
  eval "$(grep -E '^OPENROUTER_API_KEY=' "$WORKSPACE/.env" 2>/dev/null)" || true
  set +a
fi
if [ -z "${OPENROUTER_API_KEY:-}" ]; then
Confidence
94% confidence
Finding
This finding is part of the same unsafe credential-loading block that uses eval on data sourced from .env. If an attacker can modify the workspace .env file, they may inject shell syntax that executes during setup, turning a simple credential read into arbitrary command execution. Because this skill installs persistence mechanisms and is intended for unattended operation, compromise here can have broader downstream impact.

Static analysis

Detected: suspicious.prompt_injection_instructions

Prompt-injection style instruction pattern detected.

Warn
Code
suspicious.prompt_injection_instructions
Location
INSTALL-AGENT.md:31