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

Content
View full analysis

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.

...[truncated 719 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace the timestamp-derived identifier with a collision-resistant value:
python
from uuid import uuid4

todo_id = str(uuid4())
  1. If sortable identifiers are required, combine a high-resolution timestamp with a cryptographically random suffix.
  2. Before insertion, verify that the generated identifier is not already present.
  3. For update and deletion operations, require exactly one matching record. Reject the operation if zero or multiple records match.
  4. Change deletion to locate and remove one uniquely identified record rather than filtering out every matching record.
  5. 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

Content
View full analysis

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
View 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
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.