Back to skill

Security audit

ACC Error Memory

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it claims, but it processes private transcripts, can send excerpts to configured model CLIs, and stores untrusted error text where future agents may load it as guidance.

Install only after reviewing whether transcript analysis and persistent memory fit your privacy needs. Prefer local-only ACC_MODELS, avoid --with-cron until you understand the cadence, review ACC_STATE.md before loading it into sessions, and fix the update-watermark.sh timestamp interpolation issue before using this in a shared or untrusted workspace.

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

T02 · Agent Memory Poisoning

Error
Location
scripts/log-error.sh:29
Finding
Untrusted Transcript Content Can Poison Persistent Agent Memory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/log-error.sh:29-42`, `scripts/log-error.sh:77-87`, `scripts/sync-state.sh:45-71`, `SKILL.md:227-239` **Vulnerability Type**: Persistent storage and reinjection of untrusted behavioral instructions **Risk Level**: High ### Complete Code Snippet ```bash # scripts/log-error.sh:29-42 PATTERN="$PATTERN" CONTEXT="$CONTEXT" MITIGATION="$MITIGATION" python3 << 'PYTHON' import json from datetime import datetime, timezone from pathlib import Path import os workspace = os.environ.get('WORKSPACE', os.path.expanduser('~/.openclaw/workspace')) state_file = Path(workspace) / 'memory' / 'acc-state.json' pattern = os.environ.get('PATTERN', '') context = os.environ.get('CONTEXT', '') mitigation = os.environ.get('MITIGATION', '') ``` ```python # scripts/log-error.sh:77-87 active[pattern] = { 'count': old_data_copy.get('count', 0) + 1, 'severity': 'critical', # Regressions are always critical 'firstSeen': old_data_copy.get('firstSeen', now), 'lastSeen': now, 'context': context or old_data_copy.get('context', ''), 'mitigation': mitigation or old_mitigation, 'regression': True, 'previouslyResolvedOn': old_data_copy.get('resolvedOn'), 'failedLesson': old_lesson if isinstance(old_lesson, dict) else {'mitigation': old_mitigation}, } ``` ```python # scripts/sync-state.sh:45-71 if critical: lines.append("## 🔴 REPEATED ERRORS — Act on these!") lines.append("") lines.append("| Pattern | Count | Last | Mitigation |") lines.append("|---------|-------|------|------------|") for name, data in sorted(critical.items(), key=lambda x: -x[1].get('count', 0)): count = data.get('count', 0) last = data.get('lastSeen', 'unknown')[:10] mitigation = data.get('mitigation', 'be careful') regression = " ⚠️ REGRESSION" if data.get('regression') else "" lines.append(f"| {name}{regression} | {count}x | {last} | {mitigation} |") lines.append("" ...[truncated 3102 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every transcript-derived field as untrusted data rather than executable guidance. 2. Replace free-form mitigations with a fixed, allowlisted schema of narrowly defined actions. 3. Require explicit user or administrator approval before persisting a generated mitigation. 4. Add strict length, character, and content validation to pattern names, contexts, and mitigations. 5. Reject imperative instructions, references to system prompts, tool commands, credential access, safety overrides, and external URLs. 6. Escape Markdown control characters before rendering stored values. 7. Place transcript-derived content inside clearly delimited quoted blocks labeled as untrusted historical data. 8. Add an immutable preamble to generated state stating that its contents cannot override system, developer, user, or safety instructions. 9. Separate observational memory from behavioral policy; do not automatically inject free-form observations into future prompts. 10. Record provenance for every entry, including source session, generating model, approval status, and creation time. 11. Provide review, expiration, and deletion controls for persisted entries. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/haiku-screen.sh:58
Finding
Conversation Excerpts May Be Disclosed to Externally Hosted Model CLIs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/preprocess-errors.sh:14-16`, `scripts/preprocess-errors.sh:73-129`, `scripts/haiku-screen.sh:44-50`, `scripts/haiku-screen.sh:58-79`, `scripts/haiku-screen.sh:84-99`, `scripts/calibrate-patterns.sh:112-146` **Vulnerability Type**: Sensitive transcript processing without redaction or an explicit remote-processing consent gate **Risk Level**: High ### Complete Code Snippet ```bash # scripts/preprocess-errors.sh:14-16 WORKSPACE="${WORKSPACE:-$HOME/.openclaw/workspace}" AGENT_ID="${AGENT_ID:-main}" TRANSCRIPT_DIR="$HOME/.openclaw/agents/$AGENT_ID/sessions" ``` ```python # scripts/preprocess-errors.sh:73-129 # Collect all messages from all sessions all_messages = [] session_files = glob(os.path.join(transcript_dir, '*.jsonl')) for session_file in session_files: session_name = os.path.basename(session_file) line_num = 0 try: with open(session_file, 'r', encoding='utf-8', errors='replace') as f: for line in f: line_num += 1 line = line.strip() if not line: continue try: data = json.loads(line) except json.JSONDecodeError: continue if data.get('type') != 'message': continue msg = data.get('message', {}) role = msg.get('role', '') if role not in ('user', 'assistant'): continue ts_str = data.get('timestamp', '') if not ts_str: continue try: ts = datetime.fromisoformat(ts_str.replace('Z', '+00:00')) except: continue if not full_mode and watermark_ts and ts <= watermark_ts: continue content = msg.get('content', []) text = '' i ...[truncated 4342 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit, informed opt-in before sending transcript content to any remote model. 2. Default to a local, offline classifier rather than a remotely hosted CLI. 3. Display the configured model command and whether it is considered local or remote before processing. 4. Implement secret and PII redaction before constructing prompts, including detection for API keys, tokens, private keys, passwords, email addresses, and regulated identifiers. 5. Allow users to exclude sessions, directories, message types, and sensitive projects. 6. Minimize data by sending only the user phrase necessary for classification rather than both full conversation excerpts. 7. Provide a strict offline mode that rejects commands not present on an administrator-maintained allowlist. 8. Document provider retention and privacy implications prominently during installation. 9. Keep an auditable record of which excerpts were sent, to which provider, and when, without duplicating the sensitive content in logs. 10. Use separate consent and configuration for calibration because it samples exchanges that may not have matched the normal regex filter. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/update-watermark.sh:14
Finding
Arbitrary Python Code Injection Through the Watermark Timestamp Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/update-watermark.sh:14-18`, `scripts/update-watermark.sh:40-49` **Vulnerability Type**: Unquoted heredoc interpolation into executable Python source **Risk Level**: High ### Complete Code Snippet ```bash # scripts/update-watermark.sh:14-18 TIMESTAMP="" while [[ $# -gt 0 ]]; do case $1 in --timestamp) TIMESTAMP="$2"; shift 2 ;; ``` ```bash # scripts/update-watermark.sh:40-49 python3 << PYTHON import json from datetime import datetime watermark = { "session": None, "line": 0, "timestamp": "$TIMESTAMP" } with open("$WATERMARK_FILE", "w") as f: json.dump(watermark, f, indent=2) print(f"✓ Watermark updated: $TIMESTAMP") PYTHON ``` ### Technical Analysis The heredoc delimiter is unquoted, so the shell expands `$TIMESTAMP` and `$WATERMARK_FILE` before Python parses the generated source. `TIMESTAMP` originates directly from the `--timestamp` command-line argument and is inserted between Python quotation marks without escaping. An attacker can supply quotation marks, newlines, and Python statements that terminate the intended string and introduce executable Python. The injected code runs with the same operating-system identity and environment as the caller. Quoting the argument at the shell invocation does not make this safe because the value is later interpolated into source code. A payload can conceptually transform the generated section from: ```python "timestamp": "USER_VALUE" ``` into: ```python "timestamp": "" } # attacker-controlled Python statements ``` followed by comments or syntactically valid trailing code. Exact payload construction depends on preserving valid Python syntax, but no security boundary prevents it. ### Attack Path 1. An attacker gains control over, or can influence, the value supplied to `update-watermark.sh --timestamp`. 2. The attacker includes Python string terminators, newlines, and Python statements in that value. 3. Bash expands the ...[truncated 886 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never interpolate command-line data into executable Python source. 2. Use a quoted heredoc and pass values through environment variables: ```bash export TIMESTAMP WATERMARK_FILE python3 << 'PYTHON' import json import os from datetime import datetime timestamp = os.environ["TIMESTAMP"] watermark_file = os.environ["WATERMARK_FILE"] # Strictly validate the timestamp before use. datetime.fromisoformat(timestamp.replace("Z", "+00:00")) watermark = { "session": None, "line": 0, "timestamp": timestamp, } with open(watermark_file, "w", encoding="utf-8") as f: json.dump(watermark, f, indent=2) print(f"✓ Watermark updated: {timestamp}") PYTHON ``` 3. Alternatively, pass both values through `sys.argv` without constructing source dynamically. 4. Enforce strict ISO-8601 parsing and reject malformed values rather than silently accepting arbitrary text. 5. Resolve and validate the destination path before writing. 6. Add regression tests containing quotes, backslashes, command substitutions, newlines, and Python syntax. 7. Run scheduled processing under a minimally privileged account with a restricted environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The description presents this skill as an active error-pattern tracking and mitigation system. The supplied code chunk is instead a simple shell script that checks for a specific JSON file, extracts fields with jq, and writes a Markdown report summarizing existing state. This is materially narrower and different from the declared purpose: it visualizes/syncs already-collected data rather than performing the core tracking, detection, escalation, or learning behaviors described. While the generated Markdown references those concepts textually, the code itself does not implement them.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is about error pattern tracking and mitigation learning, but the supplied code does not analyze errors, detect corrections, escalate recurring mistakes, or learn mitigations. Its sole function is maintaining a processing watermark file, optionally sourcing the timestamp from pending-errors.json or the current time. This is a materially different primary purpose and accesses specific workspace memory files not implied by the declared description.

