T09 · Insecure Skill Coding Practices
Error
- Location
- assets/blueprint/scripts/mailboxctl.py:69
- Finding
- Mailbox Message ID Allows Path Traversal Outside the Mailbox<![CDATA[ ## Vulnerability Details **File Location**: `assets/blueprint/scripts/mailboxctl.py:69-70, 83-104, 184` **Vulnerability Type**: Path traversal and arbitrary file creation **Risk Level**: High ### Vulnerable Code ```python def message_path(message_id): return os.path.join(MAILBOX, f"{message_id}.md") def cmd_send(a): ensure_dirs() mid = a.message_id or next_msg_id() path = message_path(mid) if os.path.exists(path): raise SystemExit(f'message exists: {mid}') header = { 'message_id': mid, 'correlation_id': a.correlation_id, 'task_id': a.task_id, 'sender': a.sender, 'receiver': a.receiver, 'version': '1.0', 'timestamp': now_iso(), 'status': 'UNREAD', 'retry_count': int(a.retry_count), 'checksum': '' } body = f"# Message\n\n{a.body}\n" header['checksum'] = checksum_for(header, body) with open(path, 'w', encoding='utf-8') as f: f.write(render_message(header, body)) ``` ```python s.add_argument('--message-id') ``` ### Technical Analysis The caller-controlled `--message-id` value is interpolated directly into a filesystem path. The code neither restricts message IDs to a safe identifier format nor verifies that the normalized destination remains inside `MAILBOX`. A message ID containing directory traversal components such as `../` causes `os.path.join()` and `open()` to address a path outside the intended mailbox. The `.md` suffix limits the resulting filename extension but does not prevent writing outside the mailbox. The same `message_path()` function is also used by the `show` and `status` commands. Consequently, an existing external `.md` file that follows the expected message format can potentially be read or rewritten through those commands. The existence check followed by a normal `open(..., 'w')` is also vulnerable to a time-of-check/time-of-use race and does not protect against symlink redirection. ### Atta ...[truncated 1301 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Enforce a strict message-ID allowlist, such as `^MSG-[0-9]{4,}$`. - Reject absolute paths, path separators, `.` components, and `..` components. - Resolve the mailbox and destination with `os.path.realpath()` or `pathlib.Path.resolve()`, then verify that the destination is a direct child of the mailbox. - Use `os.open()` with `O_CREAT | O_EXCL` for atomic creation. - Reject symlinks or use platform-supported no-follow options such as `O_NOFOLLOW`. - Apply the same validation to `send`, `show`, and `status`. Example validation: ```python MESSAGE_ID_RE = re.compile(r'^MSG-[0-9]{4,}$') def message_path(message_id): if not MESSAGE_ID_RE.fullmatch(message_id): raise SystemExit('invalid message ID') mailbox = os.path.realpath(MAILBOX) path = os.path.realpath(os.path.join(mailbox, f'{message_id}.md')) if os.path.dirname(path) != mailbox: raise SystemExit('message path escapes mailbox') return path ``` ]]>
