Back to skill

Security audit

data-pods

Security checks for vulnerabilities and agentic risk

Overview

This skill is intended to manage local data pods, but it needs review because its advertised consent protections are not enforced and it can persist, import, export, and package sensitive data with weak safeguards.

Install only if you are comfortable reviewing each operation before it runs. Avoid using it for health, personal, client, or confidential research data until pod-name validation, safe archive import, read-only query controls, enforced consent checks, and clear export/LLM-sharing warnings are added. Treat exported .vpod and LLM markdown files as containing the full sensitive contents of the selected pod.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/pod.py:118
Finding
Consent Layer Is Not Enforced by Pod Operations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pod.py:118-159`; related consent checks are defined in `consent.py:87-105` and `scripts/consent.py:132-150` **Vulnerability Type**: Missing authorization enforcement **Risk Level**: High ### Vulnerable Code ```python def query_pod(pod_name: str, text: str = None, sql: str = None): """Query a pod.""" pod_path = PODS_DIR / pod_name if not pod_path.exists(): print(f"Error: Pod '{pod_name}' not found") return False db_path = pod_path / "data.sqlite" conn = sqlite3.connect(db_path) c = conn.cursor() if sql: try: c.execute(sql) rows = c.fetchall() for row in rows: print(row) except Exception as e: print(f"SQL Error: {e}") elif text: # Simple text search c.execute("SELECT id, title, content, tags FROM notes WHERE content LIKE ? OR title LIKE ?", (f"%{text}%", f"%{text}%")) rows = c.fetchall() if rows: print(f"📄 Found {len(rows)} results for '{text}':") for row in rows: print(f" [{row[0]}] {row[1]}: {row[2][:80]}...") else: print(f"No results for '{text}'") else: c.execute("SELECT id, title, tags, created_at FROM notes") rows = c.fetchall() if rows: print(f"📄 Notes in '{pod_name}':") for row in rows: print(f" [{row[0]}] {row[1]} | {row[2]} | {row[3]}") else: print("No notes yet.") conn.close() return True ``` A consent-checking function exists separately: ```python def check(pod: str, agent: str) -> bool: grants = load_grants() key = f"{pod}:{agent}" if key not in grants: return False grant = grants[key] if not grant.get("active"): return False # Check expiration if grant.get("expires"): e ...[truncated 2264 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a single access gateway used by every operation that reads or modifies pod data. 2. Require an authenticated agent identifier and session identifier for query, add, ingest, search, list, export, import, and pack operations. 3. Before resolving or opening a pod, verify: - The session or grant exists. - The requesting agent matches the grant. - The requested pod is explicitly allowed. - The grant remains active and has not expired. 4. Deny access by default when identity or consent information is absent. 5. Consolidate the two consent implementations into one authoritative store and remove the unused implementation. 6. Route all accesses through mandatory audit logging, including denied attempts, operation type, pod, agent, session, timestamp, and result count. 7. Add integration tests proving that absent, revoked, expired, or wrong-pod grants are denied for every command. 8. Treat direct database helpers as internal functions and prevent CLI entry points from bypassing the gateway. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/pod.py:27
Finding
Unvalidated Pod Names Allow Filesystem Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pod.py:27-36`; the same construction pattern also appears at `scripts/pod.py:96`, `scripts/pod.py:120`, `scripts/pod.py:162`, and `scripts/ingest.py:39` **Vulnerability Type**: Path traversal and storage-root escape **Risk Level**: High ### Vulnerable Code ```python def create_pod(name: str, pod_type: str = "shared"): """Create a new data pod.""" ensure_dir() pod_path = PODS_DIR / name if pod_path.exists(): print(f"Error: Pod '{name}' already exists") return False pod_path.mkdir(parents=True, exist_ok=True) # Create SQLite database db_path = pod_path / "data.sqlite" conn = sqlite3.connect(db_path) ``` Other operations use the same unvalidated input pattern: ```python pod_path = PODS_DIR / pod_name ``` ### Technical Analysis Pod names are supplied through command-line arguments and joined directly to: ```text ~/.openclaw/data-pods/ ``` The code does not reject absolute paths, `..` components, path separators, or symlinks. With `pathlib`, joining an absolute user-supplied path can discard the intended base directory. Relative traversal components can also resolve outside the pod root. Existence checks do not establish containment. Consequently, creation and access operations can target arbitrary filesystem locations that are writable or readable by the current user. The issue affects pod creation, note insertion, query, export, ingestion, and synchronization operations. ### Attack Path Example storage-root escape: 1. Invoke pod creation with traversal components: ```bash python3 scripts/pod.py create ../../../../tmp/attacker-pod ``` 2. The application computes: ```text ~/.openclaw/data-pods/../../../../tmp/attacker-pod ``` 3. The path resolves outside `~/.openclaw/data-pods`. 4. The application creates the selected directory and writes `data.sqlite`, `metadata.json`, and `manifest.yaml` there. Example unauthorize ...[truncated 1074 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict pod names to a conservative identifier format, for example: ```python if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}", name): raise ValueError("Invalid pod name") ``` 2. Explicitly reject absolute paths, path separators, empty names, `.` and `..`. 3. Resolve the candidate path and verify containment before any filesystem operation: ```python root = PODS_DIR.resolve() candidate = (root / name).resolve() if candidate.parent != root: raise ValueError("Pod path escapes storage root") ``` 4. Reject symlinked pod directories and verify each relevant path component using `lstat()` or a platform-appropriate safe-open strategy. 5. Apply the same centralized validation helper to create, add, query, export, ingest, import, search, and pack operations. 6. Where supported, open files relative to a trusted directory descriptor and use no-follow semantics to reduce time-of-check/time-of-use and symlink risks. 7. Add regression tests for absolute paths, nested paths, encoded separators, `..`, and symlink escapes. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/podsync.py:76
Finding
Archive Import Destination Can Escape the Pod Storage Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/podsync.py:76-84` **Vulnerability Type**: Unvalidated archive extraction destination and resource exhaustion **Risk Level**: High ### Vulnerable Code ```python if pod_name is None: pod_name = input_path.stem pod_path = PODS_DIR / pod_name if pod_path.exists(): print(f"Warning: Pod '{pod_name}' already exists. Use --force to overwrite.") return False # Extract zip with zipfile.ZipFile(input_path, 'r') as zipf: zipf.extractall(pod_path) ``` ### Technical Analysis The `--name` argument or archive filename stem controls `pod_name`. This value is directly joined to `PODS_DIR` without canonicalization or containment validation. An absolute name or traversal sequence can therefore select an extraction directory outside `~/.openclaw/data-pods`. The code also extracts every entry without enforcing limits on: - Archive entry count. - Individual uncompressed file size. - Total expanded size. - Compression ratio. - Path depth. - Special files or symbolic-link-like entries. Even when the Python runtime normalizes unsafe member names, the attacker-controlled extraction destination remains independently exploitable. Unrestricted expansion also permits archive-based disk exhaustion. ### Attack Path 1. Create or obtain a ZIP-compatible `.vpod` containing attacker-selected files. 2. Run or induce the user/agent to run: ```bash python3 scripts/podsync.py import attacker.vpod --name ../../../../tmp/payload ``` 3. `PODS_DIR / pod_name` resolves outside the intended pod root. 4. Because the destination does not already exist, the existence check succeeds. 5. `extractall()` writes the archive contents into the external destination. For resource exhaustion: 1. Construct a highly compressed archive with a very large expanded size or many entries. 2. Import it through `podsync.py`. 3. The importer expands all entries without a quota. 4. Availa ...[truncated 582 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply strict pod-name validation and canonical containment checks before creating the extraction directory. 2. Resolve both the storage root and destination, then require the destination to be a direct child of the trusted root. 3. Inspect every archive member before extraction: - Reject absolute names. - Reject traversal components. - Reject symbolic links and special files. - Resolve the final member path and verify that it remains under the validated destination. 4. Extract entries individually rather than calling `extractall()` on an untrusted archive. 5. Enforce maximum values for entry count, per-entry size, total expanded size, path length, nesting depth, and compression ratio. 6. Extract into a newly created private temporary directory under the pod root, validate the resulting pod structure, and atomically rename it into place. 7. Reject archives containing unexpected top-level files or an invalid/missing pod manifest and database. 8. Clean up partial extraction output on every failure. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:63
Finding
Unpinned Dependencies and Mutable Embedding Model Retrieval Create Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:63-66`; related model initialization occurs at `scripts/ingest.py:132-139` **Vulnerability Type**: Unpinned third-party dependencies and mutable remote artifacts **Risk Level**: Medium ### Vulnerable Code ```bash pip install PyPDF2 python-docx pillow pytesseract sentence-transformers ``` The embedding model is initialized without a pinned revision or trusted local path: ```python def generate_embedding(text: str, model_name: str = 'all-MiniLM-L6-v2'): """Generate embedding for text.""" if not EMBEDDINGS_AVAILABLE: return None try: model = SentenceTransformer(model_name) embedding = model.encode(text, convert_to_numpy=True) return embedding.tobytes() except Exception as e: print(f" Error generating embedding: {e}") return None ``` ### Technical Analysis The installation instructions request package names without exact versions or cryptographic hashes. A future installation therefore resolves whatever releases and transitive dependencies are current at that time. Installation and import of compromised packages could execute attacker-controlled Python code with the user's privileges. `SentenceTransformer(model_name)` may retrieve model files from an external model repository when the model is not already cached. The short model identifier does not pin a reviewed revision or artifact hash. Consequently, the effective model artifact can vary after the Skill itself has been audited. No lockfile, hash-checked requirements file, isolated environment requirement, trusted package index configuration, or offline model policy is included. ### Attack Path 1. A user follows the documented dependency installation command. 2. The package manager resolves current versions and transitive dependencies from its configured index. 3. If a selected release, dependency, package index, or distribution artifact is compromised, its installation ...[truncated 1117 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Provide a reviewed lockfile or fully pinned requirements file with exact package and transitive dependency versions. 2. Require hash verification, for example through `pip install --require-hashes`. 3. Install dependencies in an isolated virtual environment rather than the user's global Python environment. 4. Document and enforce an approved package index instead of inheriting arbitrary environment configuration. 5. Use automated dependency vulnerability scanning and controlled update review. 6. Pin the embedding model to a reviewed repository and immutable revision or commit. 7. Verify model artifact checksums before loading and prefer formats that do not permit arbitrary object deserialization. 8. Support a local model path and an explicit offline mode. 9. Require clear user approval before any network retrieval and document the network destination and downloaded artifacts. 10. Cache only verified model artifacts with restrictive filesystem permissions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Claiming capabilities that do not exist is not just a product-quality issue; it can mislead operators about what data is processed, how, and where. Undeclared export or packaging behavior for LLM/ChatGPT context could move data into new contexts and create confidentiality risks if users assume the skill remains purely local and limited to pod management.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Claiming capabilities that do not exist is not just a product-quality issue; it can mislead operators about what data is processed, how, and where. Undeclared export or packaging behavior for LLM/ChatGPT context could move data into new contexts and create confidentiality risks if users assume the skill remains purely local and limited to pod management.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Claiming capabilities that do not exist is not just a product-quality issue; it can mislead operators about what data is processed, how, and where. Undeclared export or packaging behavior for LLM/ChatGPT context could move data into new contexts and create confidentiality risks if users assume the skill remains purely local and limited to pod management.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Claiming capabilities that do not exist is not just a product-quality issue; it can mislead operators about what data is processed, how, and where. Undeclared export or packaging behavior for LLM/ChatGPT context could move data into new contexts and create confidentiality risks if users assume the skill remains purely local and limited to pod management.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The skill encourages ingestion, semantic indexing, and export/sharing of documents, including potentially sensitive categories like health and research data, without clear privacy and data-handling warnings. In this context, users may unintentionally ingest confidential files, create searchable local embeddings, or share pod archives without understanding the exposure risk.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The README makes strong security claims about a built-in consent layer, explicit permission checks, and audit logging, while also documenting direct pod query commands that appear to bypass that layer. This is dangerous because users may rely on a false access-control boundary for sensitive local data, causing unauthorized agent or local process access if the actual tooling does not enforce consent at the query path.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill advertises and instructs use of shell commands plus file read/write behavior, but it declares no explicit tool scope or permissions boundary. That creates a confused-deputy risk where an agent may execute filesystem and shell actions without transparent authorization, especially because the commands operate on user-provided names, content, and paths.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: data-pods
description: Create and manage modular portable database pods (SQLite + metadata + embeddings). Includes document ingestion with embeddings for semantic search. Full automation - just ask.
---

# Data Pods
Confidence
70% confidence
Finding
The skill is explicitly designed around persistent local storage of pods, metadata, and possibly embeddings, which creates session persistence and data-retention risk. In context, persistence is core functionality, but without clear retention, consent, and visibility controls it can accumulate sensitive user content and make later unauthorized access more damaging.

Vague Triggers

Medium
Confidence
95% confidence
Finding
Overly broad trigger text increases the chance that the skill auto-activates during ordinary conversation and performs file or shell operations without a narrowly expressed user intent. Because this skill can create, modify, ingest, and export local data, accidental invocation materially raises the risk of unintended actions on sensitive files.

Vague Triggers

Medium
Confidence
93% confidence
Finding
Trigger phrases such as 'add note,' 'add content,' or 'search pod' overlap with common user requests and can cause the skill to intercept benign conversation. In a skill with shell, file, and persistence behavior, unintended routing can lead to unauthorized file processing, local data writes, or disclosure of stored pod contents.

Session Persistence

Medium
Category
Rogue Agent
Content
# List pods
python pod.py list

# Create a pod
python pod.py create my-research --type scholar

# Add a note
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The usage guide explicitly encourages exporting pod data for sharing but does not warn that the archive may contain sensitive note content, metadata, and potentially derived embedding data. In a data-management skill, this omission can lead users to disclose private research, notes, or other stored information unintentionally.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes a skill for creating and managing portable database pods with ingestion and semantic search. This file instead implements agent consent session management and audit logging in a separate SQLite database under ~/.openclaw/consent, which is a materially different operational scope rather than an obvious implementation detail of pod creation or ingestion.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The functions grant, revoke, check_consent, and log_access implement authorization and auditing features for agents, including session issuance and access logging. Those capabilities are not mentioned in the manifest's stated purpose of managing database pods and document ingestion, so they introduce a governance/control plane unrelated to the declared user-facing scope.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The audit log persists raw query text to a local SQLite database, which can capture sensitive user inputs, secrets, personal data, or proprietary search terms without any disclosure, minimization, or redaction. In a data-pod skill handling document ingestion and semantic search, query contents are especially likely to contain confidential business or personal information, increasing privacy and data-retention risk.

Missing User Warnings

Medium
Confidence
71% confidence
Finding
The code passes extracted document text into `SentenceTransformer(model_name)`, which in many environments can trigger model downloads or interaction with external resources, and users are not warned that document-derived text is being processed for embeddings. The file mentions embeddings generically, but it does not disclose this data-processing behavior in a user-facing warning.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script stores full extracted document contents and chunked text into a local SQLite database without any explicit consent prompt, warning, or data-retention notice. Because this skill is designed for broad document ingestion and automation, users may unintentionally persist sensitive local file contents in a long-lived pod database, increasing confidentiality and privacy risk if the pod is later accessed, copied, or exposed.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The skill description says it includes document ingestion with embeddings for semantic search. In this file, the embeddings functionality is not implemented beyond schema creation, and query behavior is limited to raw SQL or LIKE-based text search rather than semantic retrieval.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The query command executes caller-supplied raw SQL directly against the pod database with no restriction to read-only statements. This enables any user or upstream agent invoking the skill to read, modify, or delete pod contents, and potentially attach other SQLite databases or use dangerous pragmas depending on the runtime environment.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The CLI exposes a raw --sql option without warning that statements may be destructive, and it routes directly into unrestricted execution. In an agent skill context, this is more dangerous because higher-level automation may treat query as informational while a prompt or tool caller can issue DELETE, DROP, UPDATE, or other state-changing commands.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
`zipf.extractall(pod_path)` extracts untrusted archive contents without validating member paths, enabling zip-slip style path traversal if a crafted archive contains filenames such as `../...`. An attacker could overwrite arbitrary files writable by the user outside the pod directory during import, which is more severe than merely lacking a warning about disk writes.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The `pack_for_llm` feature intentionally converts all note contents from a pod into a single markdown file explicitly described as 'Ready to paste into ChatGPT!'. This creates a clear data exfiltration path for potentially sensitive pod contents to external LLM services, and the skill context only partially justifies it because the manifest emphasizes pod management/sync rather than exporting private data to third-party AI systems.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
This code adds a capability to prepare pod contents for use with external LLMs, which materially expands the data-sharing surface beyond local pod storage and ordinary sync/export. Even if intended as a convenience feature, bundling full note bodies into a shareable file can expose confidential information to users, other local processes, or third-party services once uploaded.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The LLM pack operation writes complete note contents to a markdown file intended for sharing with ChatGPT, but provides no privacy warning or confirmation despite the high likelihood that pods contain personal or sensitive information. In this skill context, that omission makes accidental disclosure more likely because the feature encourages copying the output into an external service.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The pod layout says `embeddings/` is a future component, but later sections advertise v0.2 document ingestion with embeddings, semantic search, and related features as available commands. These statements directly conflict about whether embeddings support is implemented now or only planned.

Static analysis

No suspicious patterns detected.