Spaces:
Sleeping
Sleeping
| from pathlib import Path | |
| import joblib | |
| import numpy as np | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| app = FastAPI(title="Web Attack Detection API") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Assuming models are in the same directory as the script for deployment | |
| # For Colab, you might need to adjust paths or upload files. | |
| MODEL_PATH = "web_attack_detection_model.pkl" | |
| VECTORIZER_PATH = "url_vectorizer.pkl" | |
| class InputData(BaseModel): | |
| features: list[float] # This assumes features are already numerical | |
| # If you want to input URLs, you'd change this to: | |
| # url: str | |
| try: | |
| model = joblib.load(MODEL_PATH) | |
| vectorizer = joblib.load(VECTORIZER_PATH) | |
| EXPECTED_FEATURES = len(vectorizer.get_feature_names_out()) | |
| except Exception as ex: | |
| raise RuntimeError(f"Model or vectorizer failed to load: {ex}") from ex | |
| def home() -> dict[str, str | int]: | |
| return { | |
| "message": "Web attack detection model running", | |
| "expected_features": EXPECTED_FEATURES, | |
| } | |
| def health() -> dict[str, str]: | |
| return {"status": "ok"} | |
| def predict(data: InputData) -> dict[str, int | float | str]: | |
| # If InputData was 'url: str', you'd do: | |
| # x = vectorizer.transform([data.url]).toarray() | |
| if len(data.features) != EXPECTED_FEATURES: | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"Expected {EXPECTED_FEATURES} features, but got {len(data.features)}", | |
| ) | |
| try: | |
| x = np.asarray(data.features, dtype=np.float64).reshape(1, -1) | |
| if not np.isfinite(x).all(): | |
| raise HTTPException(status_code=400, detail="Features contain NaN or Inf") | |
| pred = int(model.predict(x)[0]) | |
| # IsolationForest predicts -1 for anomalies (attacks), 1 for normal | |
| result = "attack_detected" if pred == -1 else "normal_request" | |
| response: dict[str, int | float | str] = { | |
| "prediction": result, | |
| "raw_prediction": pred, | |
| } | |
| # IsolationForest does not have predict_proba, use decision_function for anomaly score | |
| # Lower scores typically mean more anomalous | |
| anomaly_score = float(model.decision_function(x)[0]) | |
| response["anomaly_score"] = anomaly_score | |
| return response | |
| except HTTPException: | |
| raise | |
| except Exception as ex: | |
| raise HTTPException(status_code=500, detail=f"Prediction failed: {ex}") from ex |