Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..")) | |
| import json | |
| import joblib | |
| import pandas as pd | |
| import requests | |
| from dotenv import load_dotenv | |
| from fastapi import FastAPI, HTTPException | |
| import numpy as np | |
| from src.data.features import extract_features | |
| from src.api.schemas import CVERequest, PredictionResponse | |
| load_dotenv() | |
| api_key = os.getenv("NVD_API_KEY") | |
| # We load our model | |
| MODEL_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "models", "model_final_xgboost_v2.pkl") | |
| model = joblib.load(MODEL_PATH) | |
| # The categories created during training are specified | |
| # Otherwise, the model fails to compare the "sellers" variable and crashes | |
| CATEGORIES_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "models", "categories.json") | |
| with open(CATEGORIES_PATH) as f: | |
| CATEGORIES = json.load(f) | |
| # We define our columns and the Threshold | |
| CATEGORICAL_COLUMNS = [ | |
| "attack_vector", "attack_complexity", "privileges_required", | |
| "user_interaction", "confidentiality_impact", "integrity_impact", | |
| "availability_impact", "cwe", "scope", "component_type", "vendor" | |
| ] | |
| THRESHOLD = 0.9898 | |
| app = FastAPI(title="Prediction of CVE exploitation") | |
| # the function we developed in 04_API_pipeline.ipynb | |
| # Used to retrieve a CVE | |
| def fetch_single_cve(cve_id: str, timeout: int = 30) -> dict: | |
| url = "https://services.nvd.nist.gov/rest/json/cves/2.0" | |
| params = {"cveId": cve_id} | |
| headers = {"apiKey": api_key} | |
| r = requests.get(url, params=params, headers=headers, timeout=timeout) | |
| r.raise_for_status() | |
| data = r.json() | |
| vulnerabilities = data.get("vulnerabilities", []) | |
| if not vulnerabilities: | |
| raise ValueError(f"CVE {cve_id} introuvable dans NVD") | |
| return vulnerabilities[0] | |
| def root(): | |
| return { | |
| "message": "CVE Exploit Prediction API", | |
| "docs_url": "https://alvlt-cve-exploit-prediction.hf.space/docs" | |
| } | |
| # the other function developed in 04_API_pipeline.ipynb | |
| # Use to make prediction | |
| def predict(request: CVERequest): | |
| try: | |
| cve_raw = fetch_single_cve(request.cve_id) | |
| except ValueError as e: | |
| raise HTTPException(status_code=404, detail=str(e)) | |
| except requests.RequestException as e: | |
| raise HTTPException(status_code=502, detail=f"Erreur NVD: {e}") | |
| features = extract_features(cve_raw) | |
| features["description_length_reconstructed"] = features["description_length"] | |
| feature_columns = model.feature_names_in_ | |
| X = pd.DataFrame([features])[list(feature_columns)] | |
| # The category type is mandatory for categorical variables | |
| for col in CATEGORICAL_COLUMNS: | |
| if col in X.columns: | |
| X[col] = pd.Categorical(X[col], categories=CATEGORIES[col]) | |
| # Converts None values to NaN | |
| NUMERIC_COLUMNS = ["base_score", "exploitability_score", "impact_score"] | |
| for col in NUMERIC_COLUMNS: | |
| if col in X.columns: | |
| X[col] = pd.to_numeric(X[col], errors="coerce") | |
| proba = model.predict_proba(X)[:, 1][0] | |
| return PredictionResponse( | |
| cve_id=request.cve_id, | |
| probability=float(proba), | |
| prediction=int(proba >= THRESHOLD), | |
| vendor=features.get("vendor"), | |
| cwe=features.get("cwe"), | |
| ) |