Back to skill

Security audit

Sophie Optimizer

Security checks for vulnerabilities and agentic risk

Overview

This skill is clearly intended to manage OpenClaw context, but it can delete main session files, restart the gateway, and write persistent memory without enough safeguards or scoping.

Review carefully before installing. This skill should only be used by an operator who intentionally wants automated OpenClaw session resets and memory rewriting, and it should be changed to require explicit confirmation, enforce the documented token threshold, restrict deletion scope, sanitize summaries before writing MEMORY.md, and avoid unattended cron use until those controls exist.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T02 · Agent Memory Poisoning

Error
Location
optimizer.py:89
Finding
Persistent Agent Memory Poisoning Through Untrusted Summary Content<![CDATA[ ## Vulnerability Details **File Location**: `optimizer.py:89-94`, with attacker-controlled input introduced at `optimizer.py:143-153` **Vulnerability Type**: Persistent agent memory poisoning **Risk Level**: High ### Complete Code Snippet ```python for arc in recent_archives: ts = arc.get('timestamp', 'Unknown') tok = arc.get('token_count', 0) summ = arc.get('summary', '').strip() new_section += f"### Snapshot: {ts}\n" new_section += f"- **Tokens:** {tok}\n" new_section += f"- **Summary:** {summ}\n\n---\n\n" ``` The summary originates from a command-line argument and is persisted without validation: ```python parser.add_argument("--summary", required=True, help="Context summary text") parser.add_argument("--tokens", type=int, default=0, help="Current token count") parser.add_argument("--session-id", default="main", help="Session ID") parser.add_argument("--reset", action="store_true", help="Trigger reset.sh after archiving") args = parser.parse_args() ensure_dirs() # Notify Start try: subprocess.run(["openclaw", "message", "send", "--message", f"🔄 Iniciando otimização de contexto ({args.tokens} tokens)..."], check=False) except Exception: pass print(f"Creating archive for session {args.session_id} ({args.tokens} tokens)...") filepath, timestamp = create_archive(args.summary, args.tokens, args.session_id) ``` The resulting content is written to long-term memory: ```python with open(MEMORY_FILE, 'w', encoding='utf-8') as f: f.write(final_content) ``` ### Technical Analysis The required `--summary` argument is treated as trusted content. It is stored in a JSON archive and subsequently interpolated verbatim into `~/openclaw/MEMORY.md`. Because no Markdown escaping, structural validation, trust labeling, or instruction filtering is applied, a caller can include additional headings, directives, or prompt-like instructions in the summary. Those instructions can escape the intended `- **Summary:**` field and be ...[truncated 1810 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all summary text as untrusted data rather than agent instructions. 2. Escape Markdown control characters and prevent injected headings, separators, links, and code blocks from altering the memory structure. 3. Store summaries in a structured, non-executable format and render them only as quoted or fenced data. 4. Add a strict schema and maximum length for archive fields. 5. Separate trusted long-term rules from untrusted conversation summaries. 6. Require explicit user approval before promoting generated summaries into long-term memory. 7. Apply prompt-injection detection and reject summaries containing attempts to define agent rules, tool instructions, or authority claims. 8. Mark imported content with clear trust metadata that downstream agents must not interpret as instructions. 9. Sanitize existing archives before rebuilding `MEMORY.md`, because fixing only new input would leave previously stored payloads active. 10. Use atomic writes and maintain a validated backup of the last trusted memory state. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
optimizer.py:171
Finding
Unprotected Destructive Session Reset Without Enforced Token Threshold<![CDATA[ ## Vulnerability Details **File Location**: `optimizer.py:171-189` and `reset.sh:14-26` **Vulnerability Type**: Unsafe destructive operation and denial of service **Risk Level**: High ### Complete Code Snippet The reset can be requested through a simple command-line flag and is launched asynchronously: ```python if args.reset: print("Triggering reset script...") # Notify Reset try: subprocess.run(["openclaw", "message", "send", "--message", "⚠️ Reiniciando sistema para limpeza de sessão. Volto já! 👑"], check=False) except Exception: pass # Use nohup to allow the script to survive if the parent dies, # though systemctl restart might handle it. subprocess.Popen(["/bin/bash", RESET_SCRIPT], start_new_session=True) ``` The launched script deletes all matching main-session files and restarts the service: ```bash # 2. Clean Session Files if [ -d "$SESSION_DIR" ]; then echo "[Sophie Optimizer] Cleaning session files in $SESSION_DIR..." # Remove .jsonl and .json files, keeping the directory structure rm -f "$SESSION_DIR"/*.jsonl rm -f "$SESSION_DIR"/*.json echo "[Sophie Optimizer] Session storage purged." else echo "[Sophie Optimizer] Warning: Session directory not found at $SESSION_DIR" fi # 3. Restart Gateway echo "[Sophie Optimizer] Restarting OpenClaw Gateway..." systemctl --user restart $SERVICE_NAME ``` Although `SKILL.md` states that the skill should exit when the token count is below 80,000, the implementation only defines the token argument and never enforces that threshold: ```python parser.add_argument("--tokens", type=int, default=0, help="Current token count") parser.add_argument("--session-id", default="main", help="Session ID") parser.add_argument("--reset", action="store_true", help="Trigger reset.sh after archiving") ``` ### Technical Analysis The reset path is destructive and lacks authorization, threshold enforcement, confirmation, backup verification, locking, a ...[truncated 2122 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the documented token threshold in code before permitting any reset. 2. Reject negative, zero, malformed, or implausible token counts where reset is requested. 3. Require explicit interactive confirmation or a separate authorized reset capability. 4. Verify that archive creation, integrity checks, and memory updates all completed successfully before deleting anything. 5. Restrict deletion to the validated session selected by `--session-id` instead of purging all main-session files. 6. Validate session identifiers against an allowlist and never derive deletion paths directly from untrusted values. 7. Use a process lock and stop or quiesce the gateway before modifying session files to prevent write races. 8. Move files into a private quarantine directory before permanent deletion, allowing rollback if restart fails. 9. Avoid detached execution for destructive actions; wait for completion and report failures. 10. Check subprocess return codes and abort on any failed prerequisite. 11. Add rate limiting and audit logging for reset requests. 12. Restrict executable and directory permissions so only the intended owner can invoke or alter the reset logic. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
optimizer.py:19
Finding
Sensitive Context Archives Created With Implicit Filesystem Permissions<![CDATA[ ## Vulnerability Details **File Location**: `optimizer.py:19-34` **Vulnerability Type**: Insecure storage of potentially sensitive context data **Risk Level**: Medium ### Complete Code Snippet ```python def ensure_dirs(): if not os.path.exists(ARCHIVES_DIR): os.makedirs(ARCHIVES_DIR) def create_archive(summary, tokens, session_id): timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") filename = f"{timestamp}.json" filepath = os.path.join(ARCHIVES_DIR, filename) data = { "timestamp": timestamp, "session_id": session_id, "token_count": tokens, "summary": summary } with open(filepath, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) ``` ### Technical Analysis Archive directories and files are created without explicit restrictive modes. Their effective permissions therefore depend on the process umask and existing directory permissions. On systems with a permissive umask, the archive directory may be accessible to other local users and JSON archives may be created as group-readable or world-readable. The files contain full context summaries and session identifiers, which may include credentials, private conversation content, internal paths, operational details, or other sensitive data. The implementation also does not verify that the archive directory or destination is not a symbolic link. If the skill directory is writable by another principal, filesystem redirection could further undermine archive confidentiality or integrity. ### Attack Path 1. The optimizer runs under an account or environment with a permissive umask, or the existing `archives` directory has broad permissions. 2. A context summary containing sensitive information is passed to the optimizer. 3. `ensure_dirs()` and `open()` create the directory and JSON archive using default permissions. 4. Another local user or process with access to those paths reads the JSON archive. ...[truncated 848 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `ARCHIVES_DIR` with mode `0700`. 2. Create archive files atomically with mode `0600`, for example by using `os.open()` with `O_CREAT`, `O_EXCL`, `O_NOFOLLOW`, and an explicit mode. 3. Apply `os.chmod()` to existing directories and archive files after verifying ownership. 4. Reject symbolic links and verify that the resolved archive path remains under the expected base directory. 5. Fail closed if the archive directory is owned by another account or is writable by group or other users. 6. Set a restrictive umask for the optimizer process. 7. Minimize stored data and redact credentials, tokens, and other secrets before archival. 8. Define retention limits and securely remove expired archives. 9. Consider encrypting archives at rest if summaries are expected to contain sensitive information. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented purpose frames the skill as benign context optimization, but the described behavior includes destructive reset actions and a gateway service restart that are not clearly disclosed as primary risks. This mismatch can mislead operators into authorizing the skill under false assumptions, increasing the chance of unintended data loss or disruption.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if [ -d "$SESSION_DIR" ]; then
    echo "[Sophie Optimizer] Cleaning session files in $SESSION_DIR..."
    # Remove .jsonl and .json files, keeping the directory structure
    rm -f "$SESSION_DIR"/*.jsonl
    rm -f "$SESSION_DIR"/*.json
    echo "[Sophie Optimizer] Session storage purged."
else
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
echo "[Sophie Optimizer] Cleaning session files in $SESSION_DIR..."
    # Remove .jsonl and .json files, keeping the directory structure
    rm -f "$SESSION_DIR"/*.jsonl
    rm -f "$SESSION_DIR"/*.json
    echo "[Sophie Optimizer] Session storage purged."
else
    echo "[Sophie Optimizer] Warning: Session directory not found at $SESSION_DIR"
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).

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises and documents file read, file write, and shell-like capabilities but does not declare any explicit tool scope or permissions boundary. That makes the skill harder to review, easier to overprivilege in practice, and increases the risk that destructive operations such as file deletion or service restart are invoked without appropriate constraint.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill openly describes wiping session storage and performing a hard reset but provides no warning, safeguard, or confirmation guidance. In context, this is dangerous because the skill targets the main session and long-term memory workflow, so misuse or accidental invocation can destroy active state and interfere with recovery.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill recommends manual or cron-based automated execution for a destructive reset workflow without cautioning against unattended operation. Automating session wipes and service restarts can repeatedly erase state, interrupt availability, and compound operational damage before anyone notices.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Notify Start
    try:
        subprocess.run(["openclaw", "message", "send", "--message", f"🔄 Iniciando otimização de contexto ({args.tokens} tokens)..."], check=False)
    except Exception:
        pass
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Notify Progress
    try:
        subprocess.run(["openclaw", "message", "send", "--message", f"✅ Contexto arquivado em {os.path.basename(filepath)}. Memória atualizada."], check=False)
    except Exception:
        pass
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
Executing an external reset shell script exceeds simple archiving and memory-update behavior and creates a privileged execution boundary that is not visible in this file. Because the script's content is not constrained here, this expands the attack surface significantly and could enable destructive or persistent host changes under the guise of routine optimization.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
A session reset is a disruptive and potentially destructive operation, yet the code performs it immediately when --reset is supplied, without an interactive confirmation, authorization check, or external safety gate. In this skill context, automatic resets are more dangerous because they are framed as routine maintenance and may be triggered in normal workflows, increasing the chance of accidental service interruption or data loss.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("Triggering reset script...")
        # Notify Reset
        try:
            subprocess.run(["openclaw", "message", "send", "--message", "⚠️ Reiniciando sistema para limpeza de sessão. Volto já! 👑"], check=False)
        except Exception:
            pass
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Session Persistence

Medium
Category
Rogue Agent
Content
except Exception:
            pass
            
        # Use nohup to allow the script to survive if the parent dies, 
        # though systemctl restart might handle it.
        subprocess.Popen(["/bin/bash", RESET_SCRIPT], start_new_session=True)
Confidence
90% confidence
Finding
Starting the reset process in a new session allows it to outlive the parent process and reduces operator visibility and control over what continues running. In combination with an external shell script, this persistence makes unintended or malicious changes harder to interrupt, audit, or roll back.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Use nohup to allow the script to survive if the parent dies, 
        # though systemctl restart might handle it.
        subprocess.Popen(["/bin/bash", RESET_SCRIPT], start_new_session=True)

if __name__ == "__main__":
    main()
Confidence
95% confidence
Finding
This call launches an external shell script via bash in a detached session, which can execute arbitrary commands contained in reset.sh with the privileges of the current process. In a skill whose stated purpose is context maintenance, handing control to an opaque shell script materially increases risk because it can reset services, alter files, or persist beyond the invoking session without further checks.

Description-Behavior Mismatch

Low
Confidence
76% confidence
Finding
The stated purpose focuses on automated context health management: monitoring tokens, snapshotting memory, and resetting sessions. In addition to those tasks, the code emits multiple status messages through the OpenClaw CLI, which is a behavioral surface not mentioned in the manifest and not strictly necessary to implement archiving or reset logic.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The skill sends user-visible status messages in Portuguese such as 'Iniciando otimização de contexto' and 'Reiniciando sistema', but there is no indication that the user opted into this locale. This is a natural-language policy issue because it imposes a specific language in user-facing interactions without documented choice or justification.

Static analysis

No suspicious patterns detected.