Missing User Warnings

High
Confidence
99% confidence
Finding
The documentation describes sending prompts to CLI-accessible models via `ACC_MODELS` but does not clearly warn that transcript content may leave the local environment and be processed by external providers. In the context of conversation analysis, this is a serious disclosure issue because sensitive user/assistant exchanges could be transmitted without explicit informed consent.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The README explicitly describes extracting user/assistant exchanges from transcripts and logging error patterns with context, but provides no warning, consent guidance, retention limits, or privacy safeguards. In an agent skill, transcript content can include sensitive personal, business, or credential-related data, so normalizing silent collection/logging increases the risk of privacy violations and downstream data exposure.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill clearly describes shell execution, environment-variable use, and read/write access to workspace files, but it does not declare any tool scope or permission boundaries. That makes the effective privilege surface implicit and harder to review, increasing the chance that an agent executes it with broader capabilities than users expect.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill persists and analyzes conversation-derived error data across sessions, but the description does not prominently warn users that their interactions are being retained. This creates an informed-consent and privacy risk, especially where transcripts may include secrets, personal data, or sensitive business context.

Ssd 3

Medium
Confidence
93% confidence
Finding
The skill is designed to persistently extract and log user-assistant exchanges across sessions, creating a durable store of natural-language interaction data. Even if intended for quality improvement, that retained corpus can expose secrets, personal data, or sensitive context to later processes, other skills, or anyone with filesystem access.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
`ACC_MODELS` allows arbitrary CLI commands to be supplied and invoked with transcript-derived prompts appended, which creates both command-surface and data-exfiltration risk. Because the command is model-agnostic and not constrained to vetted binaries or local-only inference, sensitive conversation content could be sent to unintended external services or abused by unsafe command wrappers.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The install instructions mention cron setup, but the skill description does not clearly warn that conversation-processing jobs may run automatically after installation. Automatic background analysis increases privacy and operational risk because users may not realize when or how often transcript data is processed.

