# Patch Analysis: CVE-2026-52830 (fast-mcp-telegram path-traversal session bypass)

## Affected versions tested
- Vulnerable: `fast-mcp-telegram <= 0.19.0` (tested `0.19.0`)
- Fixed: `fast-mcp-telegram 0.19.1`

## What the fix changes

Version `0.19.1` introduces a new module `src/server_components/session_token_validation.py` and replaces every ad-hoc session-path construction with a centralized validator. The key changes are:

1. **Strict token whitelist.** `validate_session_token()` enforces:
   ```python
   BEARER_TOKEN_RE = re.compile(r"^[A-Za-z0-9_-]{43}$")
   ```
   Only 43-character URL-safe base64 strings are accepted. All path separators, dots, and reserved session names are rejected.

2. **Resolved-path containment.** `session_file_path()` builds the candidate path, resolves it, and checks that it stays inside the configured session directory using `is_relative_to()`.

3. **Propagation to every token-bearing code path.** All call sites that previously did `session_dir / f"{token}.session"` now call either `validate_session_token()` or `session_file_path()`:
   - `src/server_components/session_token_verifier.py` (FastMCP `TokenVerifier`)
   - `src/server_components/auth_middleware.py` (URL token middleware)
   - `src/server_components/auth.py` (`_extract_bearer_token_from_headers`, `extract_bearer_token_from_request`)
   - `src/client/connection.py` (`_get_client_by_token`, cleanup paths)
   - `src/server_components/web_setup.py` (setup, reauthorize, delete)

4. **Reserved-name check is now centralized.** The original `RESERVED_SESSION_NAMES` set moved into `session_token_validation.py` and is applied as part of the regex/size validation, which also handles case-insensitive matches.

## Fix assumptions

The patch assumes that:
- Bearer tokens are always generated by `generate_bearer_token()` and therefore are 43-character URL-safe base64 strings.
- Any token that does not match the whitelist is attacker-controlled and must be rejected before filesystem access.
- Resolving the constructed path and checking `is_relative_to(session_dir)` is sufficient to prevent escape from the session directory.
- All entry points that consume a token go through the new validator.

## Code paths / inputs the fix does NOT cover

- **Non-HTTP transports.** STDIO and `HTTP_NO_AUTH` modes intentionally disable bearer-token auth; the fix does not change that behavior. This is by design, not a gap.
- **Filesystem-level tricks outside the token.** An attacker who can already create a symlink or hard link inside the session directory could point a *valid* token-named file at another session file. The fix resolves symlinks, so such a link would be rejected if it leaves the session directory. A hard link inside the directory, however, would resolve to a path still inside the directory and would be accepted. Creating that link requires filesystem access, which is outside the HTTP trust boundary.
- **Case-insensitive filesystem aliases.** A reserved name like `TELEGRAM` is not the same as `telegram` after validation; on a case-insensitive filesystem, the underlying file may be the same, but the token still has to pass the 43-character whitelist, so a short reserved name cannot be used.
- **Server-side configuration injection.** The fix does not protect against an attacker who can change `SESSION_DIR` or `TELEGRAM_SESSION_NAME` environment variables; those are administrator-controlled.
- **Windows backslash traversal in the URL-token path.** The URL token regex `([^/]+)` allows `\` as a literal character. On Windows, a token like `.\telegram` could be interpreted as a relative path. The fix’s whitelist rejects `\` and `.`, so this is blocked.

## Comparison of behavior before and after the fix

| Input | 0.19.0 `SessionFileTokenVerifier` | 0.19.1 `session_file_path` |
|-------|-----------------------------------|----------------------------|
| `telegram` | Rejected (reserved name) | Rejected (format) |
| `../fast-mcp-telegram/telegram` | Accepted, resolves to `telegram.session` | Rejected (format) |
| `./telegram` | Accepted, resolves to `telegram.session` | Rejected (format) |
| `fast-mcp-telegram/../telegram` | Accepted, resolves to `telegram.session` | Rejected (format) |
| `invalid-token` | Rejected (file not found) | Rejected (format) |
| valid 43-char token | Accepted if file exists | Accepted if file exists and path stays under session dir |

## Is the fix complete?

For the remote HTTP bearer-token surface, **yes**. The whitelist approach removes the entire class of path-separator and relative-directory attacks, and the containment check is a defense-in-depth guard. Every remote entry point that uses the token to build a session path (`/v1/mcp`, `/v1/url_auth/...`, `/mtproto-api/...`, web setup routes) was updated to use the same validator.

No true bypass of the fixed `0.19.1` version was found in this variant analysis. The alternate traversal aliases (`./telegram`, `fast-mcp-telegram/../telegram`, etc.) that bypass the vulnerable version’s reserved-name check are all rejected by the `0.19.1` whitelist.

## Threat-model note

The upstream GitHub repository was not reachable in this environment, so the target’s `SECURITY.md` could not be retrieved directly. Based on the package metadata and the design of the fix, the maintainers treat the bearer token as a secret credential and the session directory as a security boundary. The fix is consistent with that model: any token that is not a cryptographically generated bearer token is rejected before it can influence filesystem access.
