From 6edbe0452eefe02cc821f5b1be32abb8e8c7a4e913ccbb15b7e89e09572d5836 Mon Sep 17 00:00:00 2001 From: larssand Date: Mon, 6 Jul 2026 10:21:18 +0200 Subject: [PATCH] fix --- src/fgai/dashboard.py | 3 ++- src/fgai/monitor.py | 26 +++++++++++++++++++++++--- tests/test_monitor.py | 4 +++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/fgai/dashboard.py b/src/fgai/dashboard.py index ea67d43..e73e341 100644 --- a/src/fgai/dashboard.py +++ b/src/fgai/dashboard.py @@ -555,7 +555,8 @@ async function refresh() { const cache = data.status_cache || {}; const streamCoverage = data.stream_coverage || []; const context = data.event_context || {}; - const enabledStreams = streamCoverage.filter(item => item.enabled); + const enabledConfigStreams = (configuration.graylog_streams || []).filter(item => item.enabled); + const enabledStreams = streamCoverage.some(item => item.enabled) ? streamCoverage.filter(item => item.enabled) : enabledConfigStreams; const streamsMissingProfile = enabledStreams.filter(item => !item.profile_ready).length; const enabledWithNoRawEvents = enabledStreams.filter(item => Number(item.events_fetched || 0) === 0); const displayedAggregateEvents = mcp.status === 'refreshing' ? Number(mcp.previous_aggregate_events || 0) : Number(mcp.aggregate_events || 0); diff --git a/src/fgai/monitor.py b/src/fgai/monitor.py index 8e05c13..83528e3 100644 --- a/src/fgai/monitor.py +++ b/src/fgai/monitor.py @@ -36,6 +36,23 @@ def _stream_titles(config: dict[str, object]) -> dict[str, str]: } +def _public_runtime_config(config: dict[str, object]) -> dict[str, object]: + public = { + key: value + for key, value in config.items() + if key not in {"graylog_mcp_token", "abuseipdb_api_key", "virustotal_api_key"} + } + public["graylog_mcp_token_configured"] = bool(config.get("graylog_mcp_token")) + public["abuseipdb_api_key_configured"] = bool(config.get("abuseipdb_api_key")) + public["virustotal_api_key_configured"] = bool(config.get("virustotal_api_key")) + public["enabled_streams"] = sum( + 1 + for item in config.get("graylog_streams", []) + if isinstance(item, dict) and item.get("enabled") + ) + return public + + def _stream_name(stream_id: str, stream_titles: dict[str, str], profile: object | None = None) -> str: return stream_titles.get(stream_id) or getattr(profile, "name", "") or stream_id @@ -136,7 +153,7 @@ def build_status( config_store = ConfigStore(config_path) if config_path else None config_exists = bool(config_store and config_store.path.exists()) runtime_values = config_store.read() if config_exists and config_store else {} - runtime_config = config_store.public() if config_store else {} + runtime_config = _public_runtime_config(runtime_values) if config_store else {} stream_profiles = parse_profiles(runtime_values.get("graylog_stream_profiles", [])) stream_titles = _stream_titles(runtime_values) events = read_events(log_path) if Path(log_path).exists() else [] @@ -517,7 +534,7 @@ def write_status(status: dict[str, object], output: str) -> None: tmp_path.replace(output_path) -def write_refreshing_status(output: str, *, cache_path: str | None = None, call_timeout_seconds: int = 0, poll_timeout_seconds: int = 0) -> None: +def write_refreshing_status(output: str, *, cache_path: str | None = None, call_timeout_seconds: int = 0, poll_timeout_seconds: int = 0, runtime_values: dict[str, object] | None = None) -> None: output_path = Path(output) now = int(time.time()) try: @@ -538,6 +555,9 @@ def write_refreshing_status(output: str, *, cache_path: str | None = None, call_ current["generated_at"] = now current["stale"] = False current["stale_reason"] = "" + if runtime_values is not None: + current["configuration"] = _public_runtime_config(runtime_values) + current["stream_coverage"] = _stream_coverage(runtime_values, {}, {"streams": []}, [], _stream_titles(runtime_values)) capabilities = current.setdefault("capabilities", {}) if isinstance(capabilities, dict): previous_events = previous_mcp.get("events_fetched", 0) if isinstance(previous_mcp, dict) else 0 @@ -595,7 +615,7 @@ def monitor_loop( mcp_call_timeout = max(1, int(runtime.get("graylog_mcp_call_timeout_seconds", 8) or 8)) mcp_poll_timeout = max(60, int(runtime.get("graylog_mcp_poll_timeout_seconds", 120) or 120)) if runtime.get("log_source") == "graylog_mcp": - write_refreshing_status(output, cache_path=status_cache_path, call_timeout_seconds=mcp_call_timeout, poll_timeout_seconds=mcp_poll_timeout) + write_refreshing_status(output, cache_path=status_cache_path, call_timeout_seconds=mcp_call_timeout, poll_timeout_seconds=mcp_poll_timeout, runtime_values=runtime) try: status = build_status( log_path, policy_path=policy_path, anomaly_limit=anomaly_limit, diff --git a/tests/test_monitor.py b/tests/test_monitor.py index eae9b28..77dd8d5 100644 --- a/tests/test_monitor.py +++ b/tests/test_monitor.py @@ -55,7 +55,7 @@ class MonitorTests(unittest.TestCase): encoding="utf-8", ) - write_refreshing_status(str(output), call_timeout_seconds=8, poll_timeout_seconds=120) + write_refreshing_status(str(output), call_timeout_seconds=8, poll_timeout_seconds=120, runtime_values={"log_source": "graylog_mcp", "graylog_streams": [{"id": "fw", "title": "Firewall", "enabled": True}]}) status = json.loads(output.read_text(encoding="utf-8")) self.assertFalse(status["stale"]) @@ -68,6 +68,8 @@ class MonitorTests(unittest.TestCase): self.assertEqual(status["capabilities"]["graylog_mcp"]["previous_fetch_mode"], "aggregate") self.assertEqual(status["capabilities"]["graylog_mcp"]["call_timeout_seconds"], 8) self.assertEqual(status["capabilities"]["graylog_mcp"]["poll_timeout_seconds"], 120) + self.assertEqual(status["configuration"]["enabled_streams"], 1) + self.assertEqual(status["stream_coverage"][0]["stream_name"], "Firewall") self.assertFalse(status["status_cache"]["served_from_cache"]) def test_write_refreshing_status_uses_last_good_cache(self):