T09 · Insecure Skill Coding Practices
- Location
memory-index.py:40- Finding
Overbroad Workspace Indexing Exposes Sensitive and Untrusted Markdown Content
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This local memory skill is not clearly malicious, but it can persist and index more local content than users may expect, including files marked “off the record” after a later rebuild.
Review before installing. Use this only if you want long-term local memory and are comfortable with workspace Markdown being indexed and summarized into local files. Do not rely on “off the record” for privacy in this version; avoid saving secrets or sensitive conversations unless the indexer is restricted to an approved directory and generated indexes can be deleted or rebuilt safely.
memory-index.py:40Overbroad Workspace Indexing Exposes Sensitive and Untrusted Markdown Content
memory-index.py:40Off-the-Record Conversations Are Indexed During Subsequent Rebuilds
The documented purpose is narrowly framed as saving conversations, but the described behavior expands into indexing general workspace markdown, search, tag extraction, JSON/topic-map generation, and grep fallback. This mismatch is dangerous because users may authorize a memory-save feature without realizing it also scans and indexes unrelated workspace content, potentially broadening data exposure and persistence.
The 'off the record' guidance is internally contradictory: it still writes the conversation to disk while merely skipping logging and indexing. This is dangerous because users asking not to record content would still have persistent copies created, defeating privacy expectations and potentially exposing sensitive material through direct filesystem access or backups.
Data flows directly from a source (env vars, files, network) to a sink (network output, exec, file write) without intermediate validation.
# Fallback: try grep
print("No FTS5 matches. Trying grep fallback...")
import subprocess
result = subprocess.run(
["grep", "-rl", "-m5", query, WORKSPACE],
capture_output=True, text=True,
env={"HOME": os.environ.get("HOME", os.path.expanduser("~"))}
The skill requires shell execution plus file read/write and environment access, but it does not declare any explicit tool scope or permission boundaries. That makes the effective capability surface broader and less auditable, increasing the chance the agent can execute persistence and indexing actions without clear user or platform controls.
The setup and init actions create persistent storage and initialize a conversation log automatically during installation. While not inherently malicious, automatic session-persistence setup increases the chance that later interactions are retained without users appreciating that the environment has been prepared for long-term storage.
install:
- id: setup
kind: setup
label: "Create saved conversations directory"
run: "mkdir -p ~/.openclaw/workspace/saved"
- id: init-log
kind: init
The skill promotes saving and indexing conversations but does not prominently warn that potentially sensitive session content will be written to long-term local storage and made searchable. Users may reasonably assume ephemeral handling, so the missing disclosure undermines informed consent and increases privacy and data-retention risk.
Broad trigger phrases like 'save this' or 'save this conversation' overlap with normal conversational language and can cause unintended invocation. In a persistence skill, accidental activation is risky because it may store sensitive session content to disk and trigger indexing without sufficiently clear consent.
The skill explicitly instructs the agent to persist conversation-derived summaries and user-provided material to durable local storage. Even if intended as a memory feature, this creates a real confidentiality risk because sensitive data from the session can be retained beyond the user's expectation and later discovered through search or file access.
Saying the agent handles '/save' automatically without clearly bounded invocation rules creates ambiguity about when the skill may execute. That ambiguity is especially risky for a state-changing skill because automatic persistence and index rebuilds can occur from casual language or inferred intent rather than explicit user authorization.
The first-time setup instructions normalize creation of a persistent saved-conversation store and log file, reinforcing long-term retention as a default behavior. In a skill designed to handle session-derived content, that persistence context makes accidental or overbroad storage more likely and increases privacy impact.
# - Python 3.8+ (stdlib only — no pip packages needed)
# - An OpenClaw agent with write/edit/exec/read tools
# 1. Create the saved conversations directory (inside the workspace)
mkdir -p ~/.openclaw/workspace/saved
# 2. Initialize the conversation log
The module docstring describes a memory search/index tool, contradicting the manifest's save-focused purpose. This mismatch is security-relevant because reviewers and users may grant permissions based on the manifest while the code signals a broader retrieval and indexing role with greater access to local data.
The skill is presented as a save-to-memory utility, but the implementation walks the broader workspace and indexes arbitrary Markdown and text files. That capability expansion increases exposure because users and reviewers may authorize a seemingly narrow persistence skill while it actually performs generalized data collection over local content.
The code writes a topic-index JSON derived from workspace and saved-conversation content without explicit disclosure or consent. That creates a persistent, easily searchable summary of user-derived data, which can increase privacy exposure even if the original files were less discoverable.
TOPIC_MAP_PATH is derived from an environment-controlled directory and is opened for write without validation. If an attacker can influence the environment for this skill, they can redirect writes to arbitrary filesystem locations writable by the process, causing unauthorized file creation or overwrite and potential leakage of indexed metadata.
})
os.makedirs(os.path.dirname(TOPIC_MAP_PATH), exist_ok=True)
with open(TOPIC_MAP_PATH, 'w') as f:
json.dump(topic_map, f, indent=1)
map_size = os.path.getsize(TOPIC_MAP_PATH)
The stub index persists snippets and headings from indexed files to disk without warning, potentially exposing conversation fragments and other sensitive text in a consolidated file. In a memory-saving skill, undisclosed persistence of summarized content is especially risky because it changes the visibility and accessibility of user data.
STUB_PATH is built from an environment-derived base path and then written without any path validation. An attacker controlling that environment variable can redirect the stub output to unintended locations, potentially overwriting files or planting content that exposes indexed conversation snippets.
cleaned.append(line)
os.makedirs(os.path.dirname(STUB_PATH), exist_ok=True)
with open(STUB_PATH, 'w') as f:
f.write('\n'.join(cleaned))
stub_size = len('\n'.join(cleaned).encode('utf-8'))
Spawning external grep adds an undeclared filesystem search capability not justified by a save-oriented skill. In context, this is more dangerous because it bypasses the indexed subset and performs direct recursive content inspection of the workspace, which can reveal data the user did not expect this skill to scan.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# Fallback: try grep
print("No FTS5 matches. Trying grep fallback...")
import subprocess
result = subprocess.run(
["grep", "-rl", "-m5", query, WORKSPACE],
capture_output=True, text=True,
env={"HOME": os.environ.get("HOME", os.path.expanduser("~"))}
WORKSPACE comes from the environment and is passed to grep as the search root without validation. In this skill's context, that broadens the tool from searching expected memory data to recursively reading arbitrary accessible directories, which can expose sensitive files or create an unintended capability for filesystem reconnaissance.
# Fallback: try grep
print("No FTS5 matches. Trying grep fallback...")
import subprocess
result = subprocess.run(
["grep", "-rl", "-m5", query, WORKSPACE],
capture_output=True, text=True,
env={"HOME": os.environ.get("HOME", os.path.expanduser("~"))}
Topic-map and tag-generation features extend the skill beyond simple conversation saving and create additional derived-data artifacts on disk. While not inherently malicious, undisclosed metadata extraction increases privacy risk and can make sensitive themes easier to enumerate than the raw files themselves.
No suspicious patterns detected.