Ssd 3

Medium
Confidence
91% confidence
Finding
The preprocessing pipeline explicitly extracts and stores user/assistant exchanges from transcripts in a JSON file, which materially increases the amount of readable conversational data at rest. In this skill’s context, that broad collection is more dangerous because it centralizes content for later analysis and possible onward transmission to model CLIs.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The script forwards assistant/user conversation content to external LLM CLI tools during calibration, which expands a local pattern-learning feature into data egress to third-party tooling. Even if intended for better classification, this can expose potentially sensitive conversation content and changes the trust boundary in a way users may not expect.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The script builds subprocess commands from the configurable ACC_MODELS environment variable and executes them on sampled conversation data. Although subprocess.run is used without shell=True, this still allows arbitrary executable selection and argument control by whoever can influence the environment, enabling execution of untrusted programs and exfiltration of data.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The classification step sends sampled conversation content to external LLM commands without any explicit consent, warning, or confirmation at the point of use. In a memory/error-tracking skill, those exchanges may include sensitive user prompts, making silent transmission a privacy and compliance risk.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script builds the external classifier command from the ACC_MODELS environment variable and executes it for each exchange via subprocess.run. Although shell expansion is not used, this still permits execution of arbitrary binaries and arbitrary outbound integrations chosen by whoever controls the environment, which exceeds the narrow needs of error screening and can be abused for unintended code execution, data exfiltration, or policy bypass.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script sends assistant_text and user_text from conversation exchanges directly to external model CLIs without any consent gate, minimization beyond truncation, or assurance that the backend is local. In this skill context, the data being screened is likely to contain user prompts, assistant responses, and potentially sensitive operational context, so forwarding it to arbitrary configured model backends creates a real privacy and data-handling risk.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The script extracts user and assistant message text from session transcripts and writes those exchanges to `pending-errors.json`. Although the file header describes the output artifact, there is no explicit warning that potentially sensitive transcript content will be copied into a workspace file, which is a privacy-relevant file-write operation.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The script explicitly states it syncs state into a markdown file 'for prompt injection', meaning it is preparing agent-consumable content derived from mutable local state and placing it into a form likely to influence future model behavior. In the context of an agent skill, this creates a cross-context prompt-injection channel: if the JSON contents are attacker-controlled or polluted by untrusted inputs, they can steer or manipulate downstream agent decisions under the guise of memory or safety guidance.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The inline comment is a strong indicator that the file's purpose is to create prompt material for agent consumption, not merely track errors. Because the generated markdown includes context text from the JSON state, this increases the risk that untrusted content will be laundered into trusted prompt space, enabling instruction smuggling, behavioral manipulation, or persistence of malicious guidance across runs.

