diff --git a/gateway/platforms/webhook.py b/gateway/platforms/webhook.py index 115b22d196f..93017de4e2b 100644 --- a/gateway/platforms/webhook.py +++ b/gateway/platforms/webhook.py @@ -326,6 +326,17 @@ class WebhookAdapter(BasePlatformAdapter): _INSECURE_NO_AUTH, ) continue + if ( + effective_secret == _INSECURE_NO_AUTH + and not _is_loopback_host(self._host) + ): + logger.warning( + "[webhook] Dynamic route '%s' skipped: INSECURE_NO_AUTH " + "is only allowed on loopback hosts. Current host: '%s'.", + k, + self._host, + ) + continue new_dynamic[k] = v self._dynamic_routes = new_dynamic self._routes = {**self._dynamic_routes, **self._static_routes} diff --git a/tests/gateway/test_webhook_dynamic_routes.py b/tests/gateway/test_webhook_dynamic_routes.py index c185a6eb15e..98c0db26492 100644 --- a/tests/gateway/test_webhook_dynamic_routes.py +++ b/tests/gateway/test_webhook_dynamic_routes.py @@ -138,10 +138,20 @@ class TestDynamicRouteSecretValidation: (tmp_path / _DYNAMIC_ROUTES_FILENAME).write_text( json.dumps({"test": {"secret": _INSECURE_NO_AUTH, "prompt": "p"}}) ) - adapter = _make_adapter() + adapter = _make_adapter(extra={"host": "127.0.0.1"}) adapter._reload_dynamic_routes() assert "test" in adapter._routes + def test_insecure_no_auth_rejected_on_non_loopback_bind(self, tmp_path): + # Dynamic INSECURE_NO_AUTH routes are only valid on loopback hosts. + (tmp_path / _DYNAMIC_ROUTES_FILENAME).write_text( + json.dumps({"pub": {"secret": _INSECURE_NO_AUTH, "prompt": "p"}}) + ) + adapter = _make_adapter(extra={"host": "0.0.0.0"}) + adapter._reload_dynamic_routes() + assert "pub" not in adapter._routes + assert "pub" not in adapter._dynamic_routes + def test_warning_logged_on_skip(self, tmp_path, caplog): import logging (tmp_path / _DYNAMIC_ROUTES_FILENAME).write_text(