Spaces:
Running
Running
| """Monitoring components for the reflexive core.""" | |
| import asyncio | |
| from datetime import datetime, timedelta | |
| from typing import Any, Dict, List, Optional, Callable | |
| from collections import defaultdict, deque | |
| from fastmcp.utilities.logging import get_logger | |
| logger = get_logger(__name__) | |
| class PolicyMonitor: | |
| """Monitor for policy violations.""" | |
| def __init__(self, policy_engine=None): | |
| """Initialize the policy monitor. | |
| Args: | |
| policy_engine: Policy engine instance to monitor | |
| """ | |
| self.policy_engine = policy_engine | |
| self.violation_history = deque(maxlen=1000) # Keep last 1000 violations | |
| self.actor_violations = defaultdict(int) # Track violations per actor | |
| async def __call__(self, action_context) -> Optional[Dict[str, Any]]: | |
| """Monitor an action for policy violations.""" | |
| try: | |
| # Check for policy violations (works with or without policy engine) | |
| violations = await self._check_policy_violations(action_context) | |
| if violations: | |
| # Record violation | |
| violation_record = { | |
| "type": "violation", | |
| "severity": self._assess_violation_severity(violations), | |
| "violations": violations, | |
| "actor_id": action_context.actor_id, | |
| "action_id": action_context.action_id, | |
| "timestamp": datetime.utcnow().isoformat() | |
| } | |
| self.violation_history.append(violation_record) | |
| self.actor_violations[action_context.actor_id] += 1 | |
| return violation_record | |
| return None | |
| except Exception as e: | |
| logger.error(f"Policy monitor error: {e}") | |
| return None | |
| async def _check_policy_violations(self, action_context) -> List[Dict[str, Any]]: | |
| """Check for policy violations in an action.""" | |
| violations = [] | |
| try: | |
| # Simulate policy checking | |
| # In a real implementation, this would use the actual policy engine | |
| # Check for suspicious patterns | |
| if action_context.action_type == "admin_access" and action_context.actor_id.startswith("guest"): | |
| violations.append({ | |
| "rule": "admin_access_restriction", | |
| "message": "Guest user attempting admin access", | |
| "severity": "high" | |
| }) | |
| # Check for rate limiting | |
| recent_violations = [v for v in self.violation_history | |
| if v.get("actor_id") == action_context.actor_id | |
| and datetime.fromisoformat(v["timestamp"]) > datetime.utcnow() - timedelta(minutes=5)] | |
| if len(recent_violations) >= 3: | |
| violations.append({ | |
| "rule": "rate_limit_exceeded", | |
| "message": f"Actor {action_context.actor_id} has {len(recent_violations)} recent violations", | |
| "severity": "medium" | |
| }) | |
| # Check for resource access patterns | |
| if action_context.resource_id and "sensitive" in action_context.resource_id.lower(): | |
| if not action_context.metadata.get("authorized"): | |
| violations.append({ | |
| "rule": "unauthorized_sensitive_access", | |
| "message": "Unauthorized access to sensitive resource", | |
| "severity": "critical" | |
| }) | |
| except Exception as e: | |
| logger.error(f"Error checking policy violations: {e}") | |
| return violations | |
| def _assess_violation_severity(self, violations: List[Dict[str, Any]]) -> str: | |
| """Assess the overall severity of violations.""" | |
| if not violations: | |
| return "low" | |
| severities = [v.get("severity", "low") for v in violations] | |
| if "critical" in severities: | |
| return "critical" | |
| elif "high" in severities: | |
| return "high" | |
| elif "medium" in severities: | |
| return "medium" | |
| else: | |
| return "low" | |
| def get_violation_stats(self) -> Dict[str, Any]: | |
| """Get violation statistics.""" | |
| return { | |
| "total_violations": len(self.violation_history), | |
| "actor_violations": dict(self.actor_violations), | |
| "recent_violations": len([v for v in self.violation_history | |
| if datetime.fromisoformat(v["timestamp"]) > datetime.utcnow() - timedelta(hours=1)]) | |
| } | |
| class LedgerMonitor: | |
| """Monitor for ledger inconsistencies and anomalies.""" | |
| def __init__(self, ledger=None): | |
| """Initialize the ledger monitor. | |
| Args: | |
| ledger: Provenance ledger instance to monitor | |
| """ | |
| self.ledger = ledger | |
| self.integrity_checks = deque(maxlen=100) # Keep last 100 integrity checks | |
| async def __call__(self, action_context) -> Optional[Dict[str, Any]]: | |
| """Monitor ledger for inconsistencies.""" | |
| try: | |
| if not self.ledger: | |
| return None | |
| # Check ledger integrity | |
| integrity_issues = await self._check_ledger_integrity() | |
| if integrity_issues: | |
| # Record integrity issue | |
| issue_record = { | |
| "type": "anomaly", | |
| "severity": self._assess_integrity_severity(integrity_issues), | |
| "issues": integrity_issues, | |
| "action_id": action_context.action_id, | |
| "timestamp": datetime.utcnow().isoformat() | |
| } | |
| self.integrity_checks.append(issue_record) | |
| return issue_record | |
| return None | |
| except Exception as e: | |
| logger.error(f"Ledger monitor error: {e}") | |
| return None | |
| async def _check_ledger_integrity(self) -> List[Dict[str, Any]]: | |
| """Check ledger for integrity issues.""" | |
| issues = [] | |
| try: | |
| # Check chain integrity | |
| is_valid = self.ledger.verify_chain_integrity() | |
| if not is_valid: | |
| issues.append({ | |
| "type": "chain_integrity", | |
| "message": "Ledger chain integrity verification failed", | |
| "severity": "critical" | |
| }) | |
| # Check for missing blocks | |
| stats = self.ledger.get_ledger_statistics() | |
| if stats.get("total_entries", 0) > 0 and stats.get("total_blocks", 0) == 0: | |
| issues.append({ | |
| "type": "missing_blocks", | |
| "message": "Entries exist but no blocks found", | |
| "severity": "high" | |
| }) | |
| # Check for unsealed blocks | |
| # This would require additional ledger methods to check for unsealed blocks | |
| except Exception as e: | |
| logger.error(f"Error checking ledger integrity: {e}") | |
| issues.append({ | |
| "type": "integrity_check_error", | |
| "message": f"Error during integrity check: {str(e)}", | |
| "severity": "medium" | |
| }) | |
| return issues | |
| def _assess_integrity_severity(self, issues: List[Dict[str, Any]]) -> str: | |
| """Assess the overall severity of integrity issues.""" | |
| if not issues: | |
| return "low" | |
| severities = [i.get("severity", "low") for i in issues] | |
| if "critical" in severities: | |
| return "critical" | |
| elif "high" in severities: | |
| return "high" | |
| elif "medium" in severities: | |
| return "medium" | |
| else: | |
| return "low" | |
| def get_integrity_stats(self) -> Dict[str, Any]: | |
| """Get integrity check statistics.""" | |
| return { | |
| "total_checks": len(self.integrity_checks), | |
| "recent_issues": len([i for i in self.integrity_checks | |
| if datetime.fromisoformat(i["timestamp"]) > datetime.utcnow() - timedelta(hours=1)]) | |
| } | |
| class AnomalyDetector: | |
| """Detector for behavioral anomalies.""" | |
| def __init__(self): | |
| """Initialize the anomaly detector.""" | |
| self.actor_patterns = defaultdict(lambda: { | |
| "action_counts": defaultdict(int), | |
| "resource_access": defaultdict(int), | |
| "session_times": deque(maxlen=100), | |
| "last_seen": None | |
| }) | |
| self.global_patterns = { | |
| "action_frequency": defaultdict(int), | |
| "resource_access": defaultdict(int), | |
| "time_patterns": defaultdict(int) | |
| } | |
| async def __call__(self, action_context) -> Optional[Dict[str, Any]]: | |
| """Detect anomalies in an action.""" | |
| try: | |
| # Update patterns | |
| self._update_patterns(action_context) | |
| # Detect anomalies | |
| anomalies = await self._detect_anomalies(action_context) | |
| if anomalies: | |
| return { | |
| "type": "anomaly", | |
| "severity": self._assess_anomaly_severity(anomalies), | |
| "anomalies": anomalies, | |
| "actor_id": action_context.actor_id, | |
| "action_id": action_context.action_id, | |
| "timestamp": datetime.utcnow().isoformat() | |
| } | |
| return None | |
| except Exception as e: | |
| logger.error(f"Anomaly detector error: {e}") | |
| return None | |
| def _update_patterns(self, action_context): | |
| """Update behavioral patterns.""" | |
| actor_id = action_context.actor_id | |
| actor_data = self.actor_patterns[actor_id] | |
| # Update action counts | |
| actor_data["action_counts"][action_context.action_type] += 1 | |
| self.global_patterns["action_frequency"][action_context.action_type] += 1 | |
| # Update resource access | |
| if action_context.resource_id: | |
| actor_data["resource_access"][action_context.resource_id] += 1 | |
| self.global_patterns["resource_access"][action_context.resource_id] += 1 | |
| # Update session times | |
| actor_data["session_times"].append(action_context.timestamp) | |
| actor_data["last_seen"] = action_context.timestamp | |
| # Update time patterns | |
| hour = action_context.timestamp.hour | |
| self.global_patterns["time_patterns"][hour] += 1 | |
| async def _detect_anomalies(self, action_context) -> List[Dict[str, Any]]: | |
| """Detect anomalies in the action.""" | |
| anomalies = [] | |
| actor_id = action_context.actor_id | |
| actor_data = self.actor_patterns[actor_id] | |
| # Check for unusual action frequency | |
| if len(actor_data["session_times"]) >= 10: | |
| recent_actions = [t for t in actor_data["session_times"] | |
| if t > datetime.utcnow() - timedelta(minutes=5)] | |
| if len(recent_actions) > 20: # More than 20 actions in 5 minutes | |
| anomalies.append({ | |
| "type": "high_frequency", | |
| "message": f"Actor {actor_id} performing {len(recent_actions)} actions in 5 minutes", | |
| "severity": "medium" | |
| }) | |
| # Check for unusual time patterns | |
| current_hour = action_context.timestamp.hour | |
| if current_hour < 6 or current_hour > 22: # Unusual hours | |
| if actor_data["action_counts"].get(action_context.action_type, 0) < 5: # New action type | |
| anomalies.append({ | |
| "type": "unusual_timing", | |
| "message": f"Actor {actor_id} performing {action_context.action_type} at unusual hour {current_hour}", | |
| "severity": "low" | |
| }) | |
| # Check for new resource access | |
| if action_context.resource_id: | |
| if actor_data["resource_access"].get(action_context.resource_id, 0) == 1: | |
| # First time accessing this resource | |
| anomalies.append({ | |
| "type": "new_resource_access", | |
| "message": f"Actor {actor_id} accessing new resource {action_context.resource_id}", | |
| "severity": "low" | |
| }) | |
| # Check for privilege escalation patterns | |
| if action_context.action_type in ["admin_access", "root_access", "privilege_escalation"]: | |
| if actor_data["action_counts"].get(action_context.action_type, 0) == 1: | |
| # First time performing privileged action | |
| anomalies.append({ | |
| "type": "privilege_escalation", | |
| "message": f"Actor {actor_id} attempting privileged action for first time", | |
| "severity": "high" | |
| }) | |
| return anomalies | |
| def _assess_anomaly_severity(self, anomalies: List[Dict[str, Any]]) -> str: | |
| """Assess the overall severity of anomalies.""" | |
| if not anomalies: | |
| return "low" | |
| severities = [a.get("severity", "low") for a in anomalies] | |
| if "high" in severities: | |
| return "high" | |
| elif "medium" in severities: | |
| return "medium" | |
| else: | |
| return "low" | |
| def get_anomaly_stats(self) -> Dict[str, Any]: | |
| """Get anomaly detection statistics.""" | |
| return { | |
| "tracked_actors": len(self.actor_patterns), | |
| "global_action_types": len(self.global_patterns["action_frequency"]), | |
| "global_resources": len(self.global_patterns["resource_access"]) | |
| } | |