Back to skill

Security audit

todowrite

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent task-management helper, but it contains instructions that can create external tracking records and delete or prune task state without clear user confirmation.

Install only if you are comfortable with this skill managing persistent task ledgers and with agents following its workflow rules. Before use, require explicit confirmation for Plane issue creation, prior-session task deletion, prune or purge operations, and any --dir targeting outside the normal task directories.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Findings (2)

other

Warning
Location
completion-report.md:84
Finding

Automatic Creation of an Unrequested External Tracking Issue

Content
View full analysis

Vulnerability Details

File Location: completion-report.md, lines 84–92
Vulnerability Type: Unauthorized external resource creation
Risk Level: Medium

Vulnerable snippet:

markdown
## External-resource completion — verifiable URL required (HARD STOP)

When a task's deliverable is a non-git external resource (a form, a deployed page, a third-party SaaS record, an API-mutated document) rather than a commit or local file, the completion report MUST include a URL the user can open independently to verify the result. "I re-fetched it and confirmed" is not sufficient on its own — the user needs their own way to check, not just the assistant's word.

**Priority order** — use the first one that applies:

1. **The resource's own URL** (the form link, the deployed page URL, the record's direct link)
2. **A Plane issue URL** tracking the work, if the resource itself has no shareable URL
3. **Create a Plane intake issue on the spot** and include its URL, if neither #1 nor #2 exists — do not close out the completion report with no URL at all

Technical Analysis

The completion-report instructions impose a mandatory fallback that creates a new Plane issue whenever an external deliverable lacks both a direct URL and an existing tracking issue. The instruction does not require explicit user approval before creating this additional external resource.

Completing or updating the user-selected external resource does not inherently authorize publication of task information into a separate team-visible tracking system. This crosses a trust boundary from reporting the authorized operation to mutating an additional external service. Depending on the content used to construct the issue, task details may also become visible to other members of the Plane workspace.

This is high-risk behavior rather than evidence of malicious intent: the stated purpose is verifiability, but the mandatory instruction expands the operation bey ...[truncated 1116 chars]

Remediation
View remediation

Remediation Suggestions

Replace the automatic fallback with an explicit consent gate:

  1. If no direct or existing tracking URL is available, explain that the completion cannot include a verifiable link.
  2. Ask the user whether a Plane intake issue should be created.
  3. Show the proposed project, title, and body before submission.
  4. Create the issue only after affirmative approval.
  5. Minimize the issue content to fields necessary for verification and avoid including credentials, private URLs, or unrelated task context.
  6. If approval is denied or unavailable, complete the report with a clear statement that no independently accessible URL exists rather than creating another resource.

other

Warning
Location
conversation-id.md:74
Finding

Automatic Deletion of Prior-Session Task State Without User Confirmation

Content
View full analysis

Vulnerability Details

File Location: conversation-id.md, lines 74–80
Vulnerability Type: Unauthorized task-state mutation
Risk Level: Medium

Vulnerable snippet:

markdown
## TaskList check obligation

Before starting new work, **always check TaskList first** — if pending tasks exist, handle them first or report to the user.

- After subagent completion, re-check TaskList before processing results
- Do not re-ask via `AskUserQuestion` for content already represented as a task — continue the existing task
- Do not ignore existing items and create new ones
- **Stale tasks from prior sessions**: if `TaskList` shows completed/in_progress tasks whose context is gone, run `TaskUpdate(status: "deleted")` to clear them — orphan tasks from prior sessions carry no usable context

Technical Analysis

The Skill requires the agent to mark prior-session tasks as deleted when it determines that their context is gone. The condition is subjective and is not coupled to a user confirmation step, an ownership check, or a requirement that the task actually be obsolete.

Inspecting a task list to identify existing work does not itself authorize mutation of prior-session records. The instruction therefore crosses the boundary between read access for task routing and destructive state management. An in_progress task may remain active even when its originating conversation context is unavailable to the current agent.

The CLI treats deleted as a soft-deletion state, and its pruning workflow can subsequently archive deleted records or permanently remove them when purge is requested. The immediate impact is loss of task-list visibility and integrity; later cleanup can compound that impact.

