add new structure
This commit is contained in:
@@ -8,7 +8,7 @@ from fgai.config import ConfigStore
|
||||
from fgai.feedback import FeedbackStore
|
||||
from fgai.history import StatusSnapshotStore
|
||||
from fgai.history import FieldDiscoveryStore
|
||||
from fgai.monitor import add_llm_assessment, build_status, cached_status_with_error, monitor_cycle_timeout_seconds, write_refreshing_status, write_status
|
||||
from fgai.monitor import add_llm_assessment, build_status, cached_status_with_error, monitor_cycle_timeout_seconds, write_refreshing_status, write_status, _cluster_field_deviations
|
||||
|
||||
|
||||
class MonitorTests(unittest.TestCase):
|
||||
@@ -39,6 +39,75 @@ class MonitorTests(unittest.TestCase):
|
||||
self.assertFalse(status["status_cache"]["served_from_cache"])
|
||||
self.assertGreaterEqual(len(status["anomalies"]), 1)
|
||||
|
||||
def test_cluster_field_deviations_dampens_large_identical_clusters(self):
|
||||
field_deviations = {
|
||||
f"10.0.0.{index}": [{"detector": "relationship", "field": "client_ip->context", "score": 40}]
|
||||
for index in range(1, 6)
|
||||
}
|
||||
|
||||
_cluster_field_deviations(field_deviations)
|
||||
|
||||
for entity, deviations in field_deviations.items():
|
||||
deviation = deviations[0]
|
||||
self.assertEqual(deviation["cluster_size"], 5)
|
||||
self.assertEqual(deviation["score"], 25)
|
||||
self.assertTrue(deviation["cluster_dampened"])
|
||||
|
||||
def test_cluster_field_deviations_leaves_small_clusters_untouched(self):
|
||||
field_deviations = {
|
||||
f"10.0.0.{index}": [{"detector": "relationship", "field": "client_ip->context", "score": 40}]
|
||||
for index in range(1, 4)
|
||||
}
|
||||
|
||||
_cluster_field_deviations(field_deviations)
|
||||
|
||||
for entity, deviations in field_deviations.items():
|
||||
deviation = deviations[0]
|
||||
self.assertEqual(deviation["cluster_size"], 3)
|
||||
self.assertEqual(deviation["score"], 40)
|
||||
self.assertNotIn("cluster_dampened", deviation)
|
||||
|
||||
def test_cluster_field_deviations_excludes_suppressed_entities_from_cluster_size(self):
|
||||
field_deviations = {
|
||||
f"10.0.0.{index}": [{"detector": "relationship", "field": "client_ip->context", "score": 40}]
|
||||
for index in range(1, 5)
|
||||
}
|
||||
field_deviations["10.0.0.99"] = [{"detector": "relationship", "field": "client_ip->context", "score": 40, "feedback": "false_positive"}]
|
||||
|
||||
_cluster_field_deviations(field_deviations)
|
||||
|
||||
self.assertEqual(field_deviations["10.0.0.1"][0]["cluster_size"], 4)
|
||||
self.assertNotIn("cluster_dampened", field_deviations["10.0.0.1"][0])
|
||||
self.assertEqual(field_deviations["10.0.0.99"][0]["score"], 40)
|
||||
|
||||
def test_cluster_field_deviations_dampen_large_clusters_in_build_status(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
srcips = [f"203.0.113.{index}" for index in range(1, 6)]
|
||||
log_path = Path(tmp) / "fg.log"
|
||||
log_path.write_text(
|
||||
"\n".join(
|
||||
f"type=utm subtype=ips srcip={ip} dstip=10.0.0.10 service=https action=blocked severity=critical"
|
||||
for ip in srcips
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
canned_deviations = {
|
||||
ip: [{"detector": "relationship", "field": "client_ip->context", "stream_id": "utm", "score": 40, "reason": "new relationship: client_ip->context"}]
|
||||
for ip in srcips
|
||||
}
|
||||
|
||||
with patch("fgai.monitor.BaselineStore.profile_deviations", return_value=canned_deviations):
|
||||
status = build_status(str(log_path), baseline_path=str(Path(tmp) / "baseline.sqlite3"), incident_path=str(Path(tmp) / "incidents.json"))
|
||||
|
||||
for ip in srcips:
|
||||
deviation = status["field_deviations"][ip][0]
|
||||
self.assertEqual(deviation["cluster_size"], 5)
|
||||
self.assertEqual(deviation["score"], 25)
|
||||
self.assertTrue(deviation["cluster_dampened"])
|
||||
|
||||
anomalies_by_subject = {item["subject"]: item for item in status["anomalies"]}
|
||||
self.assertIn("new relationship: client_ip->context", anomalies_by_subject[srcips[0]]["reasons"])
|
||||
|
||||
def test_entity_scope_feedback_suppresses_anomaly_in_build_status(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
log_path = Path(tmp) / "fg.log"
|
||||
|
||||
Reference in New Issue
Block a user