Context-Inappropriate Capability

Low
Confidence
71% confidence
Finding
The installation flow sets up scheduled execution three times daily, and later sections document adding a cron job that runs the analysis pipeline automatically. Persistent autonomous scheduling is a distinct operational capability beyond the core semantic purpose described in the manifest, which focuses on detecting, logging, and learning from mistakes.

Missing User Warnings

Low
Confidence
81% confidence
Finding
This code overwrites learned-patterns.json, calibration-state.json, and calibration-errors.json, which affects persisted workspace state. Although the file header mentions one in-place update, there is no clear user-facing warning at the write sites or confirmation around these persistent modifications.

Missing User Warnings

Low
Confidence
95% confidence
Finding
This shell script updates and rewrites the JSON state file, which is a file-modifying operation covered by the missing-warning rule for code files. While the header shows usage, there is no explicit warning, confirmation, or comment disclosing that the command will persistently modify the workspace memory file.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This script writes generated content to ACC_STATE.md using shell redirection, which overwrites or recreates the file. Although the script logs success afterward, it does not disclose beforehand that it will modify a file under the user's workspace, and the file itself contains no comment or prompt warning about that side effect.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This shell script performs a direct write to a persistent workspace file, replacing the watermark contents. Although the script prints a success message afterward, it does not disclose beforehand that it will overwrite state in $WATERMARK_FILE, and the surrounding comments do not explicitly warn about this side effect.

Static analysis

No suspicious patterns detected.