Attack Path

  1. The Skill is invoked while the task ledger contains completed or in-progress entries from an earlier session.
  2. The current agent cannot recover the original conversational context and classifies those entrie ...[truncated 767 chars]
Remediation
View remediation

Remediation Suggestions

Replace automatic deletion with a confirmation-based stale-task workflow:

  1. Treat missing conversational context as insufficient evidence that a task is obsolete.
  2. List the candidate tasks by subject, current status, and last-known metadata.
  3. Require explicit user approval before changing any task to deleted.
  4. Offer non-destructive alternatives such as leaving the task unchanged, marking it blocked, or adding a review-needed annotation.
  5. Restrict bulk cleanup to tasks whose ownership and session scope have been verified.
  6. Preserve an audit record of the approving user and deletion reason.
  7. Keep pruning and permanent purge as separate, explicitly authorized operations.
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (19)

Dangerous chain: exec() wrapping compile

Critical
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

This file reads Python source from a sibling file and executes it dynamically with exec(compile(...), globals()). Even though the path is not user-supplied, this bypasses normal import safety and means any tampering with agent-task.py results in arbitrary code execution in the current process with shared globals, which is especially risky in an agent skill context that may run with privileged filesystem or API access.

Content

Scanner excerpt · resources/claude-task.py (reported line 6)May include surrounding context.

python
from pathlib import Path

_source = Path(__file__).resolve().with_name("agent-task.py")
exec(compile(_source.read_text(encoding="utf-8"), str(_source), "exec"), globals())

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

If this skill acts as a loader or compatibility shim that executes logic from another file such as agent-task.py, while presenting itself as a benign TODO-routing skill, that creates a trust-boundary problem. Reviewers and users may approve or invoke it based on the harmless description while hidden delegated code performs materially different actions, enabling tool misuse or unexpected side effects.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

If this skill acts as a loader or compatibility shim that executes logic from another file such as agent-task.py, while presenting itself as a benign TODO-routing skill, that creates a trust-boundary problem. Reviewers and users may approve or invoke it based on the harmless description while hidden delegated code performs materially different actions, enabling tool misuse or unexpected side effects.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

If this skill acts as a loader or compatibility shim that executes logic from another file such as agent-task.py, while presenting itself as a benign TODO-routing skill, that creates a trust-boundary problem. Reviewers and users may approve or invoke it based on the harmless description while hidden delegated code performs materially different actions, enabling tool misuse or unexpected side effects.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

If this skill acts as a loader or compatibility shim that executes logic from another file such as agent-task.py, while presenting itself as a benign TODO-routing skill, that creates a trust-boundary problem. Reviewers and users may approve or invoke it based on the harmless description while hidden delegated code performs materially different actions, enabling tool misuse or unexpected side effects.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

If this skill acts as a loader or compatibility shim that executes logic from another file such as agent-task.py, while presenting itself as a benign TODO-routing skill, that creates a trust-boundary problem. Reviewers and users may approve or invoke it based on the harmless description while hidden delegated code performs materially different actions, enabling tool misuse or unexpected side effects.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

This rule treats common conversational verbs like "transfer," "move," and "relocate" as hard-stop triggers for cross-medium state changes, including deleting tasks. In an agent skill for TODO routing, that creates a semantic-overloading vulnerability: ordinary user phrasing can be misclassified as authorization to perform destructive workflow actions, causing unintended task deletion or checklist mutation.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · resources/agent-task.py (reported line 74)May include surrounding context.

python
search_bases = [AGENTS_TASKS_BASE]

    openclaw = env_type == "openclaw" or any(
        key.startswith("OPENCLAW_") for key in os.environ
    )
    # OpenClaw caller identity must be explicit, not a stale inherited Claude ID.
    if openclaw and not session_id:

exec() call detected

High
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

The exec() call executes arbitrary Python code text at runtime in the current global namespace. In this case the executed text comes from a local file, but if that file is replaced, modified, or influenced through packaging/supply-chain compromise, the skill will run attacker-controlled code directly.

Content

