← Back to PRs

#18170: feat(telegram): support local Bot API server via `apiRoot` config

by iemesowum open 2026-02-16 15:31 View on GitHub →
channel: telegram size: L
Add a per-account `apiRoot` option so all Telegram HTTP calls route through a self-hosted Bot API server instead of the default `https://api.telegram.org`/. Key changes: - New `src/telegram/api-base.ts` module: `getTelegramApiBase()` resolves the base URL (explicit config override > default), `isCustomTelegramApi()` and `isLocalBotApiFilePath()` helpers for downstream branching, `validateLocalFilePath()` for path-traversal protection. - Config: `apiRoot` and `localApiDataDir` fields added to `TelegramAccountConfig` type and Zod schema (`apiRoot` validated as URL). - Bot, send, audit, and probe modules all thread the per-account `apiRoot` config through to the grammy API client and `getTelegramApiBase()`. `bot.ts` and `send.ts` use `isCustomTelegramApi()` for custom-API detection. - File downloads (voice notes, media): when a local Bot API server returns an absolute disk path in `file_path`, the path is validated against the required `localApiDataDir` config using `realpath` to block symlink and `..` traversal attacks, then read directly from disk. - SSRF relaxation is scoped to accounts that explicitly set `apiRoot` in config (not env vars); accounts without `apiRoot` always use the default cloud endpoint with full SSRF protection. Security: - Disk reads require `localApiDataDir` to be explicitly configured — there is no default. Without it, absolute file paths returned by the local server are rejected. - `realpath` resolves symlinks before the prefix check, preventing symlink-based escapes. - A compromised local Bot API server cannot trick the gateway into reading arbitrary files (e.g. /etc/shadow, ~/.ssh/id_rsa). Tests: - `api-base.test.ts`: covers URL resolution priority, normalization, `isCustomTelegramApi`, `isLocalBotApiFilePath`, and `validateLocalFilePath` (happy path, `..` traversal, symlink escape, missing file, missing config). - `delivery.resolve-media-local-api.test.ts`: covers direct disk read for absolute paths, path-traversal rejection, HTTP with relaxed SSRF for relative paths, and verifies SSRF stays strict when `apiRoot` is not set in config. Tested with a local Bot API server; confirmed voice-note file downloads work end-to-end via both the direct disk-read and HTTP paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> ## Summary Describe the problem and fix in 2–5 bullets: - Problem: Local bot API server not supported - Why it matters: Adding support for this increases privacy significantly, and allows enhanced telegram integration. - What changed: Integrated local bot api server use. SSRF relaxation on for telegram allowing SCOPED local disk access. - What did NOT change (scope boundary): Other messaging frameworks. ## Change Type (select all) - [ ] Bug fix - [X] Feature - [ ] Refactor - [ ] Docs - [X] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [ ] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [X] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - Closes # - Related # ## User-visible / Behavior Changes List user-visible changes (including defaults/config). config: apiRoot, and localApiDataDir (required for local disk access). If none, write `None`. ## Security Impact (required) - New permissions/capabilities? (`Yes`) - Secrets/tokens handling changed? (`Yes/No`) - New/changed network calls? (`Yes`) - Command/tool execution surface changed? (`Yes/No`) - Data access scope changed? (`Yes`) - If any `Yes`, explain risk + mitigation: Risk: more disk access triggered by bot api server, mitigation: only allow a configured api data dir, mandatory config, otherwise all disk read fails. ## Repro + Verification ### Environment - OS: Gentoo - Runtime/container: Local - Model/provider: Claude Opus 4.6 - Integration/channel (if any): Telegram - Relevant config (redacted): ### Steps 1. 2. 3. ### Expected - ### Actual - ## Evidence Attach at least one: - [ ] Failing test/log before + passing after - [ ] Trace/log snippets - [ ] Screenshot/recording - [ ] Perf numbers (if relevant) ## Human Verification (required) What you personally verified (not just CI), and how: I personally verified voice note receipt from Telegram in a direct message with my bot. - Verified scenarios: - Edge cases checked: Removed api data dir config, and confirmed local disk access blocked. - What you did **not** verify: download of other media types (document, file, etc.). ## Compatibility / Migration - Backward compatible? (`Yes`) - Config/env changes? (`Yes`) - Migration needed? (`No`) - If yes, exact upgrade steps: ## Failure Recovery (if this breaks) - How to disable/revert this change quickly: - Files/config to restore: openclaw.json - Known bad symptoms reviewers should watch for: ## Risks and Mitigations List only real risks for this PR. Add/remove entries as needed. If none, write `None`. - Risk: SSRF relaxation for Telegram only, per account, not globally - Mitigation: Config check, and api data dir scoping. <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds support for self-hosted local Telegram Bot API servers via per-account `apiRoot` configuration, enabling direct disk reads for media files with comprehensive path-traversal protection. **Major changes:** - New `src/telegram/api-base.ts` module with URL resolution, custom API detection, and `validateLocalFilePath()` using `realpath()` to prevent symlink/`..` escapes - Config fields `apiRoot` (validated as URL) and `localApiDataDir` added to `TelegramAccountConfig` - SSRF relaxation scoped to accounts with explicit `apiRoot` config (not env vars) - Direct disk reads require `localApiDataDir` to be configured (no default); root directory explicitly blocked - Bot, send, audit, and probe modules thread per-account `apiRoot` through to API clients - Comprehensive test coverage: `api-base.test.ts` (path validation, Windows paths, root blocking) and `delivery.resolve-media-local-api.test.ts` (disk reads, HTTP fallback, SSRF scoping) **Security measures verified:** - `realpath()` resolves symlinks before prefix checks - Root directory (`/`) rejected as `localApiDataDir` - Path-traversal with `..` blocked - Windows absolute paths (`C:\...`) properly detected via `nodePath.win32.isAbsolute()` - Audit and probe correctly receive per-account `apiRoot` config <h3>Confidence Score: 4/5</h3> - Safe to merge with minor considerations - security mitigations are comprehensive and well-tested - All previously identified security issues have been addressed (Windows paths, root directory bypass, per-account config threading). Path validation is robust with `realpath()` + prefix checks. Test coverage is comprehensive. Score not 5 due to complexity of security-sensitive disk access feature and SSRF relaxation, though both are properly scoped and protected. - No files require special attention - all previous review comments have been addressed <sub>Last reviewed commit: cda1cee</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs