Back to skill

Security audit

Work Todo - 工作待办管理

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed local work-todo manager, with no hidden execution or network behavior, but its todo storage has data-loss risks.

Install only if you are comfortable with a local shared todo file being modified and deleted by the agent. Avoid relying on it as the only copy of important work-tracking data until IDs are made collision-resistant and deletion/update operations require an exact match or confirmation.

Vulnerability Patterns
  • 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
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:177
Finding
Predictable and Colliding Todo Identifiers Can Modify or Delete Unrelated Tasks## Vulnerability Details **File Location**: `SKILL.md:177-180, 241-244, 355-358, 382-386` **Vulnerability Type**: Non-unique predictable identifiers and unsafe record selection **Risk Level**: Medium ### Vulnerable Code ```python # Generate ID todo_id = datetime.now().strftime("%Y%m%d%H%M")[:10] ``` The generated identifier is subsequently trusted by status and progress update operations: ```python for todo in data["todos"]: if todo["id"] == todo_id: todo["status"] = new_status ``` ```python for todo in data["todos"]: if todo["id"] == todo_id: # Check progress type prog = todo.get("progress") ``` Deletion removes every task with the supplied identifier: ```python def delete_todo(todo_id): """Delete a todo""" data = load_todos() data["todos"] = [t for t in data["todos"] if t["id"] != todo_id] save_todos(data) ``` ### Technical Analysis `datetime.now().strftime("%Y%m%d%H%M")` produces a 12-character value in the form `YYYYMMDDHHMM`. Truncating it to ten characters produces `YYYYMMDDHH`, so all todos created during the same hour receive the same identifier. This contradicts the documented requirement that `id` be unique. Update operations stop after finding the first matching identifier, meaning they can modify an unintended task. The deletion operation filters out all matching records and therefore deletes every task created during the same hour. The identifier is also predictable because it is derived solely from the current local time. No uniqueness check is performed before the task is appended. ### Attack Path 1. A legitimate task is created during a particular hour and receives an identifier such as `2026091614`. 2. Another task is created during the same hour, whether accidentally or by a user able to add tasks. 3. The second task receives the same identifier. 4. A status or progress update using that identifier changes the first matching task rather than necessarily changing the intended task. 5. ...[truncated 719 chars]
Remediation
## Remediation Suggestions 1. Replace the timestamp-derived identifier with a collision-resistant value: ```python from uuid import uuid4 todo_id = str(uuid4()) ``` 2. If sortable identifiers are required, combine a high-resolution timestamp with a cryptographically random suffix. 3. Before insertion, verify that the generated identifier is not already present. 4. For update and deletion operations, require exactly one matching record. Reject the operation if zero or multiple records match. 5. Change deletion to locate and remove one uniquely identified record rather than filtering out every matching record. 6. Add tests that create multiple tasks in the same second and verify that all identifiers remain unique.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:89
Finding
Non-Atomic and Unprotected Writes to a Shared Todo File## Vulnerability Details **File Location**: `SKILL.md:89-104` **Vulnerability Type**: Unsafe shared-file handling, concurrent write race, and potential symlink redirection **Risk Level**: Medium ### Vulnerable Code ```python import json from pathlib import Path TODO_FILE = Path("~/.openclaw/workspace/shared/work-todo/lwork/todos.json").expanduser() def load_todos(): if not TODO_FILE.exists(): return {"todos": []} with open(TODO_FILE, 'r', encoding='utf-8') as f: return json.load(f) ``` ```python def save_todos(data): TODO_FILE.parent.mkdir(parents=True, exist_ok=True) with open(TODO_FILE, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The todo database is placed under a declared shared workspace and is opened directly with truncating write mode. The implementation does not: - Apply explicit restrictive permissions to the directory or file. - Lock the file across the read-modify-write transaction. - Write to a temporary file and atomically replace the destination. - Verify that the destination is a regular file rather than a symbolic link. - Detect whether another process changed the file after it was read. Opening the destination with mode `w` truncates it before serialization completes. A crash or interrupted write can therefore leave empty or malformed JSON. Concurrent skill invocations can both read the same initial state and then overwrite each other, causing a lost-update condition. If another local principal can write to the shared directory, that principal may replace `todos.json` with a symbolic link. A subsequent save would follow that link and overwrite the linked target, limited to files writable by the account running the agent. This symlink path depends on the actual permissions of the shared directory; those permissions are not defined by the skill. ### Attack Path #### Concurrent Lost-Update Path 1. Two skill invocations call `load_todos ...[truncated 1575 chars]
Remediation
## Remediation Suggestions 1. Store the database in a private directory unless cross-user sharing is explicitly required. 2. Enforce restrictive permissions, such as `0700` for the directory and `0600` for the JSON file. 3. Protect the entire read-modify-write transaction with an inter-process file lock. 4. Write serialized data to a securely created temporary file in the same directory, flush and `fsync` it, and then use `os.replace()` for atomic replacement. 5. Verify that the destination and parent path are not symbolic links. Where supported, open files using `O_NOFOLLOW` and validate them with `fstat()`. 6. Validate the loaded JSON structure and preserve a recoverable backup before replacement. 7. Use optimistic concurrency control, such as a revision number or content hash, to detect stale writes. 8. Document the ownership and access model for the shared directory rather than relying on inherited permissions. A hardened save flow should follow this sequence: ```python # Conceptual sequence: # 1. Acquire an inter-process lock. # 2. Re-read and validate the current database. # 3. Create a temporary file securely in the same directory. # 4. Set mode 0600. # 5. Serialize, flush, and fsync. # 6. Atomically replace todos.json. # 7. Fsync the parent directory and release the lock. ```
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (2)

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill defines a destructive delete_todo operation that removes matching entries immediately and the surrounding instructions do not require user confirmation, preview, undo, or safeguards against ambiguous task selection. In a conversational agent, this increases the risk of accidental or mistaken data loss from natural-language misunderstandings, especially when multiple todos may be similar.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The skill documentation and examples consistently prescribe Chinese-language interaction and output, but do not offer the user any language or locale choice. This can violate language/locale policy when a skill forces a specific language without user opt-in.

Static analysis

No suspicious patterns detected.