From 6f6c1890f53656af253a03f0de07616bfb8f016d Mon Sep 17 00:00:00 2001 From: Hubert Cornet Date: Thu, 4 Sep 2025 14:29:59 +0200 Subject: [PATCH] Add cybersecurity/Network Reputation Service/script-nrs.py --- .../Network Reputation Service/script-nrs.py | 712 ++++++++++++++++++ 1 file changed, 712 insertions(+) create mode 100644 cybersecurity/Network Reputation Service/script-nrs.py diff --git a/cybersecurity/Network Reputation Service/script-nrs.py b/cybersecurity/Network Reputation Service/script-nrs.py new file mode 100644 index 0000000..c7ae41e --- /dev/null +++ b/cybersecurity/Network Reputation Service/script-nrs.py @@ -0,0 +1,712 @@ +#!/usr/bin/env python3 +""" +Script d'Audit Firewall - Network Reputation Service +Auteur: Converti depuis PowerShell +Date: 04/09/2025 +Version: 1.3 (Python) +""" + +import json +import requests +import argparse +import sys +import os +from datetime import datetime, timezone +from pathlib import Path +from urllib.parse import urlparse +import time +import getpass +from typing import List, Dict, Any, Optional, Tuple +import math + +# Import des bibliothèques avec gestion des erreurs +try: + from colorama import init, Fore, Back, Style + init(autoreset=True) + COLORAMA_AVAILABLE = True +except ImportError: + COLORAMA_AVAILABLE = False + print("Attention: colorama n'est pas installé. Les couleurs ne seront pas disponibles.") + print("Installez avec: pip install colorama") + +try: + from tqdm import tqdm + TQDM_AVAILABLE = True +except ImportError: + TQDM_AVAILABLE = False + print("Attention: tqdm n'est pas installé. Les barres de progression ne seront pas disponibles.") + print("Installez avec: pip install tqdm") + +# Configuration globale +TIMEOUT_DEFAULT = 10 +GRADE_COLORS = { + 'A+': '#28a745', 'A': '#52b83a', 'B+': '#7ac92e', 'B': '#a3da23', + 'C+': '#cceb17', 'C': '#f5f90c', 'D+': '#f7d808', 'D': '#f9b604', + 'E+': '#fb9500', 'E': '#fd7300', 'F+': '#ff5100', 'F': '#dc3545' +} + +BLOCK_KEYWORDS = [ + "site bloqué", "access denied", "filtrage web", + "Access Denied", "Site Blocked", "blocked", "forbidden" +] + +BROWSER_HEADERS = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Language": "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7" +} + +def write_color_output(text: str, color: str = "WHITE") -> None: + """Affiche du texte en couleur si colorama est disponible""" + if not COLORAMA_AVAILABLE: + print(text) + return + + color_map = { + "RED": Fore.RED, + "GREEN": Fore.GREEN, + "YELLOW": Fore.YELLOW, + "BLUE": Fore.BLUE, + "CYAN": Fore.CYAN, + "MAGENTA": Fore.MAGENTA, + "WHITE": Fore.WHITE, + "GRAY": Fore.LIGHTBLACK_EX + } + + color_code = color_map.get(color.upper(), Fore.WHITE) + print(f"{color_code}{text}") + +def prerequisites() -> bool: + """Vérifie les prérequis du script""" + write_color_output("\n === Vérification des prérequis ===", "CYAN") + + # Vérification du fichier JSON + script_dir = Path(__file__).parent + json_file = script_dir / "file-nrs.json" + + if not json_file.exists(): + write_color_output("ERREUR: Le fichier 'file-nrs.json' n'existe pas!", "RED") + write_color_output("Veuillez télécharger ou créer le fichier depuis:", "YELLOW") + write_color_output("https://gitea.tips-of-mine.com/Tips-Of-Mine/Powershell/src/branch/main/cybersecurity/Network%20Reputation%20Service/file-nrs.json", "BLUE") + write_color_output("Le fichier doit être placé dans le même dossier que ce script.", "YELLOW") + return False + + write_color_output("✓ Fichier 'file-nrs.json' trouvé", "GREEN") + + # Vérification des modules Python requis + required_modules = { + 'requests': 'requests', + 'colorama': 'colorama', + 'tqdm': 'tqdm' + } + + missing_modules = [] + for module_name, pip_name in required_modules.items(): + try: + __import__(module_name) + write_color_output(f"✓ Module {module_name} disponible", "GREEN") + except ImportError: + write_color_output(f"⚠ Module {module_name} manquant", "YELLOW") + missing_modules.append(pip_name) + + if missing_modules: + write_color_output(f"Modules manquants (optionnels): {', '.join(missing_modules)}", "YELLOW") + write_color_output(f"Installez avec: pip install {' '.join(missing_modules)}", "YELLOW") + + return True + +def calculate_category_score(results: List[Dict]) -> float: + """Calcule le score d'une catégorie""" + if not results: + return 0.0 + + correct_results = sum(1 for result in results if result['is_correct']) + return round((correct_results / len(results)) * 100, 2) + +def convert_score_to_grade(score: float) -> str: + """Convertit un score en note""" + if score >= 95: return 'A+' + elif score >= 90: return 'A' + elif score >= 85: return 'B+' + elif score >= 80: return 'B' + elif score >= 75: return 'C+' + elif score >= 70: return 'C' + elif score >= 65: return 'D+' + elif score >= 60: return 'D' + elif score >= 55: return 'E+' + elif score >= 50: return 'E' + elif score >= 45: return 'F+' + else: return 'F' + +def get_url_status(url: str, expected_action: str, proxy_url: Optional[str] = None, + proxy_auth: Optional[Tuple[str, str]] = None, timeout: int = TIMEOUT_DEFAULT) -> Dict: + """Teste une URL et détermine si elle est bloquée ou autorisée""" + + result = { + 'url': url, + 'expected': expected_action, + 'actual_result': 'Indéterminé', + 'test_status': 'Échec', + 'score': None, + 'status_code': '', + 'details': '', + 'response_time': '' + } + + # Configuration des proxies + proxies = {} + if proxy_url: + proxies = { + 'http': proxy_url, + 'https': proxy_url + } + + # Configuration de l'authentification proxy + auth = None + if proxy_auth: + auth = proxy_auth + + start_time = time.time() + + try: + response = requests.get( + url, + headers=BROWSER_HEADERS, + timeout=timeout, + proxies=proxies, + auth=auth, + verify=True, + allow_redirects=True + ) + + response_time = round(time.time() - start_time, 2) + result['response_time'] = str(response_time) + result['status_code'] = str(response.status_code) + + # Vérification des mots-clés de blocage dans le contenu + keyword_found = any(keyword.lower() in response.text.lower() for keyword in BLOCK_KEYWORDS) + + if keyword_found: + result['actual_result'] = "Bloqué" + result['details'] = "Page de blocage détectée." + else: + result['actual_result'] = "Autorisé" + result['details'] = "Le site a été atteint sans blocage." + + except requests.exceptions.Timeout: + result['actual_result'] = "Bloqué (Timeout)" + result['details'] = f"La requête a expiré après {timeout}s, indiquant un blocage probable." + result['response_time'] = str(timeout) + + except requests.exceptions.ConnectionError: + result['actual_result'] = "Erreur de Connexion" + result['details'] = "Impossible de joindre le serveur" + + except requests.exceptions.RequestException as e: + result['actual_result'] = "Erreur Script" + result['details'] = f"Erreur inattendue: {str(e)}" + return result + + except Exception as e: + result['actual_result'] = "Erreur Script" + result['details'] = f"Erreur inattendue: {str(e)}" + return result + + # Détermination si le test est conforme + is_blocked = result['actual_result'] in ['Bloqué', 'Bloqué (Timeout)'] + should_be_blocked = expected_action == 'block' + + if (is_blocked and should_be_blocked) or (not is_blocked and not should_be_blocked): + result['test_status'] = "Conforme" + result['score'] = 1 + result['details'] += " (Résultat conforme à l'attendu)" + else: + result['test_status'] = "Non Conforme" + result['score'] = 0 + result['details'] += f" (NON CONFORME; Attendu: {expected_action}; Obtenu: {result['actual_result']})" + + return result + +def check_categories(categories: Dict, proxy_url: Optional[str] = None, + proxy_auth: Optional[Tuple[str, str]] = None, timeout: int = TIMEOUT_DEFAULT) -> List[Dict]: + """Teste toutes les catégories et URLs""" + + all_results = [] + categories_data = categories.get('categorie', []) + + write_color_output(f"\n=== Début du test de {len(categories_data)} catégories ===", "CYAN") + + # Barre de progression principale si tqdm est disponible + category_progress = None + if TQDM_AVAILABLE: + category_progress = tqdm(categories_data, desc="Catégories", unit="cat") + + for category_idx, category in enumerate(categories_data): + if not TQDM_AVAILABLE: + write_color_output(f"\n=== Test de la catégorie: {category['nom']} ({category_idx + 1}/{len(categories_data)}) ===", "CYAN") + + category_results = [] + urls = category.get('urls', []) + + # Barre de progression pour les URLs si tqdm est disponible + url_progress = None + if TQDM_AVAILABLE: + url_progress = tqdm(urls, desc=f"URLs de '{category['nom']}'", unit="url", leave=False) + + for url_idx, url_obj in enumerate(urls): + if not TQDM_AVAILABLE: + write_color_output(f"Test de: {url_obj['url']} ({url_idx + 1}/{len(urls)})", "YELLOW") + + result = get_url_status( + url_obj['url'], + url_obj['expected_action'], + proxy_url, + proxy_auth, + timeout + ) + + test_result = { + 'category': category['nom'], + 'category_id': category.get('id', ''), + 'url': url_obj['url'], + 'reputation': url_obj.get('reputation', ''), + 'expected_action': url_obj['expected_action'], + 'status': result['actual_result'], + 'status_code': result['status_code'], + 'response_time': result['response_time'], + 'error': result['details'], + 'score': result['score'], + 'is_correct': result['test_status'] == "Conforme" + } + + if not TQDM_AVAILABLE: + status_color = "GREEN" if test_result['is_correct'] else "RED" + write_color_output( + f" → Résultat: {test_result['status']} | Attendu: {test_result['expected_action']} | Correct: {test_result['is_correct']}", + status_color + ) + + category_results.append(test_result) + all_results.append(test_result) + + if url_progress: + url_progress.update(1) + + if url_progress: + url_progress.close() + + category_score = calculate_category_score(category_results) + category_grade = convert_score_to_grade(category_score) + + if not TQDM_AVAILABLE: + write_color_output(f"Score de la catégorie '{category['nom']}': {category_score}% (Note: {category_grade})", "MAGENTA") + + if category_progress: + category_progress.set_postfix({ + 'Score': f"{category_score}%", + 'Grade': category_grade + }) + category_progress.update(1) + + if category_progress: + category_progress.close() + + return all_results + +def generate_html_report(results: List[Dict], output_path: Path) -> None: + """Génère le rapport HTML""" + write_color_output("Génération du rapport HTML...", "YELLOW") + + # Calcul des scores par catégorie + categories_dict = {} + for result in results: + cat_name = result['category'] + if cat_name not in categories_dict: + categories_dict[cat_name] = [] + categories_dict[cat_name].append(result) + + category_scores = [] + for cat_name, cat_results in categories_dict.items(): + score = calculate_category_score(cat_results) + grade = convert_score_to_grade(score) + + category_scores.append({ + 'category': cat_name, + 'score': score, + 'grade': grade, + 'color': GRADE_COLORS[grade], + 'total_urls': len(cat_results), + 'correct_results': sum(1 for r in cat_results if r['is_correct']), + 'results': cat_results + }) + + # Score global + global_score = calculate_category_score(results) + global_grade = convert_score_to_grade(global_score) + global_color = GRADE_COLORS[global_grade] + + # Génération du HTML + current_time = datetime.now().strftime('%d/%m/%Y à %H:%M') + + html_content = f""" + + + + + Audit Firewall - Network Reputation Service + + + +
+

