mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-28 06:00:38 +00:00
* feat(plugins): add redacted administrator diagnostics * ci: retrigger plugin diagnostics checks
192 lines
8.6 KiB
Python
192 lines
8.6 KiB
Python
"""Administrator-only, read-only projections of the live extension diagnostics."""
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
from fastapi import FastAPI, HTTPException
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.gateway.auth_middleware import AuthMiddleware
|
|
from app.gateway.internal_auth import create_internal_auth_headers
|
|
from app.gateway.routers.plugins import router
|
|
from deerflow.config.authorization_config import AuthorizationConfig
|
|
from deerflow.extensions import initialize_runtime_diagnostics, record_runtime_diagnostics
|
|
from deerflow.extensions.loader import Diagnostic, ExtensionSpec, load_extensions
|
|
|
|
URL = "/api/plugins/diagnostics"
|
|
SECRET = "fixture-private-key"
|
|
|
|
|
|
@pytest.fixture
|
|
def diagnostics_client(monkeypatch):
|
|
monkeypatch.setenv("DEER_FLOW_AUTH_DISABLED", "")
|
|
monkeypatch.setattr("app.gateway.authz._get_route_authorization_config", lambda: AuthorizationConfig())
|
|
monkeypatch.setattr("deerflow.extensions._runtime_diagnostics", [])
|
|
users = {role: SimpleNamespace(id=role, system_role=role, needs_setup=False, token_version=0) for role in ("admin", "user")}
|
|
|
|
async def resolve_session(request):
|
|
user = users.get(request.cookies.get("access_token"))
|
|
if user is None:
|
|
raise HTTPException(401, "Not authenticated")
|
|
return user
|
|
|
|
monkeypatch.setattr("app.gateway.deps.get_current_user_from_request", resolve_session)
|
|
app = FastAPI()
|
|
app.state.extension_diagnostics = initialize_runtime_diagnostics([])
|
|
app.include_router(router)
|
|
app.add_middleware(AuthMiddleware)
|
|
with TestClient(app) as client:
|
|
yield client, users
|
|
|
|
|
|
def admin(client):
|
|
client.cookies.set("access_token", "admin")
|
|
|
|
|
|
def test_admin_reads_live_diagnostics_without_exposing_or_changing_details(diagnostics_client):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
diagnostics = client.app.state.extension_diagnostics
|
|
first = Diagnostic.error("example:install", f"install() failed: api_key={SECRET}; /private/config.yaml")
|
|
record_runtime_diagnostics([first])
|
|
response = client.get(URL)
|
|
assert response.status_code == 200
|
|
assert response.headers["cache-control"] == "private, no-store"
|
|
assert response.json() == [{"source": "example:install", "level": "error", "summary": "Extension registration failed. See Gateway logs for details."}]
|
|
assert SECRET not in response.text and "/private/config.yaml" not in response.text
|
|
assert diagnostics == [first]
|
|
record_runtime_diagnostics([Diagnostic.warning("other.plugin:install", f"service start() failed; continuing without it: {SECRET}")])
|
|
updated = client.get(URL)
|
|
assert updated.status_code == 200 and len(updated.json()) == 2
|
|
assert updated.json()[1]["summary"] == "Extension service could not start. See Gateway logs for details."
|
|
assert SECRET not in updated.text
|
|
assert client.app.state.extension_diagnostics is diagnostics
|
|
assert diagnostics[0] is first and SECRET in diagnostics[0].message
|
|
|
|
|
|
def test_empty_diagnostics_are_an_empty_array(diagnostics_client):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
assert client.get(URL).json() == []
|
|
|
|
|
|
def test_failed_extension_is_visible_even_without_a_registered_plugin(diagnostics_client):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
loaded, diagnostics = load_extensions([ExtensionSpec(use="nonexistent_diagnostics_fixture:install")])
|
|
assert not loaded.plugins
|
|
record_runtime_diagnostics(diagnostics)
|
|
response = client.get(URL)
|
|
assert response.status_code == 200
|
|
assert response.json() == [{"source": "nonexistent_diagnostics_fixture:install", "level": "error", "summary": "Extension loading failed. See Gateway logs for details."}]
|
|
|
|
|
|
@pytest.mark.parametrize("role", [None, "user", "internal"])
|
|
def test_non_admin_cannot_read_even_the_diagnostics_list(diagnostics_client, monkeypatch, role):
|
|
client, _ = diagnostics_client
|
|
|
|
def unreadable():
|
|
pytest.fail("Diagnostics were read before administrator authorization")
|
|
|
|
monkeypatch.setattr("app.gateway.routers.plugins.get_runtime_diagnostics", unreadable, raising=False)
|
|
headers = {}
|
|
if role == "internal":
|
|
headers = create_internal_auth_headers()
|
|
elif role:
|
|
client.cookies.set("access_token", role)
|
|
response = client.get(URL, headers=headers)
|
|
assert response.status_code == (401 if role is None else 403)
|
|
|
|
|
|
@pytest.mark.parametrize("allow_route", [False, True])
|
|
def test_admin_pat_has_no_diagnostic_access(diagnostics_client, monkeypatch, allow_route):
|
|
client, users = diagnostics_client
|
|
monkeypatch.setattr("app.gateway.auth.pat.authenticate_pat", AsyncMock(return_value=(users["admin"], ["threads:read"])))
|
|
if allow_route:
|
|
# Pin the route's own admin guard even if an alternative host admits PATs.
|
|
monkeypatch.setattr("app.gateway.auth.pat.is_pat_allowed_route", lambda method, path: True)
|
|
response = client.get(URL, headers={"Authorization": "Bearer fixture-token"})
|
|
assert response.status_code == 403
|
|
|
|
|
|
@pytest.mark.parametrize("method", ["POST", "PUT", "PATCH", "DELETE"])
|
|
def test_diagnostics_have_no_write_operation(diagnostics_client, method):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
diagnostic = Diagnostic.error("example:install", SECRET)
|
|
record_runtime_diagnostics([diagnostic])
|
|
assert client.request(method, URL, json={"clear": True}).status_code == 405
|
|
assert client.app.state.extension_diagnostics == [diagnostic]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"source",
|
|
[f"/private/{SECRET}.py", f"https://user:{SECRET}@example.com/plugin", f"example:install\n{SECRET}", f"{SECRET}" * 100, ""],
|
|
ids=["path", "credentials", "newline", "oversized", "empty"],
|
|
)
|
|
def test_invalid_or_oversized_sources_are_redacted(diagnostics_client, source):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
record_runtime_diagnostics([Diagnostic.error(source, f"arbitrary failure: {SECRET}")])
|
|
response = client.get(URL)
|
|
assert response.status_code == 200
|
|
assert response.json() == [{"source": "[redacted]", "level": "error", "summary": "Extension reported a diagnostic. See Gateway logs for details."}]
|
|
assert SECRET not in response.text
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("message", "summary"),
|
|
[
|
|
("extension requires extension-api private-version", "Extension loading failed."),
|
|
("install() failed: credential", "Extension registration failed."),
|
|
("router path /private is already served", "Extension router could not be mounted."),
|
|
("service start() raised CancelledError; continuing without it", "Extension service could not start."),
|
|
("service stop() timed out after 5s; continuing shutdown", "Extension service could not stop."),
|
|
("PrivateMiddleware.before_model failed and was skipped", "Extension reported a diagnostic."),
|
|
],
|
|
)
|
|
def test_only_fixed_summaries_are_returned(diagnostics_client, message, summary):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
record_runtime_diagnostics([Diagnostic.warning("example:install", f"{message}: {SECRET}")])
|
|
response = client.get(URL)
|
|
assert response.status_code == 200
|
|
assert response.json()[0] == {"source": "example:install", "level": "warning", "summary": f"{summary} See Gateway logs for details."}
|
|
assert SECRET not in response.text
|
|
|
|
|
|
def test_existing_retention_bound_is_preserved_without_draining_the_list(diagnostics_client):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
records = [Diagnostic.error(f"example_{index}:install", SECRET) for index in range(1005)]
|
|
record_runtime_diagnostics(records)
|
|
response = client.get(URL)
|
|
assert response.status_code == 200
|
|
assert len(response.json()) == 1000
|
|
assert response.json()[0]["source"] == "example_5:install"
|
|
assert response.json()[-1]["source"] == "example_1004:install"
|
|
assert client.app.state.extension_diagnostics == records[-1000:]
|
|
assert client.get(URL).json() == response.json()
|
|
assert SECRET not in response.text
|
|
|
|
|
|
def test_unknown_level_is_not_returned_as_free_form_text(diagnostics_client):
|
|
client, _ = diagnostics_client
|
|
admin(client)
|
|
record_runtime_diagnostics([Diagnostic(SECRET, "example:install", SECRET)])
|
|
response = client.get(URL)
|
|
assert response.status_code == 200
|
|
assert response.json()[0]["level"] == "unknown"
|
|
assert SECRET not in response.text
|
|
|
|
|
|
def test_auth_disabled_mode_keeps_the_existing_synthetic_admin_policy(diagnostics_client, monkeypatch):
|
|
client, _ = diagnostics_client
|
|
monkeypatch.setenv("DEER_FLOW_AUTH_DISABLED", "1")
|
|
monkeypatch.setenv("DEER_FLOW_ENV", "test")
|
|
monkeypatch.setenv("ENVIRONMENT", "test")
|
|
response = client.get(URL)
|
|
assert response.status_code == 200
|
|
assert response.json() == []
|