dofbi commited on
Commit
89c8ad5
·
1 Parent(s): a2e0997

♻️ refactor (api): Flask to FastAPI

Browse files
Files changed (9) hide show
  1. .env.example +3 -0
  2. .gitignore +9 -0
  3. Dockerfile +14 -0
  4. README.md +163 -1
  5. analyzer.py +189 -0
  6. app.py +34 -0
  7. config.py +12 -0
  8. models.py +4 -0
  9. requirements.txt +6 -0
.env.example ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ OPENROUTER_API_KEY=votre_clé_api_ici
2
+ API_URL=https://openrouter.ai/api/v1/chat/completions
3
+ MODEL=deepseek/deepseek-chat:free
.gitignore ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ .env
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ env/
8
+ venv/
9
+ *.log
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+
7
+ WORKDIR /app
8
+
9
+ COPY --chown=user ./requirements.txt requirements.txt
10
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
+
12
+ COPY --chown=user . /app
13
+
14
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -9,4 +9,166 @@ license: mit
9
  short_description: 'AfricTivistes Digital Security Audit '
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  short_description: 'AfricTivistes Digital Security Audit '
10
  ---
11
 
12
+ # API d'Analyse d'Audit de Sécurité
13
+
14
+ API Flask permettant d'analyser automatiquement les réponses à un audit de sécurité et de générer des recommandations via l'intelligence artificielle.
15
+
16
+ ## Présentation
17
+
18
+ Cette API utilise l'intelligence artificielle pour analyser les réponses fournies lors d'un audit de sécurité informatique. Elle identifie automatiquement le type d'audit, évalue le niveau de sécurité global, détecte les points forts et les vulnérabilités, puis génère des recommandations personnalisées.
19
+
20
+ ## Fonctionnalités
21
+
22
+ - **Détection automatique du type d'audit** (complet ou spécifique à un domaine)
23
+ - **Analyse des réponses** par IA spécialisée en cybersécurité
24
+ - **Évaluation du niveau de sécurité** avec score sur 10
25
+ - **Identification des points forts** et leur importance
26
+ - **Détection des vulnérabilités** et classification par niveau de risque
27
+ - **Génération de recommandations détaillées** pour remédier aux failles
28
+ - **Production d'un résumé** de la situation et des étapes prioritaires
29
+
30
+ ## Prérequis
31
+
32
+ - Python 3.7+
33
+ - Bibliothèques: Flask, Requests
34
+ - Clé API OpenRouter (ou autre fournisseur de modèles LLM)
35
+
36
+ ## Installation
37
+
38
+ 1. Clonez le dépôt:
39
+ ```bash
40
+ git clone https://github.com/AfricTivistes/adisa.git
41
+ cd adisa
42
+ ```
43
+
44
+ 2. Installez les dépendances:
45
+ ```bash
46
+ pip install -r requirements.txt
47
+ ```
48
+
49
+ 3. Configurez votre clé API dans `app.py` ou via une variable d'environnement.
50
+
51
+ ## Utilisation
52
+
53
+ ### Démarrer l'API
54
+
55
+ ```bash
56
+ uvicorn app:app --host 0.0.0.0 --port 7860
57
+ ```
58
+
59
+ L'API sera accessible par défaut sur `http://localhost:7860`.
60
+
61
+ ### Endpoints
62
+
63
+ #### 1. Analyse d'audit
64
+
65
+ **Endpoint**: `POST /api/analyze`
66
+
67
+ **Description**: Analyse les réponses d'un audit de sécurité et génère une évaluation complète.
68
+
69
+ **Format de requête**:
70
+ ```json
71
+ {
72
+ "Question 1": "Réponse 1",
73
+ "Question 2": "Réponse 2",
74
+ "Comment gérez-vous les mots de passe?": "Nous utilisons un gestionnaire de mots de passe d'entreprise",
75
+ ...
76
+ }
77
+ ```
78
+
79
+ **Format de réponse**:
80
+ ```json
81
+ {
82
+ "status": "success",
83
+ "audit_type": "Audit spécifique: Politique de mots de passe",
84
+ "evaluation": {
85
+ "score": 7,
86
+ "level": "Bon"
87
+ },
88
+ "strengths": [
89
+ {
90
+ "text": "Utilisation d'un gestionnaire de mots de passe centralisé",
91
+ "rating": "Fort"
92
+ },
93
+ ...
94
+ ],
95
+ "weaknesses": [
96
+ {
97
+ "text": "Absence de politique de changement régulier des mots de passe",
98
+ "rating": "Moyen"
99
+ },
100
+ ...
101
+ ],
102
+ "recommendations": [
103
+ "Mettre en place une authentification à deux facteurs pour tous les services critiques",
104
+ "Automatiser la détection des mots de passe faibles",
105
+ ...
106
+ ],
107
+ "summary": "La gestion des mots de passe présente un bon niveau général mais nécessite des améliorations..."
108
+ }
109
+ ```
110
+
111
+ #### 2. Vérification de santé
112
+
113
+ **Endpoint**: `GET /api/health`
114
+
115
+ **Description**: Vérifie que l'API est opérationnelle.
116
+
117
+ **Format de réponse**:
118
+ ```json
119
+ {
120
+ "status": "ok",
121
+ "message": "API d'analyse d'audit de sécurité opérationnelle"
122
+ }
123
+ ```
124
+
125
+ ## Personnalisation
126
+
127
+ ### Modification du modèle d'IA
128
+
129
+ Vous pouvez modifier le modèle utilisé en changeant les paramètres dans le constructeur de la classe `SecurityAuditAnalyzer`:
130
+
131
+ ```python
132
+ security_analyzer = SecurityAuditAnalyzer(
133
+ api_key="votre-clé-api",
134
+ model="autre-modèle"
135
+ )
136
+ ```
137
+
138
+ ### Types d'audits supportés
139
+
140
+ L'API détecte automatiquement plusieurs domaines de la cybersécurité:
141
+
142
+ - Politique de mots de passe
143
+ - Sécurité des réseaux
144
+ - Gestion des accès
145
+ - Protection des données
146
+ - Sécurité physique
147
+ - Formation et sensibilisation
148
+ - Gestion des incidents
149
+ - Conformité réglementaire
150
+
151
+ ## Architecture du code
152
+
153
+ - `app.py` : Point d'entrée de l'application Flask et définition des routes
154
+ - `SecurityAuditAnalyzer` : Classe principale pour l'analyse des audits
155
+ - `analyze_responses()` : Méthode principale d'analyse
156
+ - `_build_prompt()` : Construction du prompt pour l'IA
157
+ - `_detect_audit_type()` : Détection automatique du type d'audit
158
+ - `_parse_analysis()` : Extraction des données structurées
159
+
160
+ ## Sécurité
161
+
162
+ ⚠️ **Attention**: Ne stockez jamais votre clé API directement dans le code en production. Utilisez plutôt des variables d'environnement ou un fichier de configuration sécurisé.
163
+
164
+ ## Exemple d'utilisation avec cURL
165
+
166
+ ```bash
167
+ curl -X POST http://localhost:7860/api/analyze \
168
+ -H "Content-Type: application/json" \
169
+ -d '{
170
+ "Avez-vous une politique de mots de passe?": "Oui, nous exigeons des mots de passe d'au moins 8 caractères",
171
+ "Utilisez-vous l'authentification à deux facteurs?": "Seulement pour les comptes administrateurs",
172
+ "À quelle fréquence les mots de passe sont-ils changés?": "Il n'y a pas d'obligation de changement"
173
+ }'
174
+ ```
analyzer.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import re
3
+
4
+ class SecurityAuditAnalyzer:
5
+ def __init__(self, api_key, model="deepseek/deepseek-chat:free", api_url="https://openrouter.ai/api/v1/chat/completions"):
6
+ self.api_url = api_url
7
+ self.api_key = api_key
8
+ self.model = model
9
+ self.headers = {
10
+ "Content-Type": "application/json",
11
+ "Authorization": f"Bearer {self.api_key}",
12
+ "HTTP-Referer": "https://security-audit-api.com",
13
+ "X-Title": "Security Audit API"
14
+ }
15
+
16
+ def analyze_responses(self, responses):
17
+ """Analyse les réponses d'un audit et retourne une évaluation structurée."""
18
+ prompt = self._build_prompt(responses)
19
+ payload = {
20
+ "model": self.model,
21
+ "messages": [
22
+ {"role": "system", "content": self._get_system_prompt()},
23
+ {"role": "user", "content": prompt}
24
+ ],
25
+ "temperature": 0.5
26
+ }
27
+ try:
28
+ response = requests.post(self.api_url, headers=self.headers, json=payload)
29
+ response.raise_for_status()
30
+ result = response.json()
31
+ analysis = result['choices'][0]['message']['content']
32
+ return self._parse_analysis(analysis)
33
+ except requests.RequestException as e:
34
+ return {
35
+ "error": str(e),
36
+ "status": "error",
37
+ "evaluation": {"score": 0, "level": "Inconnu"},
38
+ "strengths": [], "weaknesses": [], "recommendations": [],
39
+ "summary": "Une erreur s'est produite lors de l'analyse."
40
+ }
41
+
42
+ def _get_system_prompt(self):
43
+ return """Vous êtes un expert en cybersécurité spécialisé dans l'analyse des audits de sécurité.
44
+ Votre tâche est d'analyser les réponses fournies à un audit de sécurité et de produire une évaluation détaillée.
45
+
46
+ Adaptez votre analyse au type d'audit fourni (audit complet ou audit spécifique à un domaine).
47
+ Identifiez automatiquement le domaine spécifique de l'audit si les questions sont concentrées sur un sujet particulier
48
+ (ex: sécurité des réseaux, politique de mots de passe, gestion des données, etc.).
49
+
50
+ Structurez toujours votre réponse avec les balises suivantes:
51
+ <AUDIT_TYPE>Type de l'audit identifié</AUDIT_TYPE>
52
+ <EVALUATION>Score sur 10 et niveau (Critique, Faible, Moyen, Bon, Excellent)</EVALUATION>
53
+ <POINTS_FORTS>Liste des points forts avec leur niveau d'importance (Fort, Moyen, Faible)</POINTS_FORTS>
54
+ <FAILLES>Liste des vulnérabilités identifiées avec leur niveau de risque (Critique, Élevé, Moyen, Faible)</FAILLES>
55
+ <RECOMMANDATIONS>Liste des recommandations détaillées pour chaque faille identifiée</RECOMMANDATIONS>
56
+ <RESUME>Résumé concis de la situation générale de sécurité et prochaines étapes prioritaires</RESUME>
57
+ """
58
+
59
+ def _build_prompt(self, responses):
60
+ audit_type = self._detect_audit_type(responses)
61
+ prompt = f"Type d'audit détecté: {audit_type}\n\n"
62
+ prompt += "Voici les réponses à un audit de sécurité numérique:\n\n"
63
+ for question, answer in responses.items():
64
+ prompt += f"Question: {question}\nRéponse: {answer}\n\n"
65
+ prompt += """
66
+ Analysez ces réponses et fournissez:
67
+ 1. Le type d'audit que vous avez identifié
68
+ 2. Une évaluation globale du niveau de sécurité (score sur 10 et niveau: Critique, Faible, Moyen, Bon, Excellent)
69
+ 3. Les points forts de la sécurité actuelle (listez-les avec leur importance: Fort, Moyen, Faible)
70
+ 4. Les vulnérabilités identifiées (listez-les avec leur niveau de risque: Critique, Élevé, Moyen, Faible)
71
+ 5. Des recommandations détaillées pour résoudre chaque vulnérabilité
72
+ 6. Un résumé général de la situation actuelle et prochaines étapes prioritaires
73
+
74
+ Formatez votre réponse avec les sections clairement délimitées par les balises spécifiées.
75
+ """
76
+ return prompt
77
+
78
+ def _detect_audit_type(self, responses):
79
+ domains = {
80
+ "Politique de mots de passe": ["mot de passe", "password", "authentification", "connexion"],
81
+ "Sécurité des réseaux": ["réseau", "network", "firewall", "pare-feu", "vpn", "wifi", "routeur"],
82
+ "Gestion des accès": ["accès", "access", "droits", "permission", "privilege", "utilisateur", "user"],
83
+ "Protection des données": ["donnée", "data", "chiffrement", "encryption", "backup", "sauvegarde"],
84
+ "Sécurité physique": ["physique", "physical", "bâtiment", "building", "local", "vol", "theft"],
85
+ "Formation et sensibilisation": ["formation", "training", "sensibilisation", "awareness", "employé"],
86
+ "Gestion des incidents": ["incident", "crise", "réponse", "response", "attaque", "breach"],
87
+ "Conformité réglementaire": ["rgpd", "gdpr", "conformité", "compliance", "régulation", "legal"]
88
+ }
89
+ domain_counts = {domain: 0 for domain in domains}
90
+ for question in responses.keys():
91
+ question_lower = question.lower()
92
+ for domain, keywords in domains.items():
93
+ for keyword in keywords:
94
+ if keyword.lower() in question_lower:
95
+ domain_counts[domain] += 1
96
+ main_domains = sorted(domain_counts.items(), key=lambda x: x[1], reverse=True)
97
+ if len(main_domains) > 3 and main_domains[0][1] > 0 and main_domains[1][1] > 0 and main_domains[2][1] > 0:
98
+ return "Audit de sécurité complet"
99
+ elif main_domains[0][1] > 0:
100
+ return f"Audit spécifique: {main_domains[0][0]}"
101
+ else:
102
+ return "Audit de sécurité général"
103
+
104
+ def _parse_analysis(self, analysis):
105
+ result = {
106
+ "status": "success",
107
+ "audit_type": self._extract_between(analysis, "<AUDIT_TYPE>", "</AUDIT_TYPE>"),
108
+ "evaluation": {},
109
+ "strengths": [],
110
+ "weaknesses": [],
111
+ "recommendations": [],
112
+ "summary": self._extract_between(analysis, "<RESUME>", "</RESUME>").strip()
113
+ }
114
+ eval_text = self._extract_between(analysis, "<EVALUATION>", "</EVALUATION>")
115
+ score_match = re.search(r'(\d+)[\/\s]*10', eval_text)
116
+ result["evaluation"]["score"] = int(score_match.group(1)) if score_match else 0
117
+
118
+ level_keywords = ["Critique", "Faible", "Moyen", "Bon", "Excellent"]
119
+ for keyword in level_keywords:
120
+ if keyword.lower() in eval_text.lower():
121
+ result["evaluation"]["level"] = keyword
122
+ break
123
+ else:
124
+ result["evaluation"]["level"] = "Non spécifié"
125
+
126
+ strengths_text = self._extract_between(analysis, "<POINTS_FORTS>", "</POINTS_FORTS>")
127
+ result["strengths"] = self._extract_rated_items(strengths_text, ["Fort", "Moyen", "Faible"])
128
+
129
+ weaknesses_text = self._extract_between(analysis, "<FAILLES>", "</FAILLES>")
130
+ result["weaknesses"] = self._extract_rated_items(weaknesses_text, ["Critique", "Élevé", "Moyen", "Faible"])
131
+
132
+ recommendations_text = self._extract_between(analysis, "<RECOMMANDATIONS>", "</RECOMMANDATIONS>")
133
+ result["recommendations"] = self._extract_list_items(recommendations_text)
134
+
135
+ if not result["audit_type"]:
136
+ if "audit complet" in analysis.lower():
137
+ result["audit_type"] = "Audit de sécurité complet"
138
+ elif "audit spécifique" in analysis.lower():
139
+ specific_match = re.search(r'audit spécifique[:\s]*([\w\s]+)', analysis.lower())
140
+ if specific_match:
141
+ result["audit_type"] = f"Audit spécifique: {specific_match.group(1).strip().capitalize()}"
142
+ else:
143
+ result["audit_type"] = "Audit spécifique"
144
+ else:
145
+ result["audit_type"] = "Audit de sécurité général"
146
+
147
+ return result
148
+
149
+ def _extract_between(self, text, start_marker, end_marker):
150
+ try:
151
+ start = text.index(start_marker) + len(start_marker)
152
+ end = text.index(end_marker, start)
153
+ return text[start:end].strip()
154
+ except ValueError:
155
+ return ""
156
+
157
+ def _extract_list_items(self, text):
158
+ items = []
159
+ for line in text.split('\n'):
160
+ line = line.strip()
161
+ if line.startswith(('- ', '• ', '* ', '1. ', '2. ')) and len(line) > 2:
162
+ items.append(line[2:].strip())
163
+ elif line and not any(header in line.lower() for header in ["points", "failles", "recommandations"]):
164
+ items.append(line)
165
+ return [item for item in items if item]
166
+
167
+ def _extract_rated_items(self, text, ratings):
168
+ items = []
169
+ for line in text.split('\n'):
170
+ line = line.strip()
171
+ if not line:
172
+ continue
173
+ item = {"text": line, "rating": None}
174
+ for rating in ratings:
175
+ if f"{rating}:" in line.lower() or f"{rating} :" in line.lower():
176
+ parts = re.split(f"{rating}:", line, 1, flags=re.IGNORECASE)
177
+ if len(parts) > 1:
178
+ item["text"] = parts[1].strip()
179
+ item["rating"] = rating
180
+ break
181
+ if f"({rating})" in line.lower() or f"[{rating}]" in line.lower():
182
+ item["text"] = re.sub(rf"[\(\[]?{rating}[\)\]]?", "", line, flags=re.IGNORECASE).strip()
183
+ item["rating"] = rating
184
+ break
185
+ if item["rating"] is None and line.startswith(('- ', '• ', '* ')) and len(line) > 2:
186
+ item["text"] = line[2:].strip()
187
+ if item["text"] and not any(header in item["text"].lower() for header in ["points forts", "failles"]):
188
+ items.append(item)
189
+ return items
app.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from models import AuditResponses
3
+ from analyzer import SecurityAuditAnalyzer
4
+ from config import settings
5
+
6
+ app = FastAPI(title="Security Audit API", description="API pour analyser les réponses d'audits de sécurité")
7
+
8
+ # Initialisation de l'analyseur avec les paramètres de configuration
9
+ security_analyzer = SecurityAuditAnalyzer(
10
+ api_key=settings.OPENROUTER_API_KEY,
11
+ model=settings.MODEL,
12
+ api_url=settings.API_URL
13
+ )
14
+
15
+ @app.post("/api/analyze")
16
+ async def analyze_audit(responses: AuditResponses):
17
+ """
18
+ Analyse les réponses d'un audit de sécurité.
19
+
20
+ Args:
21
+ responses: Dictionnaire de questions/réponses
22
+
23
+ Returns:
24
+ Résultats structurés de l'analyse
25
+ """
26
+ analysis_result = security_analyzer.analyze_responses(responses.responses)
27
+ if analysis_result.get("status") == "error":
28
+ raise HTTPException(status_code=500, detail=analysis_result["error"])
29
+ return analysis_result
30
+
31
+ @app.get("/api/health")
32
+ async def health_check():
33
+ """Vérifie l'état de l'API."""
34
+ return {"status": "ok", "message": "API d'analyse d'audit de sécurité opérationnelle"}
config.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic_settings import BaseSettings
2
+
3
+ class Settings(BaseSettings):
4
+ OPENROUTER_API_KEY: str
5
+ API_URL: str = "https://openrouter.ai/api/v1/chat/completions"
6
+ MODEL: str = "deepseek/deepseek-chat:free"
7
+
8
+ class Config:
9
+ env_file = ".env"
10
+ env_file_encoding = "utf-8"
11
+
12
+ settings = Settings()
models.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+
3
+ class AuditResponses(BaseModel):
4
+ responses: dict[str, str]
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi==0.111.0
2
+ uvicorn==0.30.1
3
+ requests==2.32.3
4
+ pydantic==2.8.2
5
+ pydantic-settings==2.3.4
6
+ python-dotenv==1.0.1