🛡️ Audit Firewall - Network Reputation Service

+

Rapport généré le {current_time}

+ + +
+

📊 Résumé Exécutif

+
+
+
{global_grade}
+
{global_score}%
+
Score Global
+
+
+
📋 Total des URLs testées : {len(results)}
+
✅ URLs correctement filtrées : {sum(1 for r in results if r['is_correct'])}
+
🎯 Taux de réussite : {round((sum(1 for r in results if r['is_correct']) / len(results)) * 100, 2)}%
+
📅 Date du test : {datetime.now().strftime('%d/%m/%Y %H:%M')}
+
+
+
+ + +
+

📈 Scores par Catégorie

+ + + + + + + + + + + """ + + # Ajout des lignes du tableau + for cat_score in category_scores: + html_content += f""" + + + + + + + """ + + html_content += """ + +
CatégorieScore (%)NoteURLs TotalesRésultats Corrects
{cat_score['category']}{cat_score['score']}%{cat_score['grade']}{cat_score['total_urls']}{cat_score['correct_results']}
+
+ + +
+

🔍 Détail des Tests par Catégorie

+
""" + + # Ajout des catégories + for idx, cat_score in enumerate(category_scores, 1): + html_content += f""" +
+
+
{cat_score['category']}
+ {cat_score['grade']} - {cat_score['score']}% +
+
+ + + + + + + + + + + """ + + # Ajout des résultats de chaque URL + for result in cat_score['results']: + row_class = "correct" if result['is_correct'] else "incorrect" + correct_text = "✅ Oui" if result['is_correct'] else "❌ Non" + url_display = result['url'][:30] + ("..." if len(result['url']) > 30 else "") + + html_content += f""" + + + + + + + """ + + html_content += """ + +
URLRéputationAction AttendueStatutCorrect
{url_display}{result['reputation']}{result['expected_action']}{result['status']}{correct_text}
+
+
""" + + html_content += """ +
+
+ + +
+

