Files

712 lines
30 KiB
Python

#!/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"""<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audit Firewall - Network Reputation Service</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f5f5f5; color: #333; line-height: 1.6; }}
.container {{ max-width: 1200px; margin: 0 auto; padding: 20px; }}
h1 {{ text-align: center; color: #2c3e50; margin-bottom: 10px; font-size: 2.5em; }}
.subtitle {{ text-align: center; color: #666; margin-bottom: 30px; font-size: 1.1em; }}
.section {{ background: white; margin: 20px 0; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
.section h2 {{ color: #2c3e50; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #3498db; }}
/* Score global */
.global-score {{ display: flex; align-items: center; justify-content: center; margin: 30px 0; }}
.score-box {{ background-color: {global_color}; color: white; width: 150px; height: 150px; border-radius: 15px; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 4px 15px rgba(0,0,0,0.2); margin-right: 40px; }}
.score-grade {{ font-size: 3em; font-weight: bold; }}
.score-percent {{ font-size: 1.5em; margin-top: 5px; }}
.score-label {{ font-size: 1em; margin-top: 5px; opacity: 0.9; }}
.stats {{ text-align: left; }}
.stats div {{ margin: 8px 0; font-size: 1.2em; }}
/* Tableau des scores */
table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
th, td {{ padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }}
th {{ background-color: #3498db; color: white; font-weight: bold; }}
tr:nth-child(even) {{ background-color: #f9f9f9; }}
tr:hover {{ background-color: #e8f4fd; }}
.grade-cell {{ font-weight: bold; color: white; text-align: center; border-radius: 5px; }}
/* Catégories en grille */
.categories-grid {{ display: flex; flex-wrap: wrap; gap: 20px; margin: 20px 0; }}
.category-item {{ flex: 1; min-width: 280px; max-width: calc(25% - 15px); background: white; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); overflow: hidden; }}
.category-header {{ padding: 15px; cursor: pointer; transition: background-color 0.3s; }}
.category-header:hover {{ background-color: #f8f9fa; }}
.category-title {{ font-size: 1.2em; font-weight: bold; margin-bottom: 8px; color: #2c3e50; }}
.category-score {{ display: inline-block; padding: 5px 15px; border-radius: 20px; color: white; font-weight: bold; font-size: 0.9em; }}
.category-content {{ display: none; padding: 0 15px 15px; }}
.category-content.active {{ display: block; animation: slideDown 0.3s ease; }}
/* Animations */
@keyframes slideDown {{ from {{ opacity: 0; transform: translateY(-10px); }} to {{ opacity: 1; transform: translateY(0); }} }}
/* Tables détaillées */
.detail-table {{ font-size: 0.9em; }}
.detail-table th {{ font-size: 0.8em; }}
.correct {{ background-color: #d4edda !important; color: #155724; }}
.incorrect {{ background-color: #f8d7da !important; color: #721c24; }}
/* Barème */
.grading-scale {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 10px; }}
.grade-item {{ display: flex; align-items: center; padding: 10px; border-radius: 8px; background-color: #f8f9fa; }}
.grade-badge {{ width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-weight: bold; color: white; margin-right: 15px; }}
/* Responsive */
@media (max-width: 768px) {{
.global-score {{ flex-direction: column; }}
.score-box {{ margin-right: 0; margin-bottom: 20px; }}
.category-item {{ max-width: 100%; }}
}}
/* Bouton toggle */
.toggle-btn {{ float: right; font-size: 1.2em; transition: transform 0.3s; }}
.toggle-btn.active {{ transform: rotate(180deg); }}
</style>
</head>
<body>
<div class="container">
<h1>🛡️ Audit Firewall - Network Reputation Service</h1>
<p class="subtitle">Rapport généré le {current_time}</p>
<!-- Résumé Exécutif -->
<div class="section">
<h2>📊 Résumé Exécutif</h2>
<div class="global-score">
<div class="score-box">
<div class="score-grade">{global_grade}</div>
<div class="score-percent">{global_score}%</div>
<div class="score-label">Score Global</div>
</div>
<div class="stats">
<div><strong>📋 Total des URLs testées :</strong> {len(results)}</div>
<div><strong>✅ URLs correctement filtrées :</strong> {sum(1 for r in results if r['is_correct'])}</div>
<div><strong>🎯 Taux de réussite :</strong> {round((sum(1 for r in results if r['is_correct']) / len(results)) * 100, 2)}%</div>
<div><strong>📅 Date du test :</strong> {datetime.now().strftime('%d/%m/%Y %H:%M')}</div>
</div>
</div>
</div>
<!-- Scores par Catégorie -->
<div class="section">
<h2>📈 Scores par Catégorie</h2>
<table>
<thead>
<tr>
<th>Catégorie</th>
<th>Score (%)</th>
<th>Note</th>
<th>URLs Totales</th>
<th>Résultats Corrects</th>
</tr>
</thead>
<tbody>"""
# Ajout des lignes du tableau
for cat_score in category_scores:
html_content += f"""
<tr>
<td><strong>{cat_score['category']}</strong></td>
<td>{cat_score['score']}%</td>
<td><span class="grade-cell" style="background-color: {cat_score['color']}; padding: 5px 10px; border-radius: 5px;">{cat_score['grade']}</span></td>
<td>{cat_score['total_urls']}</td>
<td>{cat_score['correct_results']}</td>
</tr>"""
html_content += """
</tbody>
</table>
</div>
<!-- Détail des Tests par Catégorie -->
<div class="section">
<h2>🔍 Détail des Tests par Catégorie</h2>
<div class="categories-grid">"""
# Ajout des catégories
for idx, cat_score in enumerate(category_scores, 1):
html_content += f"""
<div class="category-item">
<div class="category-header" onclick="toggleCategory('cat-{idx}')">
<div class="category-title">{cat_score['category']} <span class="toggle-btn" id="btn-cat-{idx}">▼</span></div>
<span class="category-score" style="background-color: {cat_score['color']};">{cat_score['grade']} - {cat_score['score']}%</span>
</div>
<div class="category-content" id="cat-{idx}">
<table class="detail-table">
<thead>
<tr>
<th>URL</th>
<th>Réputation</th>
<th>Action Attendue</th>
<th>Statut</th>
<th>Correct</th>
</tr>
</thead>
<tbody>"""
# 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"""
<tr class="{row_class}">
<td title="{result['url']}">{url_display}</td>
<td>{result['reputation']}</td>
<td>{result['expected_action']}</td>
<td>{result['status']}</td>
<td>{correct_text}</td>
</tr>"""
html_content += """
</tbody>
</table>
</div>
</div>"""
html_content += """
</div>
</div>
<!-- Barème de Notation -->
<div class="section">
<h2>📏 Barème de Notation</h2>
<div class="grading-scale">"""
# 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"""
<div class="grade-item">
<div class="grade-badge" style="background-color: {grade_info['color']};">{grade_info['grade']}</div>
<div>
<strong>{grade_info['score']}</strong><br>
<span style="color: #666;">{grade_info['interpretation']}</span>
</div>
</div>"""
html_content += """
</div>
</div>
</div>
<script>
function toggleCategory(categoryId) {
const content = document.getElementById(categoryId);
const btn = document.getElementById('btn-' + categoryId);
if (content.classList.contains('active')) {
content.classList.remove('active');
btn.classList.remove('active');
content.style.display = 'none';
} else {
content.classList.add('active');
btn.classList.add('active');
content.style.display = 'block';
}
}
// Animation au chargement
document.addEventListener('DOMContentLoaded', function() {
const items = document.querySelectorAll('.category-item');
items.forEach((item, index) => {
setTimeout(() => {
item.style.opacity = '1';
item.style.transform = 'translateY(0)';
}, index * 100);
});
});
</script>
</body>
</html>"""
# É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)