mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-18 13:46:02 +00:00
fix(channels): harden runtime credential management APIs (#3581)
* fix(channels): harden runtime credential management APIs * fix(channels): address review feedback on credential hardening Follow-up to the runtime credential-hardening pass, resolving five review findings: - WeChat auth persistence now writes through a 0o600 NamedTemporaryFile + Path.replace instead of write_text-then-chmod, so the iLink bot_token is never briefly readable at umask defaults (mirrors ChannelRuntimeConfigStore). - The post-write chmod is split into its own try/except: a chmod failure on a filesystem without POSIX perms now logs at debug instead of masquerading as a "failed to persist" warning. - Extracted the three near-identical _require_admin_user helpers (mcp, channel_connections, channels) into a single require_admin_user(request, *, detail) in app/gateway/deps.py; each router supplies its own detail string. - Strengthened the runtime-config-store chmod coverage: a new test injects a temp-file chmod failure and asserts it is logged at debug while the destination is still owner-only (mutation-verified to fail if the chmod is dropped), plus a loose-pre-existing-file case. - Removed the unused _FakeRepo from the blocking-io test: its isinstance gate routes through the repo-less 503 path, so neither stub was ever invoked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
@@ -124,10 +124,29 @@ Connection records live in SQL tables under `deerflow.persistence.channel_connec
|
||||
|
||||
Incoming messages that resolve to a connection carry `connection_id`, `owner_user_id`, and `workspace_id`. `ChannelManager` uses `owner_user_id` as the DeerFlow run user id and preserves the raw platform user id as `channel_user_id`.
|
||||
|
||||
Runtime provider credentials are deployment-level bot secrets, not user-owned
|
||||
connection credentials. They can come from `channels.*` in `config.yaml` or
|
||||
from the browser runtime setup flow, which persists them through
|
||||
`ChannelRuntimeConfigStore` so local/private deployments can configure bots
|
||||
without editing YAML. The runtime store is a local plaintext JSON fallback with
|
||||
owner-only file permissions (`0600`); use it only where the DeerFlow data
|
||||
directory is already trusted as secret storage. WeChat QR login auth state
|
||||
follows the same local-runtime model and may persist a QR-derived bot token in
|
||||
the channel state directory.
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Browser APIs remain authenticated and CSRF-protected.
|
||||
- Connect codes are 128-bit random, short-lived, and single-use.
|
||||
- Runtime provider bot tokens are shared deployment secrets. Runtime setup
|
||||
responses mask password fields, and mutating runtime/channel-worker APIs
|
||||
require an admin user.
|
||||
- Stored per-connection credentials use the `channel_credentials` encryption
|
||||
path. If stored credential material cannot be decrypted, DeerFlow treats it
|
||||
as unavailable instead of using corrupt secrets.
|
||||
- The local plaintext runtime credential fallback is documented above; prefer
|
||||
deployment-managed environment/config secrets for non-local deployments until
|
||||
a dedicated secret backend is configured.
|
||||
- `allowed_users` is **not** a bind-time defense. Because connect codes are processed before the allowlist (see Connect Flow), anyone who possesses a valid code can consume it — not only allowlisted users. Bind security therefore rests entirely on the code's confidentiality: it is 128-bit random, expires after 10 minutes, is single-use, and is shown only in the initiating user's browser (never echoed back to chat). Treat connect codes like one-time passwords and do not forward them.
|
||||
- An external identity — `(provider, external account, workspace/team/guild)` — has at most one active owner. The most recent successful bind wins: connecting an identity that another DeerFlow user already holds transfers ownership and revokes the previous owner's binding (and its stored credentials). This is enforced at the database layer, so two users racing to bind the same identity cannot both end up connected.
|
||||
- Provider bot tokens remain in `channels.*` and are never returned to the browser.
|
||||
|
||||
Reference in New Issue
Block a user