Scanner excerpt · resources/claude-task.py (reported line 6)May include surrounding context.

python
from pathlib import Path

_source = Path(__file__).resolve().with_name("agent-task.py")
exec(compile(_source.read_text(encoding="utf-8"), str(_source), "exec"), globals())

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · resources/tests/test_agent_task.py (reported line 17)May include surrounding context.

python
def setUp(self):
        self.fixture = tempfile.TemporaryDirectory()
        self.home = Path(self.fixture.name)
        self.env = {k: v for k, v in os.environ.items() if not k.startswith(("CLAUDE_", "AGENT_", "OPENCLAW_"))}
        self.env["HOME"] = str(self.home)
    def tearDown(self):
        self.fixture.cleanup()

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · agent-task.md (reported line 54)May include surrounding context.

md
## Operations and safety

- `list`/`ls`, `show`/`get`, `dir` and `prune --dry-run` never create directories,
  locks or watermarks. Missing list returns `[]`; missing show exits 1.
- `add`/`create` reserves numeric IDs above `.highwatermark` and existing filenames.
- `update`/`edit` changes fields/dependencies. `--status deleted` is soft deletion.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · conversation-id.md (reported line 79)May include surrounding context.

md
- After subagent completion, re-check TaskList before processing results
- Do not re-ask via `AskUserQuestion` for content already represented as a task — continue the existing task
- Do not ignore existing items and create new ones
- **Stale tasks from prior sessions**: if `TaskList` shows completed/in_progress tasks whose context is gone, run `TaskUpdate(status: "deleted")` to clear them — orphan tasks from prior sessions carry no usable context

## Reference

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Phrases such as "to the checklist" and "next session" are underspecified and can appear in normal planning discussion without intending an immediate medium transfer. In this skill, those phrases are bound to mandatory state transitions like deleting a task and creating a checklist entry, so ambiguous natural language can trigger unintended side effects.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

resolve_task_dir() accepts an arbitrary --dir and returns it without constraining it to approved task roots. Because writer commands create directories, write JSON, rename files, and delete files under that path, a caller can repurpose this skill to modify arbitrary filesystem locations instead of only the declared TODO storage areas.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · resources/agent-task.py (reported line 137)May include surrounding context.

python
@contextmanager
def ledger_lock(task_dir: Path):
    """All CLI writers serialize the entire read/modify/write transaction."""
    task_dir.mkdir(parents=True, exist_ok=True)
    with open(task_dir / ".agent-task.lock", "a+b") as lock:
        if os.name == "nt":

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest describes routing TODO checklists to appropriate storage and maintaining conversational/task-list discipline, but this file implements hard deletion (delete) and pruning/archival/purging of task files. Those lifecycle-management operations go beyond simple TODO routing/checklist recording and materially modify stored task data.

Content

No source excerpt is available for this finding.

compile() call detected

Medium
Category
Dangerous Code Execution
Confidence
93% confidence
Finding

compile() is being used as part of a dynamic code-loading chain that turns file contents into executable code. On its own compile() is not necessarily unsafe, but here it directly enables runtime execution of externally stored source code, making the overall construct dangerous if the sibling file is ever tampered with.

Content

Scanner excerpt · resources/claude-task.py (reported line 6)May include surrounding context.

python
from pathlib import Path

_source = Path(__file__).resolve().with_name("agent-task.py")
exec(compile(_source.read_text(encoding="utf-8"), str(_source), "exec"), globals())

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · resources/tests/test_agent_task.py (reported line 22)May include surrounding context.

python
def tearDown(self):
        self.fixture.cleanup()
    def run_cli(self, *args, legacy=False, env=None):
        return subprocess.run([sys.executable, str(RESOURCE / ("claude-task.py" if legacy else "agent-task.py")), *args], env=env or self.env, capture_output=True, text=True)
    def test_alias_and_flag_placement(self):
        for legacy in (False, True):
            for args in (("--env", "agent", "--session", "A", "list", "--json"), ("list", "--json", "--env", "agent", "--session", "A")):

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
resources/tests/test_agent_task.py:87

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
resources/tests/test_claude_task.py:24