#!/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égorie Score (%) Note URLs Totales Ré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 += """
URL Réputation Action Attendue Statut Correct
{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)