APPENDIX
#[Link]
FRONTEND
import joblib
import pandas as pd
import os
import numpy as np
from flask import Flask, render_template, request, redirect, url_for, session, flash
# --- NEW: Import Flask-Mail ---
from flask_mail import Mail, Message
app = Flask(_name_)
app.secret_key = 'your_super_secret_key_here'
# --- NEW: Flask-Mail Configuration ---
# IMPORTANT: For local testing only. Do not share this code with credentials included.
# Flask-Mail Configuration
[Link]['MAIL_SERVER'] = '[Link]'
[Link]['MAIL_PORT'] = 587
[Link]['MAIL_USE_TLS'] = True
[Link]['MAIL_USE_SSL'] = False
[Link]['MAIL_USERNAME'] = 'naveencse26@[Link]'
[Link]['MAIL_PASSWORD'] = 'vctcgnonguwzihbs'
[Link]['MAIL_DEFAULT_SENDER'] = 'naveencse26@[Link]'
59
# Initialize the Mail extension
mail = Mail(app)
# --- DEBUGGING INFO ---
print(f"Current NumPy Version: {np._version_}")
# --- ROBUST NUMPY PATCH (Fixes MT19937 Error in NumPy 1.26+) ---
try:
# 1. Import the BitGenerator class that the model expects
from [Link]._mt19937 import MT19937
# 2. Access the internal pickle registry in NumPy
# In NumPy 1.26+, this registry is located here:
if hasattr([Link], '_pickle'):
# Get the registry dictionary (or create it if missing)
registry = getattr([Link]._pickle, 'bit_generator_classes', None)
if registry is None:
# If the attribute doesn't exist, we create a new dictionary and attach it
registry = {}
[Link]._pickle.bit_generator_classes = registry
# 3. Manually register the old path to the new class
registry['[Link]._mt19937.MT19937'] = MT19937
p r i n t ( " ⬛NumPy Patch Applied: Registered MT19937
successfully.") else:
print("ı. Patch skipped: NumPy structure different than expected.")
except Exception as e:
print(f"ı. Patch failed: {e}")
# --- MODEL LOADING ---
59
MODEL_PATH = "gradient_boosting.joblib"
model = None
try:
if [Link](MODEL_PATH):
model = [Link](MODEL_PATH)
p r i n t ( f " ⬛Model loaded successfully from {MODEL_PATH}")
else:
print(f"+ Error: Model file not found at {MODEL_PATH}")
except Exception as e:
print(f"+ CRITICAL Error loading model: {e}")
print(">>> SOLUTION: If this error persists, run this command in your terminal: pip install
numpy==1.23.5")
# --- MOCK DATABASE ---
users_db = {}
history_db = []
# --- NEW: Email Sending Function ---
def send_alert_email(attack, location, data):
"""
Sends an email alert to the SOC team when a threat is detected.
"""
# This recipient list should be configured for your team
recipient_list = ['karthihema2000@[Link]']
subject = f"SentinelIDS Alert: {attack} Attack Detected"
# Create a nicely formatted email body using the data from the form
body = f"""
59
SentinelIDS has detected a potential threat.
----- THREAT DETAILS -----
Attack Type: {attack}
Predicted Location: {location}
----- PACKET DATA -----
Source Port: {[Link]('src_port')}
Destination Port: {[Link]('dst_port')}
Protocol: {[Link]('protocol')}
Duration (s): {[Link]('duration_s')}
Packet Count: {[Link]('packet_count')}
Byte Count: {[Link]('byte_count')}
Payload Entropy: {[Link]('payload_entropy')}
TTL: {[Link]('ttl')}
TCP Window Size: {[Link]('tcp_window_size')}
Flags: {[Link]('flags')}
Please investigate this event immediately.
"""
msg = Message(
subject=subject,
recipients=recipient_list,
body=body
try:
59
[Link](msg)
print(f"SUCCESS: Alert email sent for {attack} attack.")
except Exception as e:
print(f"ERROR: Failed to send email. {e}")
# --- ROUTES ---
@[Link]('/')
def home():
return render_template('[Link]')
@[Link]('/login', methods=['GET', 'POST'])
def login():
if [Link] == 'POST':
username = [Link]('username')
password = [Link]('password')
if username in users_db and users_db[username] == password:
session['user'] = username
flash('Login successful!', 'success')
return redirect(url_for('index'))
else:
flash('Invalid username or password.', 'danger')
return render_template('[Link]')
@[Link]('/signup', methods=['GET', 'POST'])
def signup():
if [Link] == 'POST':
username = [Link]('username')
password = [Link]('password')
59
if username in users_db:
flash('Username already exists.', 'warning')
else:
users_db[username] = password
flash('Account created! Please login.', 'success')
return redirect(url_for('login'))
return render_template('[Link]')
@[Link]('/logout')
def logout():
[Link]('user', None)
flash('You have been logged out.', 'info')
return redirect(url_for('home'))
@[Link]('/index', methods=['GET', 'POST'])
def index():
if 'user' not in session:
flash('Please login.', 'warning')
return redirect(url_for('login'))
if [Link] == 'POST':
try:
data = {
"src_port": int([Link]('src_port')),
"dst_port": int([Link]('dst_port')),
"protocol": [Link]('protocol'),
"duration_s": float([Link]('duration_s')),
"packet_count": int([Link]('packet_count')),
59
"byte_count": int([Link]('byte_count')),
"payload_entropy": float([Link]('payload_entropy')),
"ttl": int([Link]('ttl')),
"tcp_window_size": int([Link]('tcp_window_size')),
"flags": [Link]('flags')
X_new = [Link]([data])
if model:
pred = [Link](X_new)
location = pred[0][0]
attack = pred[0][1]
# --- NEW: Call email function if attack is not 'Normal' ---
if attack != 'Normal':
send_alert_email(attack, location, data)
record = [Link]()
record['prediction_location'] = location
record['prediction_attack'] = attack
record['user'] = session['user']
history_db.append(record)
return render_template('[Link]', location=location, attack=attack, data=data)
else:
flash('Model not loaded.', 'danger')
except Exception as e:
flash(f'Prediction error: {str(e)}', 'danger')
59
return render_template('[Link]')
# return render_template('[Link]', location=location, attack=attack, data=data)
@[Link]('/history')
def history():
if 'user' not in session:
return redirect(url_for('login'))
user_history = [h for h in history_db if h['user'] == session['user']]
return render_template('[Link]', history=user_history)
if _name_ == '_main_':
[Link](debug=True)
BACKEND
# Cell 1: Constants and ensure output dir
CSV_PATH = "/content/honeypot_full_36groups_2000each.csv" # update if
needed
OUT_DIR = "/content/multioutput_models"
RANDOM_STATE = 42
TEST_SIZE = 0.2
import os
[Link](OUT_DIR, exist_ok=True)
print("OUT_DIR =", OUT_DIR)
# Cell 2: imports
import traceback, sys
import pandas as pd
import numpy as np
59
from pathlib import Path
from collections import Counter, defaultdict
import joblib
import [Link] as plt
%matplotlib inline
# Cell 3: sklearn imports and OneHotEncoder helper
from sklearn.model_selection import train_test_split
from [Link] import ColumnTransformer
from [Link] import OneHotEncoder, StandardScaler
from [Link] import Pipeline
from [Link] import RandomForestClassifier,
GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from [Link] import MultiOutputClassifier
from [Link] import classification_report, accuracy_score
from [Link] import OneHotEncoder as _OHE
def make_onehot_encoder(**kwargs):
try:
return _OHE(handle_unknown="ignore", sparse_output=False, **kwargs)
except TypeError:
return _OHE(handle_unknown="ignore", sparse=False, **kwargs)
# Cell 4: load csv
csvp = Path(CSV_PATH)
if not [Link]():
raise FileNotFoundError(f"CSV not found at {csvp}")
df = pd.read_csv(csvp)
print("Loaded:", CSV_PATH, "rows:", len(df))
# Cell 5: define targets and features
TARGET_COLS = ["honeypot_location", "attack_type"]
for t in TARGET_COLS:
if t not in [Link]:
60
raise RuntimeError(f"Target column '{t}' missing from CSV")
X = [Link](columns=TARGET_COLS)
y = df[TARGET_COLS].astype(str)
print("Features (X) columns:", list([Link]))
print("Targets (y) columns:", TARGET_COLS)
for t in TARGET_COLS:
print(f" - {t}: {sorted(y[t].unique())[:10]} ... (total {y[t].nunique()})")
# Cell 6: drop high-cardinality and handle missing
drop_cols = [c for c in ("timestamp", "src_ip", "dst_ip") if c in [Link]]
if drop_cols:
print("Dropping from features (high-cardinality):", drop_cols)
X = [Link](columns=drop_cols)
if [Link]().any().any() or [Link]().any().any():
print("Dropping rows with missing values (simple handling).")
combined = [Link]([X, y], axis=1)
combined = [Link]().reset_index(drop=True)
X = combined[[Link]]
y = combined[TARGET_COLS]
print("After cleaning, rows:", len(X))
# Cell 7: train/test split
X_train, X_test, y_train_df, y_test_df = train_test_split(
X, y, test_size=TEST_SIZE, stratify=y[TARGET_COLS[1]] if
TARGET_COLS[1] in y else None,
random_state=RANDOM_STATE
)
print("Train shape:", X_train.shape, "Test shape:", X_test.shape)
# Cell 8: build preprocessor - identify numeric and categorical
numeric_cols = X_train.select_dtypes(include=[[Link]]).[Link]()
61
categorical_cols = X_train.select_dtypes(include=["object",
"category"]).[Link]()
print("Numeric cols:", numeric_cols)
print("Categorical cols:", categorical_cols)
numeric_transformer = Pipeline([("scaler", StandardScaler())])
ohe = make_onehot_encoder()
categorical_transformer = Pipeline([("onehot", ohe)])
preprocessor = ColumnTransformer(transformers=[
("num", numeric_transformer, numeric_cols),
("cat", categorical_transformer, categorical_cols)
], remainder="drop")
print("Preprocessor ready")
# Cell 9: define base estimators and multioutput pipelines
rf = RandomForestClassifier(n_estimators=200,
random_state=RANDOM_STATE, n_jobs=-1)
gb = GradientBoostingClassifier(n_estimators=100,
random_state=RANDOM_STATE)
lr = LogisticRegression(max_iter=1000, random_state=RANDOM_STATE)
rf_pipe = Pipeline([("pre", preprocessor), ("clf", MultiOutputClassifier(rf))])
gb_pipe = Pipeline([("pre", preprocessor), ("clf", MultiOutputClassifier(gb))])
lr_pipe = Pipeline([("pre", preprocessor), ("clf", MultiOutputClassifier(lr))])
models = {
"random_forest": rf_pipe,
"gradient_boosting": gb_pipe,
"logistic_regression": lr_pipe
}
print("Model pipelines:", list([Link]()))
# Cell 10: fit models (rf)
print("\nTraining random_forest ...")
62
try:
models["random_forest"].fit(X_train, y_train_df)
[Link](models["random_forest"],
Path(OUT_DIR)/"random_forest.joblib")
print("Saved random_forest")
except Exception as e:
print("Error training random_forest:", e)
traceback.print_exc(file=[Link])
# Cell 11: fit models (gb)
print("\nTraining gradient_boosting ...")
try:
models["gradient_boosting"].fit(X_train, y_train_df)
[Link](models["gradient_boosting"],
Path(OUT_DIR)/"gradient_boosting.joblib")
print("Saved gradient_boosting")
except Exception as e:
print("Error training gradient_boosting:", e)
traceback.print_exc(file=[Link])
# Cell 12: fit models (lr)
print("\nTraining logistic_regression ...")
try:
models["logistic_regression"].fit(X_train, y_train_df)
[Link](models["logistic_regression"],
Path(OUT_DIR)/"logistic_regression.joblib")
print("Saved logistic_regression")
except Exception as e:
print("Error training logistic_regression:", e)
traceback.print_exc(file=[Link]) # Cell 13: store trained dict for convenience
trained = {}
for name in list([Link]()):
try:
trained[name] = [Link](Path(OUT_DIR)/f"{name}.joblib")
except Exception:
trained[name] = models[name]
63
print("Trained models ready:", list([Link]()))
# Recovery + evaluation (attack_type only)
import joblib
from pathlib import Path
import pandas as pd
from [Link] import classification_report, accuracy_score
# 1) Rebuild trained
trained = {}
model_files = {
"random_forest": Path(OUT_DIR)/"random_forest.joblib",
"gradient_boosting": Path(OUT_DIR)/"gradient_boosting.joblib",
"logistic_regression": Path(OUT_DIR)/"logistic_regression.joblib"
}
for name, path in model_files.items():
if [Link]():
try:
trained[name] = [Link](path)
print(f"Loaded {name} from {path}")
except Exception as e:
print(f"Failed to load {path}: {e}")
elif 'models' in globals() and name in models:
trained[name] = models[name]
print(f"Using in-memory pipeline for {name}")
else:
print(f"No saved or in-memory pipeline found for {name} — skipping.")
if not trained:
raise RuntimeError("No models found to evaluate.")
# 2) Evaluate attack_type only
eval_results = {}
64
predictions = {}
for name, pipe in [Link]():
try:
y_pred = [Link](X_test)
y_pred_df = [Link](y_pred, columns=TARGET_COLS,
index=X_test.index)
y_true = y_test_df["attack_type"]
y_pred_attack = y_pred_df["attack_type"]
print(f"\n=== {name} - Evaluation for attack_type ===")
print(classification_report(y_true, y_pred_attack, digits=4))
acc = accuracy_score(y_true, y_pred_attack)
print(f"{name} attack_type accuracy: {acc:.4f}")
eval_results[name] = {"attack_type_accuracy": acc}
predictions[name] = y_pred_attack
except Exception as e:
print(f"Error evaluating {name}: {e}")
import traceback; traceback.print_exc()
print("\nDone. Variables ready: trained, eval_results, predictions (attack_type
only)")
# Cell 15: build probas dict per target where possible
print("\nCollecting predict_proba per model/target if available...")
proba_available = defaultdict(dict)
target_classes = {t: sorted(y[t].unique()) for t in TARGET_COLS}
probas = defaultdict(list)
for name, pipe in [Link]():
try:
mo = pipe.named_steps["clf"]
X_trans = pipe.named_steps["pre"].transform(X_test)
for i, target in enumerate(TARGET_COLS):
65
est = mo.estimators_[i]
has_proba = hasattr(est, "predict_proba")
proba_available[name][target] = has_proba
if has_proba:
p = est.predict_proba(X_trans)
probas[target].append((est.classes_, p))
except Exception as e:
print(f"Could not extract predict_proba from model {name}: {e}")
# Cell 16: Build ensemble_preds DataFrame per-target (soft voting where possible,
else majority)
ensemble_preds = [Link](index=X_test.index, columns=TARGET_COLS)
for target in TARGET_COLS:
if len(probas[target]) > 0:
classes = target_classes[target]
avg_proba = [Link]((len(X_test), len(classes)), dtype=float)
count_models = 0
for cls_order, proba_array in probas[target]:
aligned = [Link]((len(X_test), len(classes)), dtype=float)
for j, c in enumerate(cls_order):
if c in classes:
idx = [Link](c)
aligned[:, idx] = proba_array[:, j]
avg_proba += aligned
count_models += 1
if count_models > 0:
avg_proba /= count_models
chosen = [classes[i] for i in [Link](avg_proba, axis=1)]
ensemble_preds[target] = chosen
print(f"Ensemble (soft) used for target {target} with {count_models}
models' probabilities.")
continue
# fallback majority vote
66
hard_votes = []
for name, preds_df in [Link]():
hard_votes.append(preds_df[target].values)
hard_votes = [Link](hard_votes).T
majority = []
for row in hard_votes:
counts = Counter(row)
majority_label, _ = counts.most_common(1)[0]
[Link](majority_label)
ensemble_preds[target] = majority
print(f"Ensemble (hard majority) used for target {target}.")
from [Link] import classification_report, accuracy_score
print("\n=== Ensemble evaluation (attack_type only) ===")
true_attack = y_test_df["attack_type"]
pred_attack = ensemble_preds["attack_type"]
print(classification_report(true_attack, pred_attack, digits=4))
print("Accuracy:", accuracy_score(true_attack, pred_attack))
# Cell 18: Save ensemble predictions CSV (both targets)
ensemble_preds.to_csv(Path(OUT_DIR)/"ensemble_predictions_test.csv",
index=False)
print("Saved ensemble test predictions to",
Path(OUT_DIR)/"ensemble_predictions_test.csv")
# Cell 19: Save [Link]
meta = {
"models_saved": {name: str(Path(OUT_DIR)/f"{name}.joblib") for name in
[Link]()},
"targets": TARGET_COLS,
"feature_columns": [Link]()
}
import json
67
with open(Path(OUT_DIR)/"[Link]", "w") as f:
[Link](meta, f, indent=2)
print("Saved [Link]")
# Cell 20: Print a short summary of artifacts
print("Artifacts in OUT_DIR:")
for p in Path(OUT_DIR).glob("*"):
print(" -", [Link])
# Cell 21: Evaluate attack_type only (accuracy + classification report)
from [Link] import accuracy_score, classification_report
true_attack = y_test_df["attack_type"]
pred_attack = ensemble_preds["attack_type"]
print("Ensemble (attack_type) Accuracy:", accuracy_score(true_attack,
pred_attack))
# Robust Cell: rebuild predictions for attack_type only, compute accuracies, show
acc_df
from [Link] import accuracy_score
import pandas as pd
# 1) Ensure we have trained or saved models to predict with
if 'trained' not in globals() or not trained:
# try to load from disk
trained = {}
for name in ["random_forest", "gradient_boosting", "logistic_regression"]:
p = Path(OUT_DIR)/f"{name}.joblib"
if [Link]():
try:
trained[name] = [Link](p)
print(f"Loaded {name} from {p}")
except Exception as e:
print(f"Failed to load {p}: {e}")
elif 'models' in globals() and name in models:
68