#!/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"""
Rapport généré le {current_time}
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']} |
URL | Réputation | Action Attendue | Statut | Correct |
---|---|---|---|---|
{url_display} | {result['reputation']} | {result['expected_action']} | {result['status']} | {correct_text} |