Spaces:
Runtime error
Runtime error
| import os | |
| import threading | |
| import time | |
| from collections import deque | |
| import joblib | |
| import pandas as pd | |
| from flask import Flask, jsonify, request | |
| from flask_cors import CORS | |
| from flask_socketio import SocketIO | |
| import sys | |
| sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from ml_models.ensemble_pipeline import load_ensemble | |
| from ollama_ai.ai_analysis import analyze_network_traffic | |
| if getattr(sys, "frozen", False): | |
| BASE_DIR = sys._MEIPASS | |
| else: | |
| BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| app = Flask(__name__, static_folder=os.path.join(BASE_DIR, "dashboard"), static_url_path="/") | |
| CORS(app) | |
| socketio = SocketIO(app, cors_allowed_origins="*", async_mode="threading") | |
| MODEL_PATH = os.path.join(BASE_DIR, "ml_models", "device_classifier.pkl") | |
| SCALER_PATH = os.path.join(BASE_DIR, "datasets", "scaler.pkl") | |
| LE_PATH = os.path.join(BASE_DIR, "datasets", "label_encoder.pkl") | |
| ENSEMBLE_META_PATH = os.path.join(BASE_DIR, "ml_models", "ensemble_artifacts", "metadata.json") | |
| model = None | |
| scaler = None | |
| label_encoder = None | |
| ensemble_pipeline = None | |
| sniffer_instance = None | |
| recent_alerts = deque(maxlen=50) | |
| latest_snapshot = None | |
| _broadcast_started = False | |
| _broadcast_lock = threading.Lock() | |
| _snapshot_lock = threading.Lock() | |
| def load_models(): | |
| global model, scaler, label_encoder, ensemble_pipeline | |
| try: | |
| if model is None and os.path.exists(MODEL_PATH): | |
| model = joblib.load(MODEL_PATH) | |
| if scaler is None and os.path.exists(SCALER_PATH): | |
| scaler = joblib.load(SCALER_PATH) | |
| if label_encoder is None and os.path.exists(LE_PATH): | |
| label_encoder = joblib.load(LE_PATH) | |
| if ensemble_pipeline is None and os.path.exists(ENSEMBLE_META_PATH): | |
| ensemble_pipeline = load_ensemble() | |
| except Exception as exc: | |
| print(f"Error loading models: {exc}") | |
| def _protocol_to_numeric(protocol: str) -> int: | |
| protocol = str(protocol).upper() | |
| if protocol == "TCP": | |
| return 0 | |
| if protocol == "UDP": | |
| return 1 | |
| return 2 | |
| def _append_alert(ip: str, threat_type: str, severity: str, confidence: float): | |
| alert = { | |
| "ip": ip, | |
| "threat_type": threat_type, | |
| "severity": severity, | |
| "confidence": round(confidence, 2), | |
| "timestamp": time.time(), | |
| } | |
| recent_alerts.appendleft(alert) | |
| return alert | |
| def _build_legacy_predictions(features_list): | |
| results = [] | |
| anomaly_count = 0 | |
| batch_data = [] | |
| for feature in features_list: | |
| protocol_val = _protocol_to_numeric(feature["protocol"]) | |
| batch_data.append( | |
| { | |
| "packet_size": feature["packet_size"], | |
| "protocol": protocol_val, | |
| "packet_rate": feature["packet_rate"], | |
| "flow_duration": feature["flow_duration"], | |
| } | |
| ) | |
| df = pd.DataFrame(batch_data) | |
| if scaler: | |
| expected_cols = getattr(scaler, "feature_names_in_", df.columns) | |
| for col in expected_cols: | |
| if col not in df.columns: | |
| df[col] = 0 | |
| df = df[expected_cols] | |
| df_scaled = scaler.transform(df) | |
| else: | |
| df_scaled = df.values | |
| if model: | |
| preds_encoded = model.predict(df_scaled) | |
| pred_probs = model.predict_proba(df_scaled) | |
| else: | |
| preds_encoded = [0] * len(df) | |
| pred_probs = [[1.0]] * len(df) | |
| if label_encoder: | |
| device_types = label_encoder.inverse_transform(preds_encoded) | |
| else: | |
| classes = ["smartphone", "laptop", "smart_tv", "iot_device", "tablet"] | |
| device_types = [classes[p % len(classes)] for p in preds_encoded] | |
| for index, feature in enumerate(features_list): | |
| confidence = float(max(pred_probs[index]) * 100) | |
| device_type = device_types[index] | |
| is_suspicious = confidence < 60.0 | |
| if is_suspicious: | |
| anomaly_count += 1 | |
| _append_alert( | |
| feature["ip"], | |
| device_type, | |
| "Medium" if confidence >= 40 else "High", | |
| confidence, | |
| ) | |
| results.append( | |
| { | |
| "ip": feature["ip"], | |
| "threat_type": device_type, | |
| "device_type": device_type, | |
| "confidence": round(confidence, 2), | |
| "is_suspicious": is_suspicious, | |
| "severity": "Suspicious" if is_suspicious else "Normal", | |
| "metrics": { | |
| "packet_rate": round(feature["packet_rate"], 2), | |
| "packet_size": round(feature["packet_size"], 2), | |
| "byte_rate": round(feature.get("byte_rate", 0.0), 2), | |
| "flow_duration": round(feature["flow_duration"], 2), | |
| }, | |
| } | |
| ) | |
| return results, anomaly_count | |
| def _build_ensemble_predictions(features_list): | |
| results = [] | |
| anomaly_count = 0 | |
| for feature in features_list: | |
| prediction = ensemble_pipeline.predict(feature) | |
| confidence = float(prediction["confidence"] * 100.0) | |
| if prediction["is_anomaly"]: | |
| anomaly_count += 1 | |
| _append_alert( | |
| feature["ip"], | |
| prediction["threat_type"], | |
| prediction["severity"], | |
| confidence, | |
| ) | |
| results.append( | |
| { | |
| "ip": feature["ip"], | |
| "threat_type": prediction["threat_type"], | |
| "device_type": prediction["threat_type"], | |
| "confidence": round(confidence, 2), | |
| "is_suspicious": prediction["is_anomaly"], | |
| "severity": prediction["severity"], | |
| "anomaly_score": float(prediction["anomaly_score"]), | |
| "autoencoder_error": float(prediction["autoencoder_error"]), | |
| "metrics": { | |
| "packet_rate": round(feature["packet_rate"], 2), | |
| "packet_size": round(feature["packet_size"], 2), | |
| "byte_rate": round(feature.get("byte_rate", 0.0), 2), | |
| "flow_duration": round(feature["flow_duration"], 2), | |
| }, | |
| } | |
| ) | |
| return results, anomaly_count | |
| def _build_snapshot(): | |
| load_models() | |
| if sniffer_instance is None: | |
| return { | |
| "health": {"health_score": 100, "status": "Unknown", "anomalies": 0}, | |
| "devices": [], | |
| "arp_devices": [], | |
| "alerts": list(recent_alerts)[:10], | |
| "total_devices": 0, | |
| "anomalies": 0, | |
| "timestamp": time.time(), | |
| "capture": {"mode": "idle", "running": False, "interface": None, "last_error": None}, | |
| } | |
| features_list = sniffer_instance.get_latest_features() | |
| arp_devices = sniffer_instance.arp_scan() | |
| capture_status = sniffer_instance.get_status() if hasattr(sniffer_instance, "get_status") else { | |
| "mode": "unknown", | |
| "running": bool(getattr(sniffer_instance, "is_sniffing", False)), | |
| "interface": None, | |
| "last_error": getattr(sniffer_instance, "last_error", None), | |
| } | |
| if not features_list: | |
| return { | |
| "health": {"health_score": 100, "status": "Unknown", "anomalies": 0}, | |
| "devices": [], | |
| "arp_devices": arp_devices, | |
| "alerts": list(recent_alerts)[:10], | |
| "total_devices": 0, | |
| "anomalies": 0, | |
| "timestamp": time.time(), | |
| "capture": capture_status, | |
| } | |
| if ensemble_pipeline is not None and os.path.exists(ENSEMBLE_META_PATH): | |
| devices, anomaly_count = _build_ensemble_predictions(features_list) | |
| else: | |
| devices, anomaly_count = _build_legacy_predictions(features_list) | |
| health_score = max(0, 100 - int(anomaly_count) * 10) | |
| status = "Good" | |
| if health_score < 80: | |
| status = "Warning" | |
| if health_score < 50: | |
| status = "Critical" | |
| total_traffic = round(float(sum(feature.get("byte_rate", 0.0) for feature in features_list)), 2) | |
| return { | |
| "health": { | |
| "health_score": health_score, | |
| "status": status, | |
| "anomalies": anomaly_count, | |
| "total_devices": len(features_list), | |
| "traffic_load": total_traffic, | |
| }, | |
| "devices": devices, | |
| "arp_devices": arp_devices, | |
| "alerts": list(recent_alerts)[:10], | |
| "total_devices": len(devices), | |
| "anomalies": anomaly_count, | |
| "timestamp": time.time(), | |
| "capture": capture_status, | |
| } | |
| def _store_snapshot(snapshot): | |
| global latest_snapshot | |
| with _snapshot_lock: | |
| latest_snapshot = snapshot | |
| def _get_snapshot(): | |
| with _snapshot_lock: | |
| if latest_snapshot is not None: | |
| return latest_snapshot | |
| snapshot = _build_snapshot() | |
| _store_snapshot(snapshot) | |
| return snapshot | |
| def _broadcast_loop(): | |
| while True: | |
| snapshot = _build_snapshot() | |
| _store_snapshot(snapshot) | |
| socketio.emit("network_update", snapshot) | |
| socketio.sleep(2) | |
| def start_socket_broadcasts(): | |
| global _broadcast_started | |
| with _broadcast_lock: | |
| if _broadcast_started: | |
| return | |
| _broadcast_started = True | |
| socketio.start_background_task(_broadcast_loop) | |
| def index(): | |
| return app.send_static_file("index.html") | |
| def scan_network(): | |
| snapshot = _get_snapshot() | |
| return jsonify( | |
| { | |
| "devices": snapshot["devices"], | |
| "arp_devices": snapshot["arp_devices"], | |
| "alerts": snapshot["alerts"], | |
| "total_devices": snapshot["total_devices"], | |
| "anomalies": snapshot["anomalies"], | |
| "health": snapshot["health"], | |
| "timestamp": snapshot["timestamp"], | |
| "capture": snapshot.get("capture", {}), | |
| } | |
| ) | |
| def arp_devices(): | |
| if sniffer_instance is None: | |
| return jsonify({"devices": []}) | |
| return jsonify({"devices": sniffer_instance.arp_scan()}) | |
| def predict_device(): | |
| load_models() | |
| data = request.json | |
| if not data: | |
| return jsonify({"error": "No input data provided"}), 400 | |
| required = ["packet_size", "protocol", "packet_rate", "flow_duration"] | |
| if not all(key in data for key in required): | |
| return jsonify({"error": f"Missing required fields. Expected: {required}"}), 400 | |
| try: | |
| protocol_val = data["protocol"] | |
| if isinstance(protocol_val, str): | |
| protocol_val = _protocol_to_numeric(protocol_val) | |
| df = pd.DataFrame( | |
| [ | |
| { | |
| "packet_size": float(data["packet_size"]), | |
| "protocol": protocol_val, | |
| "packet_rate": float(data["packet_rate"]), | |
| "flow_duration": float(data["flow_duration"]), | |
| } | |
| ] | |
| ) | |
| if scaler: | |
| expected_cols = getattr(scaler, "feature_names_in_", df.columns) | |
| for col in expected_cols: | |
| if col not in df.columns: | |
| df[col] = 0 | |
| df = df[expected_cols] | |
| df_scaled = scaler.transform(df) | |
| else: | |
| df_scaled = df.values | |
| if not model: | |
| return jsonify({"error": "Model not trained yet."}), 503 | |
| pred_encoded = model.predict(df_scaled)[0] | |
| confidence = float(max(model.predict_proba(df_scaled)[0]) * 100) | |
| if label_encoder: | |
| device_type = label_encoder.inverse_transform([pred_encoded])[0] | |
| else: | |
| classes = ["smartphone", "laptop", "smart_tv", "iot_device", "tablet"] | |
| device_type = classes[pred_encoded % len(classes)] | |
| return jsonify({"device_type": device_type, "confidence": round(confidence, 2)}) | |
| except Exception as exc: | |
| import traceback | |
| return jsonify({"error": str(exc), "trace": traceback.format_exc()}), 500 | |
| def network_health(): | |
| snapshot = _get_snapshot() | |
| return jsonify(snapshot["health"]) | |
| def capture_status(): | |
| snapshot = _get_snapshot() | |
| return jsonify(snapshot.get("capture", {"mode": "idle", "running": False, "interface": None, "last_error": None})) | |
| def ai_analysis(): | |
| data = request.json | |
| if not data: | |
| return jsonify({"error": "No data provided"}), 400 | |
| result = analyze_network_traffic(data) | |
| return jsonify(result) | |
| if __name__ == "__main__": | |
| app.run(debug=True, host="0.0.0.0", port=5000) | |