📏 Barème de Notation

+
""" + + # Ajout du barème de notation + grading_scale = [ + {'grade': 'A+', 'score': '95-100%', 'interpretation': 'Excellent / Parfait', 'color': '#28a745'}, + {'grade': 'A', 'score': '90-95%', 'interpretation': 'Très bon niveau de filtrage', 'color': '#52b83a'}, + {'grade': 'B+', 'score': '85-90%', 'interpretation': 'Très bon', 'color': '#7ac92e'}, + {'grade': 'B', 'score': '80-85%', 'interpretation': 'Bon, quelques ajustements nécessaires', 'color': '#a3da23'}, + {'grade': 'C+', 'score': '75-80%', 'interpretation': 'Assez bon', 'color': '#cceb17'}, + {'grade': 'C', 'score': '70-75%', 'interpretation': 'Moyen, lacunes importantes', 'color': '#f5f90c'}, + {'grade': 'D+', 'score': '65-70%', 'interpretation': 'Passable', 'color': '#f7d808'}, + {'grade': 'D', 'score': '60-65%', 'interpretation': 'Faible, filtrage inefficace', 'color': '#f9b604'}, + {'grade': 'E+', 'score': '55-60%', 'interpretation': 'Très faible', 'color': '#fb9500'}, + {'grade': 'E', 'score': '50-55%', 'interpretation': 'Insuffisant', 'color': '#fd7300'}, + {'grade': 'F+', 'score': '45-50%', 'interpretation': 'Critique', 'color': '#ff5100'}, + {'grade': 'F', 'score': '0-45%', 'interpretation': 'Très faible, action immédiate requise', 'color': '#dc3545'} + ] + + for grade_info in grading_scale: + html_content += f""" +
+
{grade_info['grade']}
+
+ {grade_info['score']}
+ {grade_info['interpretation']} +
+
""" + + html_content += """ +
+
+
+ + + +""" + + # Écriture du fichier HTML + try: + with open(output_path, 'w', encoding='utf-8') as f: + f.write(html_content) + write_color_output(f"✓ Rapport généré: {output_path}", "GREEN") + except Exception as e: + write_color_output(f"ERREUR: Impossible de générer le rapport: {str(e)}", "RED") + +def main(): + """Fonction principale""" + parser = argparse.ArgumentParser(description="Script d'Audit Firewall - Network Reputation Service") + parser.add_argument("--proxy-url", help="URL du proxy à utiliser (ex: 'http://proxy.domaine.com:8080')") + parser.add_argument("--proxy-auth", action="store_true", help="Utiliser l'authentification pour le proxy") + parser.add_argument("--timeout", type=int, default=TIMEOUT_DEFAULT, help="Délai d'attente en secondes pour chaque requête") + + args = parser.parse_args() + + write_color_output(""" +╔══════════════════════════════════════════════════════════════════════════════╗ +║ AUDIT FIREWALL - NETWORK REPUTATION SERVICE ║ +║ Version 1.3 (Python) ║ +╚══════════════════════════════════════════════════════════════════════════════╝ + """, "CYAN") + + # Vérification des prérequis + if not prerequisites(): + write_color_output("ERREUR: Les prérequis ne sont pas satisfaits. Arrêt du script.", "RED") + sys.exit(1) + + # Chargement du fichier JSON + try: + script_dir = Path(__file__).parent + json_file = script_dir / "file-nrs.json" + + with open(json_file, 'r', encoding='utf-8') as f: + categories = json.load(f) + + write_color_output(f"✓ Fichier JSON chargé avec {len(categories.get('categorie', []))} catégories", "GREEN") + except Exception as e: + write_color_output(f"ERREUR: Impossible de charger le fichier JSON: {str(e)}", "RED") + sys.exit(1) + + # Configuration de l'authentification proxy si nécessaire + proxy_auth = None + if args.proxy_auth and args.proxy_url: + write_color_output(f"Configuration de l'authentification pour le proxy {args.proxy_url}", "YELLOW") + username = input("Nom d'utilisateur: ") + password = getpass.getpass("Mot de passe: ") + proxy_auth = (username, password) + + # Création du dossier de sortie + current_time = datetime.now() + report_date = current_time.strftime("%A %d %B %Y-%H %M") + script_dir = Path(__file__).parent + reports_dir = script_dir / "Rapports" + output_dir = reports_dir / report_date + + reports_dir.mkdir(exist_ok=True) + output_dir.mkdir(exist_ok=True) + + write_color_output(f"\nDossier de sortie: {output_dir}", "YELLOW") + + # Début des tests + write_color_output("\n=== DÉBUT DES TESTS ===", "GREEN") + start_time = time.time() + + all_results = check_categories( + categories, + args.proxy_url, + proxy_auth, + args.timeout + ) + + end_time = time.time() + duration = end_time - start_time + duration_str = f"{int(duration // 3600):02d}:{int((duration % 3600) // 60):02d}:{int(duration % 60):02d}" + + write_color_output("\n=== RÉSULTATS FINAUX ===", "GREEN") + write_color_output(f"\nTemps total d'exécution: {duration_str}", "YELLOW") + write_color_output(f"Total des URLs testées: {len(all_results)}", "YELLOW") + + # Calcul du score global + global_score = calculate_category_score(all_results) + global_grade = convert_score_to_grade(global_score) + + write_color_output(f"\n\nScore global: {global_score}% - Note: {global_grade}", "MAGENTA") + + # Génération du rapport HTML + report_path = output_dir / "Audit_Firewall_Report.html" + generate_html_report(all_results, report_path) + + # Sauvegarde des résultats en JSON + json_results_path = output_dir / "Results.json" + try: + with open(json_results_path, 'w', encoding='utf-8') as f: + json.dump(all_results, f, indent=2, ensure_ascii=False) + write_color_output(f"✓ Résultats sauvegardés: {json_results_path}", "GREEN") + except Exception as e: + write_color_output(f"ERREUR: Impossible de sauvegarder les résultats: {str(e)}", "RED") + + write_color_output("\n=== AUDIT TERMINÉ ===", "GREEN") + write_color_output(f"\nRapport disponible à: {report_path}", "CYAN") + + # Ouverture automatique du rapport + try: + open_report = input("\nVoulez-vous ouvrir le rapport maintenant? (O/N): ").strip().lower() + if open_report in ['o', 'oui', 'y', 'yes']: + import webbrowser + webbrowser.open(f"file://{report_path.absolute()}") + except KeyboardInterrupt: + print("\nAu revoir!") + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + write_color_output("\n\nScript interrompu par l'utilisateur.", "YELLOW") + sys.exit(0) + except Exception as e: + write_color_output(f"ERREUR FATALE: {str(e)}", "RED") + import traceback + traceback.print_exc() + sys.exit(1) \ No newline at end of file