Back to skill

Security audit

Task Ledger

Security checks for vulnerabilities and agentic risk

Overview

This workflow helper is mostly coherent, but some scripts can read or write outside the intended task folders when given unsafe task data.

Review before installing. Use this only in trusted workspaces, avoid passing untrusted task IDs or task JSON into its scripts, and fix task-ID validation plus output path containment before relying on it for sensitive deployments, document sync, cron, or long-running automation.

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

T09 · Insecure Skill Coding Practices

Error
Location
toolkit/scripts/new-task.sh:10
Finding
Unvalidated Task Identifiers Permit Path Traversal and Filesystem Writes<![CDATA[ ## Vulnerability Details **File Location**: `toolkit/scripts/new-task.sh:10,43-58`; `toolkit/scripts/update-task.py:49,83,119-120,391`; the same unsafe task-path construction is also present in `close-task.py:33,40,99`, `task-advance.py:23,25,120`, `task-bind-cron.py:23,43,73`, `task-bind-process.py:24,48,72`, `task-bind-subtask.py:23,31,56`, `task-start-if-ready.py:120,122,186`, and `task-verify.py:32,36,113`. **Vulnerability Type**: Path traversal and insufficient filesystem path confinement **Risk Level**: High ### Vulnerable Code From `toolkit/scripts/new-task.sh`: ```bash slug="$1" title="$2" goal="${3:-}" execution_mode="${4:-background-process}" stages_csv="${5:-prepare,execute,verify}" priority="${6:-normal}" owner="${7:-main}" ``` ```bash task_id="${slug}-${timestamp}" mkdir -p tasks logs outputs "outputs/${task_id}" : > "logs/${task_id}.log" stages_json="" for stage_id in "${stages[@]}"; do if [[ -n "$stages_json" ]]; then stages_json+=$'\n , ' else stages_json+=" " fi stages_json+="{ \"id\": \"${stage_id}\", \"status\": \"todo\" }" done cat > "tasks/${task_id}.json" <<EOF ``` From `toolkit/scripts/update-task.py`: ```python def task_exists(task_id): return (TASKS_DIR / f'{task_id}.json').exists() ``` ```python task_id = sys.argv[1] path = TASKS_DIR / f"{task_id}.json" if not path.exists(): die(f"Task not found: {task_id}", 2) data = json.loads(path.read_text()) ``` ```python path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + '\n') ``` ### Technical Analysis Task identifiers are accepted from command-line arguments and concatenated directly into filesystem paths. Neither the shell creation script nor the Python mutation helpers reject path separators, `..` components, absolute paths, or symlink targets. In `new-task.sh`, the attacker-controlled slug becomes part of paths under `tasks`, `logs`, and `outputs`. Normal path resolution processes traversal components before the timestamp suf ...[truncated 2365 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define one canonical task-ID validator and apply it in every script before constructing a path. A conservative pattern is: ```text ^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$ ``` 2. Reject identifiers containing `/`, `\`, `..`, control characters, leading dots, or platform-specific path prefixes. 3. In Python, resolve both the trusted root and candidate path and enforce containment: ```python root = TASKS_DIR.resolve() candidate = (root / f"{task_id}.json").resolve() if candidate.parent != root: die("Invalid task ID") ``` 4. Where nested paths are not required, require the resolved candidate's direct parent to equal the expected root rather than relying only on a prefix comparison. 5. Reject symlink destinations or open files using operating-system facilities that do not follow symlinks where supported. 6. Use exclusive creation for new task and log files so existing files are not silently overwritten. 7. Derive all log and output paths from the validated canonical task ID. 8. Add automated tests covering `../`, absolute paths, repeated separators, backslashes, encoded separators, symlink escapes, empty identifiers, and overlong identifiers. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
toolkit/scripts/task-export.py:237
Finding
Mutable Task Metadata Controls an Unconfined Report Write Destination<![CDATA[ ## Vulnerability Details **File Location**: `toolkit/scripts/task-export.py:237-252` **Vulnerability Type**: Arbitrary file write through untrusted output path metadata **Risk Level**: High ### Vulnerable Code ```python def write_long_report(task_path, data, markdown): reporting = data.setdefault('reporting', { 'mode': 'short-first', 'preferFileBackedReports': True, 'longReportPath': None, }) if not reporting.get('preferFileBackedReports', True): return None output_dir = ((data.get('artifacts') or {}).get('outputDir')) if not output_dir: return None report_path = Path(output_dir) / 'report.md' full_path = ROOT / report_path full_path.parent.mkdir(parents=True, exist_ok=True) full_path.write_text(markdown) reporting['longReportPath'] = str(report_path) task_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + '\n') return str(report_path) ``` ### Technical Analysis The exporter reads `artifacts.outputDir` from a mutable task JSON document and treats it as a trusted filesystem path. It does not require that the path be relative, normalize traversal components, verify containment under `ROOT/outputs`, or reject symlinks. With `pathlib`, joining `ROOT` to an absolute `report_path` does not guarantee confinement: an absolute right-hand path can replace the preceding root. Relative paths containing `..` can likewise escape the workspace after filesystem resolution. The exporter then creates parent directories and writes `report.md` at the attacker-selected destination. The write uses normal overwrite behavior and follows filesystem symlinks. ### Attack Path 1. An attacker creates or modifies a task JSON record. 2. The attacker sets `artifacts.outputDir` to an absolute directory or a relative path containing traversal components. 3. A user or agent runs: ```bash ./scripts/task-export.py <taskId> --write-report ``` 4. `write_long_report()` app ...[truncated 1206 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not trust `artifacts.outputDir` as an authoritative write location. Derive the report destination from a validated task ID: ```python outputs_root = (ROOT / "outputs").resolve() report = (outputs_root / validated_task_id / "report.md").resolve() ``` 2. Verify that the resolved destination is a descendant of `outputs_root` before creating directories or opening the file. 3. Reject absolute paths, `..` components, empty components, control characters, and platform-specific path prefixes in stored path metadata. 4. Treat `artifacts.outputDir` as display metadata only, or validate it when loading every task record. 5. Reject symlinked output directories and report files where possible. Use secure file-opening flags that prevent symlink following. 6. Use atomic report creation through a temporary file in the validated destination followed by a controlled rename. 7. If overwriting reports is expected, verify the existing target is a regular file owned by the expected user. Otherwise, use exclusive creation. 8. Add tests proving that absolute paths, traversal paths, and symlink chains cannot cause writes outside `ROOT/outputs`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
toolkit/scripts/new-task.sh:48
Finding
Manual Shell Interpolation Allows Task JSON Corruption and Property Injection<![CDATA[ ## Vulnerability Details **File Location**: `toolkit/scripts/new-task.sh:48-137` **Vulnerability Type**: Improper JSON serialization and structured-data injection **Risk Level**: Medium ### Vulnerable Code ```bash stages_json="" for stage_id in "${stages[@]}"; do if [[ -n "$stages_json" ]]; then stages_json+=$'\n , ' else stages_json+=" " fi stages_json+="{ \"id\": \"${stage_id}\", \"status\": \"todo\" }" done cat > "tasks/${task_id}.json" <<EOF { "taskId": "${task_id}", "title": "${title}", "goal": "${goal}", "status": "pending", "priority": "${priority}", "createdAt": "${iso_time}", "startedAt": null, "updatedAt": "${iso_time}", "completedAt": null, "lastVerifiedAt": null, "ownerSession": "main", "owner": "${owner}", "assignedAgent": null, "executionMode": "${execution_mode}", "idempotent": true, "canResume": true, "retryCount": 0, "maxRetries": 2, "stage": "${first_stage}", "stages": [ ${stages_json} ], "dependsOn": [], "blockedBy": [], "blockedReason": null, "nextAction": "Start ${first_stage}", "resumeHint": "Verify real state before resuming; do not blindly rerun side effects.", "decisionNotes": "", "workingSummary": "", "artifacts": { "logPath": "logs/${task_id}.log", "outputDir": "outputs/${task_id}", "files": [] }, "process": { "sessionId": null, "pid": null }, "subtask": { "sessionKey": null, "agentId": null }, "cron": { "jobId": null, "schedule": null, "nextRunAt": null }, "rollback": { "available": false, "strategy": null, "status": "not-applicable", "artifacts": [] }, "notifications": { "notifiedStart": false, "notifiedCompletion": false, "notifiedRecovery": false }, "events": [ { "ts": "${iso_time}", "type": "task.created", "message": "Task skeleton created", "details": { "status": "pending", "stage": "${first_stage}", ...[truncated 2586 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Stop constructing JSON through shell string interpolation. 2. Generate the complete task object with a JSON-aware implementation such as Python: ```python task = { "taskId": task_id, "title": title, "goal": goal, "stages": [{"id": stage, "status": "todo"} for stage in stages], } json.dump(task, output, ensure_ascii=False, indent=2) ``` 3. Alternatively, use `jq --arg` and `jq --argjson` so every string receives correct JSON escaping. 4. Validate all fields before serialization: - Apply the canonical task-ID pattern. - Restrict stage IDs to a conservative identifier syntax. - Restrict execution modes to documented values. - Enforce maximum lengths. - Reject control characters where they are not required. 5. Validate the generated object against `toolkit/task-templates/task.schema.json` before committing it to disk. 6. Write to a temporary file in the validated `tasks` directory, parse the temporary file back as JSON, and atomically rename it only after validation succeeds. 7. Add tests using embedded quotes, backslashes, Unicode, newlines, JSON delimiters, and attempted property injection. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents this skill as a durable workflow system for managing long-running jobs. The provided code does not implement workflow orchestration or execution behavior; it only bootstraps files into a workspace by creating directories and copying templates/scripts when absent. That is a materially different primary purpose from the declared description, so this is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The supplied code does not appear malicious or unrelated, but it is materially narrower than the declared description. The description presents a comprehensive durable workflow layer for long-running, recoverable, parallel, auditable execution. In contrast, this code is a single command-line script whose purpose is to finalize an existing task record in local JSON storage: validate a final status, update timestamps and status fields, set/reset result/error fields, normalize stage states, append an audit event, and save the file. The audit/event behavior aligns loosely with 'auditable outputs,' and task/stage state management is consistent with a workflow system, but this chunk does not itself implement the broader workflow-layer capabilities claimed in the description. Therefore this is a description/behavior mismatch due to overbroad declared purpose versus the actual narrow behavior shown.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a broad durable workflow system for orchestrating long-running work, including recoverability, parallelization, background execution, external side effects, and resumable/auditable execution. The supplied code instead is a narrow inspection utility: it reads task JSON files from a local tasks directory, derives dependency readiness states, and prints a report of currently open tasks. While it relates to task/workflow metadata, it does not implement the primary capabilities claimed in the description. This is a material purpose mismatch: the code is a status-reporting/listing script, not a durable workflow layer.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a general-purpose durable workflow layer/runtime for orchestrating long-running recoverable work. The supplied code is not such a workflow layer; it is a one-off maintenance/migration utility for task metadata files. Its primary behavior is reading and rewriting local JSON task files to enforce schema defaults and record a migration event. While task records and audit-style events are conceptually adjacent to workflow systems, this code does not implement background execution, parallelized sub-agents, resumability mechanics, cron handling, or orchestration logic. Therefore the actual code's primary purpose materially differs from the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents this skill as a durable workflow layer for managing complex long-running work, including recovery, parallelization, background execution, cron, and auditable resumable execution. The supplied code chunk does not implement those capabilities. It is a simple CLI script that accepts a task ID, loads a corresponding JSON file from a local tasks directory, and prints stored fields such as status, stages, events, errors, and results. While the displayed fields reference workflow concepts like stages, child tasks, and cron job IDs, the script itself only reads and formats existing task data. That is materially different from the declared primary purpose, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The code is specifically a 'task doctor' CLI that inspects task JSON records and filesystem artifact paths for consistency. It validates statuses, dependencies, parent/child links, rollback metadata, notifications, and execution-mode-related fields, then emits findings. While this supports auditability and may be adjacent to a durable workflow system, it does not itself provide the declared primary capability of a durable workflow layer for executing and resuming long-running work. The actual behavior is materially narrower and different in purpose: validation/health checking rather than orchestration/execution.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description suggests a substantial workflow engine/layer responsible for orchestrating long-running work, recovery, parallelism, resumability, and auditable execution. The supplied code chunk does not implement such behavior. Instead, it is a read-only inspection tool for task event history: it parses CLI arguments, loads a single task JSON file, optionally filters events by type and limit, and prints them. While viewing task events may support auditability as a minor supporting detail, the code’s primary purpose is event retrieval/display, not workflow execution or durable coordination. Therefore the code materially underdelivers relative to the declared purpose, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The implemented code does not provide a durable workflow layer or orchestration system. It only loads existing task metadata from local JSON files and renders/export them in various formats. This is an auditing/reporting/visualization utility, not a mechanism for executing multi-stage work, resuming runs, coordinating sub-agents, scheduling background jobs, or managing external side effects. That makes the primary purpose materially different from the declared description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a substantial workflow system for orchestrating durable long-running work. The supplied code chunk does something much narrower: it scans a local tasks folder, parses task JSON files, derives parent-child relationships, filters for open/root tasks, and prints a textual tree. This is a read-only inspection/reporting script, not a workflow layer. While task visualization could support a workflow system, this chunk’s primary purpose is materially different from the declared purpose, so it should be flagged as a mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill instructs the agent to copy bundled assets into the workspace and create runtime directories, which requires file read/write capability, but it declares no explicit tool scope or permissions boundary. This creates a least-privilege and transparency problem: an agent may perform filesystem mutations without users or policy layers being able to evaluate that capability from the manifest.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrase "continue" is excessively generic and is likely to appear in ordinary conversation, which can cause the resume workflow to activate when the user did not intend to resume a durable task. In this skill, unintended invocation is more dangerous because the playbook directs the agent to inspect task state, read logs and outputs, and potentially update checkpoint JSON, which could lead to unintended disclosure or state changes.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The script concatenates an untrusted task ID into a filesystem path and only checks whether that resulting path exists. Because no validation or canonical-path containment check is performed, an attacker can supply path traversal sequences such as '../' to read arbitrary JSON files outside the intended tasks directory, subject to the script's OS permissions. In this skill context, task data includes process/session IDs, cron job IDs, log paths, output directories, recent events, errors, and results, which can expose sensitive operational metadata and make follow-on attacks easier.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The script is presented as an export utility, but when invoked with --write-report it mutates task metadata and writes files to disk. This side effect can surprise callers, break trust boundaries, and enable unintended persistence or state tampering in workflows that expect read-only export behavior.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
write_long_report uses a task-controlled artifacts.outputDir to build report_path and then joins it with ROOT without validating that the final path stays inside an approved directory. An attacker who can influence task JSON could supply absolute paths or traversal segments, causing arbitrary file write outside the intended workspace and potentially overwriting sensitive files accessible to the process.

Vague Triggers

Medium
Confidence
90% confidence
Finding
This markdown file defines activation-style user intents such as "列出未完成任务" and "看下哪个任务卡住了" that could overlap with ordinary conversation about tasks, but it does not clearly state the context in which this recovery guide should or should not activate. There are no exclusion conditions or negative examples to limit invocation to explicit task-recovery scenarios.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The listed user intents are entirely in Chinese, which implies a language-specific invocation pattern. The file does not state that the skill is region-specific or offer equivalent language options, so it may violate language/locale choice policy.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
The task title and goal are written in Chinese, indicating a fixed language choice in user-facing task metadata. In this example JSON, there is no indication that the language is optional, user-selected, or justified as a region-specific requirement, which can violate language/locale policy constraints.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The user-facing title and goal are written only in Chinese, which imposes a language choice without any visible opt-in or justification in the file. Under the locale policy rule, this is a natural-language policy concern because the skill does not offer a language alternative or document that it is intentionally region-specific.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The example invocation hardcodes a Chinese title and goal, which can signal an implicit language expectation in user-facing guidance. Under the language/locale policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the file clearly offers a choice or justifies the locale constraint.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This shell script creates directories, truncates a log file, and writes a new task JSON file, but the code provides no prior warning, confirmation, or comment disclosing that it will modify files on disk. For code files, file writes should be flagged when there is no visible user disclosure and the operation is not clearly documented in-code.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This manifest hard-codes user-facing task metadata in Chinese ("部署服务", related goal text) with no indication that language selection is optional or region-specific. That can violate a language/locale policy when skills are expected to be language-neutral or user-selectable.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The title and goal are written in Chinese, which indicates a fixed language choice in user-facing task content. The file does not provide any indication that the user selected this locale or that the task is region-specific, so it may violate language/locale policy requirements.

Vague Triggers

Low
Confidence
80% confidence
Finding
This JSON manifest-like template describes syncing local changes to a Feishu document, but it does not define any explicit trigger phrases, invocation constraints, or exclusion conditions. For a manifest/plain-text scoped rule, the broad description could allow unintended invocation in contexts where a user mentions syncing or updating a document without intending this specific remote-write workflow.

Vague Triggers

Low
Confidence
78% confidence
Finding
This JSON schema is a manifest-file type, so vague-trigger review applies. The schema defines a very broad, general-purpose task object without any natural-language constraints, scope boundaries, or negative examples indicating when a skill using this schema should or should not be invoked, which can contribute to unintended matching in manifest-driven systems.

Static analysis

No suspicious patterns detected.