mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-21 10:15:47 +00:00
fix(channels): validate channel provider before resolving its config (#4100)
`_provider_config` resolved a request-supplied provider name with an
unallowlisted `getattr(config, provider, None)`. `ChannelConnectionsConfig`
carries non-provider attributes -- the `enabled` and `require_bound_identity`
bool fields plus the `provider_status` method -- so a name matching one of
them returned that attribute instead of falling through to the intended 404.
Callers (e.g. `POST /api/channels/{provider}/connect`, reachable by any
authenticated user) then dereferenced the bool/method as a provider config,
crashing with `AttributeError` -> HTTP 500.
Validate `provider` against the `_PROVIDER_META` allowlist before the lookup,
matching how `_credential_fields` / `_connect_instruction` / `_connect_url`
already gate provider names, so unknown providers get a clean 404.
Add a parametrized router regression test covering `enabled`,
`require_bound_identity`, `provider_status`, and an unknown name.
This commit is contained in:
@@ -201,6 +201,13 @@ def _get_repository(request: Request, config: ChannelConnectionsConfig) -> Chann
|
||||
|
||||
|
||||
def _provider_config(config: ChannelConnectionsConfig, provider: str):
|
||||
# Resolve provider configs only for known providers. An unrestricted
|
||||
# getattr would let a request-supplied name that happens to match another
|
||||
# config attribute (e.g. the "enabled" / "require_bound_identity" bool
|
||||
# fields) slip past the 404 and return a non-provider value, which callers
|
||||
# then dereference as a provider config (AttributeError -> HTTP 500).
|
||||
if provider not in _PROVIDER_META:
|
||||
raise HTTPException(status_code=404, detail="Unknown channel provider")
|
||||
provider_config = getattr(config, provider, None)
|
||||
if provider_config is None:
|
||||
raise HTTPException(status_code=404, detail="Unknown channel provider")
|
||||
|
||||
@@ -598,6 +598,29 @@ def test_connect_unconfigured_runtime_channel_returns_400(tmp_path):
|
||||
anyio.run(repo.close)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("provider", ["enabled", "require_bound_identity", "provider_status", "unknown_provider"])
|
||||
def test_connect_rejects_non_provider_config_attribute_with_404(tmp_path, provider):
|
||||
import anyio
|
||||
|
||||
# A request-supplied provider name that collides with a real (non-provider)
|
||||
# ChannelConnectionsConfig attribute -- e.g. the "enabled" /
|
||||
# "require_bound_identity" bool fields, or the "provider_status" method --
|
||||
# must resolve to the intended 404. Before the allowlist check, an
|
||||
# unrestricted getattr returned that attribute instead of falling through to
|
||||
# the 404, and the connect handler then dereferenced it as a provider config
|
||||
# (AttributeError -> HTTP 500) for any authenticated user.
|
||||
repo = anyio.run(_make_repo, tmp_path)
|
||||
app = _make_app(_enabled_connections_config(), repo, _channels_config())
|
||||
|
||||
with TestClient(app, raise_server_exceptions=False) as client:
|
||||
response = client.post(f"/api/channels/{provider}/connect")
|
||||
|
||||
assert response.status_code == 404
|
||||
assert response.json()["detail"] == "Unknown channel provider"
|
||||
|
||||
anyio.run(repo.close)
|
||||
|
||||
|
||||
def test_configure_provider_runtime_credentials_enables_connect_without_file_edits(tmp_path):
|
||||
import anyio
|
||||
|
||||
|
||||
Reference in New Issue
Block a user