Added per-profile baseline readiness.
This commit is contained in:
@@ -182,6 +182,16 @@ class BaselineStore:
|
|||||||
output[entity].append(evidence)
|
output[entity].append(evidence)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def profile_readiness(self, profiles: dict[str, object]) -> list[dict[str, object]]:
|
||||||
|
rows: list[dict[str, object]] = []
|
||||||
|
with self._connect() as connection:
|
||||||
|
for stream_id, profile in profiles.items():
|
||||||
|
fields = [*getattr(profile, "categorical_fields", ()), *getattr(profile, "numeric_fields", ())]
|
||||||
|
for field in fields:
|
||||||
|
count = connection.execute("select count(distinct bucket_start) from profile_buckets where stream_id=? and field=?", (stream_id, str(field).lower())).fetchone()[0]
|
||||||
|
rows.append({"stream_id": stream_id, "field": str(field), "buckets": count, "ready": count >= 12})
|
||||||
|
return rows
|
||||||
|
|
||||||
def profiles(self, source_ips: set[str]) -> dict[str, dict[str, object]]:
|
def profiles(self, source_ips: set[str]) -> dict[str, dict[str, object]]:
|
||||||
profiles: dict[str, dict[str, object]] = {}
|
profiles: dict[str, dict[str, object]] = {}
|
||||||
with self._connect() as connection:
|
with self._connect() as connection:
|
||||||
|
|||||||
@@ -186,10 +186,12 @@ async function refresh() {
|
|||||||
]);
|
]);
|
||||||
const d = data.diagnostics || {};
|
const d = data.diagnostics || {};
|
||||||
const context = data.event_context || {};
|
const context = data.event_context || {};
|
||||||
|
const profileReadiness = data.profile_readiness || [];
|
||||||
const correlations = data.cross_source_correlations || [];
|
const correlations = data.cross_source_correlations || [];
|
||||||
document.getElementById('diagnostics').innerHTML =
|
document.getElementById('diagnostics').innerHTML =
|
||||||
'<h3>Cross-Source Correlations</h3>' + table(correlations, [{label:'Source IP', key:'source_ip'}, {label:'Streams', render:r => esc((r.streams || []).join(', '))}, {label:'Events', key:'events'}, {label:'Security Events', key:'security_events'}]) +
|
'<h3>Cross-Source Correlations</h3>' + table(correlations, [{label:'Source IP', key:'source_ip'}, {label:'Streams', render:r => esc((r.streams || []).join(', '))}, {label:'Events', key:'events'}, {label:'Security Events', key:'security_events'}]) +
|
||||||
'<h3>Entities</h3>' + table(context.source_profiles || [], [{label:'Entity', key:'entity'}, {label:'Events', key:'events'}, {label:'UTM', key:'utm_events'}, {label:'Deny', key:'deny_or_threat_actions'}, {label:'Destinations', key:'distinct_destinations'}, {label:'Actions', render:r => esc((r.top_actions || []).join(', '))}]) +
|
'<h3>Entities</h3>' + table(context.source_profiles || [], [{label:'Entity', key:'entity'}, {label:'Events', key:'events'}, {label:'UTM', key:'utm_events'}, {label:'Deny', key:'deny_or_threat_actions'}, {label:'Destinations', key:'distinct_destinations'}, {label:'Actions', render:r => esc((r.top_actions || []).join(', '))}]) +
|
||||||
|
'<h3>Profile Baseline Readiness</h3>' + table(profileReadiness, [{label:'Stream', key:'stream_id'}, {label:'Field', key:'field'}, {label:'Buckets', key:'buckets'}, {label:'Ready', render:r => r.ready ? 'ready' : 'learning'}]) +
|
||||||
'<h3>Security Event Samples</h3>' + table(context.security_event_samples || [], [{label:'Entity', key:'entity'}, {label:'Type', key:'type'}, {label:'Action', key:'action'}, {label:'Severity', key:'severity'}, {label:'Destination', key:'dst'}, {label:'Service', key:'service'}]) +
|
'<h3>Security Event Samples</h3>' + table(context.security_event_samples || [], [{label:'Entity', key:'entity'}, {label:'Type', key:'type'}, {label:'Action', key:'action'}, {label:'Severity', key:'severity'}, {label:'Destination', key:'dst'}, {label:'Service', key:'service'}]) +
|
||||||
'<h3>Top Sources</h3>' + table(d.top_source_ips || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) +
|
'<h3>Top Sources</h3>' + table(d.top_source_ips || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) +
|
||||||
'<h3>Top Destinations</h3>' + table(d.top_destination_ips || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) +
|
'<h3>Top Destinations</h3>' + table(d.top_destination_ips || [], [{label:'Value', key:'value'}, {label:'Count', key:'count'}]) +
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ def build_status(
|
|||||||
anomalies = detect_source_anomalies(events, limit=anomaly_limit, baselines=profiles, field_deviations=field_deviations)
|
anomalies = detect_source_anomalies(events, limit=anomaly_limit, baselines=profiles, field_deviations=field_deviations)
|
||||||
baseline_events = baseline.ingest(events) if baseline else 0
|
baseline_events = baseline.ingest(events) if baseline else 0
|
||||||
profile_baseline_fields = baseline.ingest_profile_fields(events, stream_profiles) if baseline else 0
|
profile_baseline_fields = baseline.ingest_profile_fields(events, stream_profiles) if baseline else 0
|
||||||
|
profile_readiness = baseline.profile_readiness(stream_profiles) if baseline else []
|
||||||
intel_ips = sorted(
|
intel_ips = sorted(
|
||||||
{
|
{
|
||||||
ip
|
ip
|
||||||
@@ -114,6 +115,7 @@ def build_status(
|
|||||||
"capabilities": {"threat_intel": threat_intel_status, "graylog_mcp": mcp_status},
|
"capabilities": {"threat_intel": threat_intel_status, "graylog_mcp": mcp_status},
|
||||||
"configuration": runtime_config,
|
"configuration": runtime_config,
|
||||||
"stream_profiles": [{"stream_id": item.stream_id, "entity_field": item.entity_field, "timestamp_field": item.timestamp_field, "categorical_fields": list(item.categorical_fields), "numeric_fields": list(item.numeric_fields)} for item in stream_profiles.values()],
|
"stream_profiles": [{"stream_id": item.stream_id, "entity_field": item.entity_field, "timestamp_field": item.timestamp_field, "categorical_fields": list(item.categorical_fields), "numeric_fields": list(item.numeric_fields)} for item in stream_profiles.values()],
|
||||||
|
"profile_readiness": profile_readiness,
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"top_source_ips": top_field_values(events, "srcip", limit=10),
|
"top_source_ips": top_field_values(events, "srcip", limit=10),
|
||||||
"top_destination_ips": top_field_values(events, "dstip", limit=10),
|
"top_destination_ips": top_field_values(events, "dstip", limit=10),
|
||||||
|
|||||||
Reference in New Issue
Block a user