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. ]]>
