mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-23 08:55:44 +08:00
* fix(adapters): stop SSRF and arbitrary local file reads in media handling The platform adapters fetch URLs and open paths that come from inbound messages, so both are attacker-influenced. New guard module pkg/utils/outbound_url.py: - assert_safe_outbound_url: checks the scheme, rejects literal private / loopback / link-local / reserved / multicast addresses, and resolves the hostname so a public-looking name pointing at an internal address is refused too. Optional allowed_domains confines a fetch to a known prefix. - assert_safe_local_media_path: resolves a caller-supplied path and requires it to stay inside the media roots (data dir + temp dir), so symlinks and .. cannot escape. Directories are refused. Wired in: - DingTalk send_message: session_webhook is taken from the inbound event and was POSTed verbatim, so a forged event could redirect reply content (including LLM output) to any host. Now restricted to dingtalk.com. - DingTalk send_attachment: component.url was fetched with no destination check; now goes through the SSRF guard, which also covers cloud metadata. - DingTalk upload_image_media: caller-supplied file_path was opened directly; now confined to the media roots. - Slack attachment_bytes: component.path was opened directly and uploaded to the channel (arbitrary file exfiltration); now confined to the media roots. - Slack download_media: redirects were followed with no destination check. Every hop is now validated, the hop count is bounded, and a redirect off the Slack domain drops the bearer token. Tests: 25 new cases for the guard (internal address forms, scheme rejection, suffix impostor, traversal, symlink escape, directory). The Slack credential test now stubs DNS because the guard resolves before fetching, and asserts allow_redirects is False for every case since redirects are handled manually. Co-authored-by: multica-agent <[email protected]> * fix(web): hide passkey UI unless the core reports it is supported The core refuses every passkey route on a cloud deployment (403 edition_unsupported) and reports passkey_login_enabled/passkey_supported as false. The WebUI still rendered the controls: - Login page read 'passkey_login_enabled !== false || passkey_supported', so an absent flag counted as enabled. It now requires an explicit true. - The account settings panel always rendered the passkey section and called GET /passkeys on open, which 403s on cloud. It now reads the capabilities first, skips the list call when unsupported, and does not render the section. Also drop the client-supplied origin from both passkey options requests. The server resolves the WebAuthn RP ID and expected origin from api.webui_url / api.webhook_prefix, so sending window.location.origin was dead weight that would invite a future handler to trust it again. Verified: tsc --noEmit clean, eslint clean (only two pre-existing hook-dep warnings), pnpm build succeeds. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: multica-agent <[email protected]>
Debug LangBot Frontend
Please refer to the Development Guide for more information.
Tests
Run the frontend smoke tests without a backend process:
pnpm test:e2e
The Playwright suite starts Vite and mocks the LangBot backend and Space APIs.