Back to skill

Security audit

Easy TODO list management for busy crustaceans and their humans

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local TODO-list skill, with some operational risks around automatic reminders and fragile local storage handling.

Install this only if you want a local TODO manager that can generate scheduled summaries and create recurring task instances automatically. Keep backups for important task data, avoid setting TODOS_FILE to unrelated paths, and be cautious about adding untrusted task text because the current storage parser can corrupt the TODO file if reserved markers appear in task content.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
cli.js:105
Finding
Store Delimiter Injection Can Corrupt Parsing and Erase Existing TODO Data<![CDATA[ ## Vulnerability Details **File Location**: `cli.js:105-120`, with the attacker-controlled fields introduced at `cli.js:216-237` **Vulnerability Type**: Unsafe delimiter-based parsing of attacker-controlled persistent data **Risk Level**: Medium ### Vulnerable Code ```js function readStore() { if (!fs.existsSync(TODOS_FILE)) return { ...EMPTY_STORE, tasks: [], recurringTasks: [] }; const content = fs.readFileSync(TODOS_FILE, "utf8"); const s = content.indexOf(STORE_START); const e = content.indexOf(STORE_END); if (s === -1 || e === -1) return { ...EMPTY_STORE }; try { return JSON.parse(content.slice(s + STORE_START.length, e).trim()); } catch { return { ...EMPTY_STORE }; } } function writeStore(store) { const json = JSON.stringify(store, null, 2); fs.writeFileSync(TODOS_FILE, renderMarkdown(store, json), "utf8"); } ``` The affected input fields are populated directly from command-line input: ```js function cmdAdd(pos, flags) { const title = pos[0] || flags["title"]; if (!title) return die('task title required: node cli.js add "<title>"'); const store = readStore(); const task = { id: nextId(store, "T"), title, notes: flags["notes"] || undefined, dueDate: flags["due"] || undefined, priority: flags["priority"] || "medium", status: "active", createdAt: new Date().toISOString(), tags: flags["tags"] ? flags["tags"].split(",").map(s => s.trim()) : [], }; store.tasks.push(task); writeStore(store); const due = task.dueDate ? ` (due ${task.dueDate})` : ""; const prio = task.priority !== "medium" ? ` [${task.priority}]` : ""; console.log(`Added ${task.id}: ${task.title}${due}${prio}`); } ``` ### Technical Analysis The application embeds a JSON document inside Markdown using the textual markers `<!-- STORE_JSON` and `STORE_JSON -->`. It retrieves the end of the JSON document with: ```js const e = content.indexOf(STORE_END); ``` Task ...[truncated 2456 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Separate machine-readable storage from rendered Markdown.** Persist authoritative state in a dedicated JSON file and generate `todos.md` only as a human-readable view. Parse the JSON file directly rather than extracting JSON with textual sentinels. 2. **Fail closed on malformed storage.** Do not return an empty store after a parsing error. Report the corruption, terminate with a nonzero status, and avoid all writes until the data has been recovered. ```js try { return JSON.parse(rawData); } catch (error) { throw new Error(`TODO store is corrupted: ${error.message}`); } ``` 3. **Reject reserved delimiters if embedded JSON must be retained.** Validate every string field, including titles, notes, and tags, and reject values containing `STORE_START` or `STORE_END`. This is a defense-in-depth measure rather than a substitute for eliminating delimiter-based parsing. 4. **Use an unambiguous extraction strategy.** If the existing format must remain compatible, locate and validate the genuine final marker rather than the first occurrence, and verify that the extracted content forms exactly one valid JSON document. This reduces exposure but remains less robust than separate storage. 5. **Implement atomic writes.** Write the new database to a temporary file in the same directory, flush it, and atomically rename it over the destination. This prevents partial writes from causing additional corruption. 6. **Preserve recoverable backups.** Before replacing a valid store, retain a restricted-permission backup. Never replace the current store when reading or schema validation has failed. 7. **Add regression tests.** Test titles, notes, and tags containing `STORE_JSON -->`, `<!-- STORE_JSON`, Markdown syntax, newlines, and Unicode. Verify that malformed storage causes a visible error and never results in an automatic overwrite. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Ae1

High
Category
analysis-evasion
Content
The file is managed entirely by `cli.js` — never edit it by hand. It contains a JSON block (machine-readable) and Markdown tables (human-readable) that are rege
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill invokes a local Node CLI and accesses a persistent file in the user's home directory, but it does not declare any explicit tool scope such as allowed tools or permissions. That makes the execution boundary unclear and can enable broader-than-expected code or environment access if the platform defaults are permissive, reducing transparency and increasing the chance of unintended actions.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs the agent to automatically run scheduled briefings and a daily `materialize` action without waiting for a user request. This creates autonomous reads and writes against user task data, which can surprise users, generate unsolicited messages, and modify state without explicit opt-in or runtime confirmation.

Context-Inappropriate Capability

Low
Confidence
91% confidence
Finding
The CLI allows the output file path to be fully controlled by the TODOS_FILE environment variable, and later reads from and writes to that path without restriction. In an agent or skill execution context, environment variables may be influenced externally, so this can redirect writes to unintended files and cause data overwrite or disclosure outside the expected TODO store location.

Missing User Warnings

Low
Confidence
94% confidence
Finding
Because writeStore() writes directly to TODOS_FILE, a caller can silently redirect the CLI's persistent output to any filesystem path writable by the process. The lack of any user-facing warning or path validation makes this especially risky in automated agent settings, where a seemingly harmless TODO operation could overwrite unrelated files.

Static analysis

No suspicious patterns detected.