pmescan 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pmescan-0.1.0/.gitignore +25 -0
- pmescan-0.1.0/01-VISION.md +62 -0
- pmescan-0.1.0/02-UTILISATION.md +89 -0
- pmescan-0.1.0/03-CONCEPTION.md +184 -0
- pmescan-0.1.0/04-ETAPES.md +75 -0
- pmescan-0.1.0/CONTRIBUTING.md +108 -0
- pmescan-0.1.0/LICENSE +21 -0
- pmescan-0.1.0/PKG-INFO +268 -0
- pmescan-0.1.0/README-mode-agressif.md +101 -0
- pmescan-0.1.0/README.md +234 -0
- pmescan-0.1.0/pmescan/__init__.py +6 -0
- pmescan-0.1.0/pmescan/autorisation.py +70 -0
- pmescan-0.1.0/pmescan/checks/__init__.py +1 -0
- pmescan-0.1.0/pmescan/checks/base.py +21 -0
- pmescan-0.1.0/pmescan/checks/cms.py +460 -0
- pmescan-0.1.0/pmescan/checks/dns_mail.py +176 -0
- pmescan-0.1.0/pmescan/checks/entetes.py +158 -0
- pmescan-0.1.0/pmescan/checks/exposes.py +118 -0
- pmescan-0.1.0/pmescan/checks/ports.py +170 -0
- pmescan-0.1.0/pmescan/checks/tls.py +252 -0
- pmescan-0.1.0/pmescan/cli.py +243 -0
- pmescan-0.1.0/pmescan/domaine.py +84 -0
- pmescan-0.1.0/pmescan/i18n/__init__.py +19 -0
- pmescan-0.1.0/pmescan/i18n/fr.yaml +607 -0
- pmescan-0.1.0/pmescan/modele.py +45 -0
- pmescan-0.1.0/pmescan/notation.py +34 -0
- pmescan-0.1.0/pmescan/rapport/__init__.py +5 -0
- pmescan-0.1.0/pmescan/rapport/generateur.py +164 -0
- pmescan-0.1.0/pmescan/rapport/modele.html +257 -0
- pmescan-0.1.0/pmescan/scanner.py +78 -0
- pmescan-0.1.0/pyproject.toml +79 -0
- pmescan-0.1.0/tests/__init__.py +0 -0
- pmescan-0.1.0/tests/conftest.py +171 -0
- pmescan-0.1.0/tests/fixtures/entetes_non_securises.json +10 -0
- pmescan-0.1.0/tests/fixtures/entetes_securises.json +15 -0
- pmescan-0.1.0/tests/test_autorisation.py +174 -0
- pmescan-0.1.0/tests/test_cli.py +576 -0
- pmescan-0.1.0/tests/test_cms.py +686 -0
- pmescan-0.1.0/tests/test_dns_mail.py +231 -0
- pmescan-0.1.0/tests/test_domaine.py +188 -0
- pmescan-0.1.0/tests/test_entetes.py +119 -0
- pmescan-0.1.0/tests/test_exposes.py +161 -0
- pmescan-0.1.0/tests/test_i18n.py +367 -0
- pmescan-0.1.0/tests/test_notation.py +68 -0
- pmescan-0.1.0/tests/test_ports.py +378 -0
- pmescan-0.1.0/tests/test_rapport.py +246 -0
- pmescan-0.1.0/tests/test_scanner.py +228 -0
- pmescan-0.1.0/tests/test_tls.py +216 -0
pmescan-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Environnements Python
|
|
2
|
+
.venv/
|
|
3
|
+
venv/
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
|
|
7
|
+
# Build / distribution
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
|
|
12
|
+
# Tests / outils
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
|
|
17
|
+
# Rapports générés
|
|
18
|
+
*.pdf
|
|
19
|
+
consent.log
|
|
20
|
+
|
|
21
|
+
# Éditeurs / OS
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# 01 — Vision : à quoi sert le projet
|
|
2
|
+
|
|
3
|
+
## Nom
|
|
4
|
+
|
|
5
|
+
**pmescan** — Scanner de sécurité pour PME francophones.
|
|
6
|
+
|
|
7
|
+
## Le problème
|
|
8
|
+
|
|
9
|
+
Les petites et moyennes entreprises (PME), en particulier au Sénégal et en Afrique
|
|
10
|
+
francophone, ont presque toutes une présence en ligne : site vitrine, boutique
|
|
11
|
+
WordPress, adresse e-mail professionnelle. Mais :
|
|
12
|
+
|
|
13
|
+
- Elles n'ont **pas d'équipe sécurité** ni de budget pour un audit à 2 000 €.
|
|
14
|
+
- Les outils existants (Mozilla Observatory, Nuclei, SSL Labs) sont **en anglais**,
|
|
15
|
+
**techniques**, et crachent une sortie **illisible pour un gérant**.
|
|
16
|
+
- Résultat : un certificat TLS expiré, un WordPress non à jour, une base de données
|
|
17
|
+
MySQL exposée sur Internet, ou l'absence de DMARC passent inaperçus — jusqu'à
|
|
18
|
+
l'incident (site piraté, usurpation d'e-mail, fuite de données clients).
|
|
19
|
+
|
|
20
|
+
## La solution
|
|
21
|
+
|
|
22
|
+
Un outil **gratuit et open source** qui scanne un site web et produit un **rapport
|
|
23
|
+
PDF en français clair**, compréhensible par un dirigeant non technique. Chaque faille
|
|
24
|
+
est traduite en **risque métier** et accompagnée d'une **action priorisée**.
|
|
25
|
+
|
|
26
|
+
Exemple de traduction :
|
|
27
|
+
|
|
28
|
+
| Au lieu de… | pmescan dit… |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `DMARC record not found` | « N'importe qui peut envoyer des e-mails en se faisant passer pour votre entreprise. » |
|
|
31
|
+
| `TLS 1.0 enabled` | « Votre site utilise un chiffrement obsolète qu'un attaquant peut casser. » |
|
|
32
|
+
| `Port 3306 open` | « Votre base de données est accessible depuis Internet. À fermer en urgence. » |
|
|
33
|
+
|
|
34
|
+
## Pour qui
|
|
35
|
+
|
|
36
|
+
1. **Gérants de PME** — veulent savoir en 30 secondes si leur site est à risque.
|
|
37
|
+
2. **Agences web et prestataires** — veulent un rapport en marque blanche pour leurs clients.
|
|
38
|
+
3. **Freelances sécurité / étudiants** — veulent un outil pédagogique et un point de départ.
|
|
39
|
+
|
|
40
|
+
## Ce que le projet n'est pas
|
|
41
|
+
|
|
42
|
+
- Ce n'est **pas un outil de piratage**. Le mode par défaut ne fait que lire des
|
|
43
|
+
informations publiques.
|
|
44
|
+
- Ce n'est **pas un pentest complet**. Il repère les failles courantes, pas les 0-day.
|
|
45
|
+
- Ce n'est **pas** un remplaçant d'un expert pour les gros systèmes critiques.
|
|
46
|
+
|
|
47
|
+
## Objectif de différenciation
|
|
48
|
+
|
|
49
|
+
Là où les concurrents s'arrêtent à la donnée technique brute, pmescan ajoute trois
|
|
50
|
+
choses qu'aucun ne réunit :
|
|
51
|
+
|
|
52
|
+
1. **Langue** : tout en français (et plus tard wolof, anglais).
|
|
53
|
+
2. **Vulgarisation** : risque métier + qui corrige + effort estimé.
|
|
54
|
+
3. **Ancrage local** : références à la loi sénégalaise 2008-12 sur les données
|
|
55
|
+
personnelles et à la CDP, pour crédibiliser le rapport auprès des dirigeants.
|
|
56
|
+
|
|
57
|
+
## Mesure du succès (version 1)
|
|
58
|
+
|
|
59
|
+
- Un gérant comprend son rapport **sans aide**.
|
|
60
|
+
- Un scan passif complet s'exécute en **moins de 60 secondes**.
|
|
61
|
+
- Le README montre la valeur en **30 secondes** (démo + exemple de PDF).
|
|
62
|
+
- Publié sur **PyPI**, installable en une commande.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# 02 — Utilisation : comment le projet s'utilise
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
Depuis les sources (pmescan n'est pas encore publié sur PyPI) :
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m venv .venv
|
|
9
|
+
.venv/bin/pip install . # Windows : .venv\Scripts\pip install .
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Une fois publié : `pip install pmescan`. Pour le rapport PDF, il faut en plus les
|
|
13
|
+
bibliothèques système de WeasyPrint (voir le README).
|
|
14
|
+
|
|
15
|
+
## Les trois modes
|
|
16
|
+
|
|
17
|
+
| Mode | Commande | Ce qu'il fait | Autorisation |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| **Passif** | *(défaut)* | Lit seulement ce que le site publie : TLS, en-têtes, DNS/e-mail, page d'accueil pour le CMS. | Non |
|
|
20
|
+
| **Actif** | `--mode actif --jai-l-autorisation` | Ajoute les fichiers exposés, les fichiers de version du CMS et le test des ports. | Oui |
|
|
21
|
+
| **Agressif** | `--mode agressif --jai-l-autorisation` | Réservé à de futures vérifications. Équivaut aujourd'hui au mode actif. | Oui |
|
|
22
|
+
|
|
23
|
+
> ⚠️ Les modes actif et agressif ne s'utilisent que sur une cible dont vous êtes
|
|
24
|
+
> propriétaire, ou pour laquelle vous avez une autorisation écrite. Voir
|
|
25
|
+
> `README-mode-agressif.md`.
|
|
26
|
+
|
|
27
|
+
## Commandes de base
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Scan passif, résultat dans le terminal
|
|
31
|
+
pmescan scan exemple.sn
|
|
32
|
+
|
|
33
|
+
# Rapport PDF en français
|
|
34
|
+
pmescan scan exemple.sn --pdf
|
|
35
|
+
|
|
36
|
+
# Préciser le fichier PDF
|
|
37
|
+
pmescan scan exemple.sn --pdf --sortie rapport-client.pdf
|
|
38
|
+
|
|
39
|
+
# Sortie JSON (intégration, script)
|
|
40
|
+
pmescan scan exemple.sn --json
|
|
41
|
+
|
|
42
|
+
# Mode actif (fichiers exposés, ports…) : autorisation obligatoire
|
|
43
|
+
pmescan scan exemple.sn --mode actif --jai-l-autorisation
|
|
44
|
+
|
|
45
|
+
# Vérifier aussi les failles connues du CMS (contacte osv.dev)
|
|
46
|
+
pmescan scan exemple.sn --verifier-cve
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Ce que voit l'utilisateur
|
|
50
|
+
|
|
51
|
+
Le terminal affiche une ligne par vérification, `[OK]` ou `[PROBLEME]`, avec
|
|
52
|
+
l'identifiant et la gravité, puis la note globale :
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
[PROBLEME] dmarc_absent (elevee)
|
|
56
|
+
[OK] cms_detecte (info)
|
|
57
|
+
Note globale : D (47/100)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Les explications en français se trouvent dans le rapport PDF.
|
|
61
|
+
|
|
62
|
+
## Contenu du rapport PDF
|
|
63
|
+
|
|
64
|
+
- **Page 1** : note globale (A à F), résumé en 3 phrases, les 3 actions les plus urgentes.
|
|
65
|
+
- **Fiches** : une par problème, avec :
|
|
66
|
+
- le risque expliqué simplement ;
|
|
67
|
+
- la gravité (critique, élevée, moyenne ou faible) ;
|
|
68
|
+
- qui doit corriger (hébergeur, développeur ou gérant) ;
|
|
69
|
+
- l'effort estimé.
|
|
70
|
+
- **Annexe technique** : identifiants et preuves brutes pour le prestataire.
|
|
71
|
+
- **Rappel légal** : loi sénégalaise 2008-12 et CDP.
|
|
72
|
+
|
|
73
|
+
## Cas d'usage typiques
|
|
74
|
+
|
|
75
|
+
1. **Un gérant** lance `pmescan scan monsite.sn --pdf`, lit les 3 actions urgentes et
|
|
76
|
+
les transmet à son hébergeur.
|
|
77
|
+
2. **Une agence web** scanne chaque mois les sites de ses clients, en mode actif avec
|
|
78
|
+
leur autorisation écrite, et leur remet le PDF.
|
|
79
|
+
3. **Une intégration continue** lance `pmescan scan monsite.sn --json`. Le pipeline
|
|
80
|
+
échoue si le code de sortie vaut 1, c'est-à-dire s'il y a un problème critique.
|
|
81
|
+
|
|
82
|
+
## Codes de sortie (pour les scripts)
|
|
83
|
+
|
|
84
|
+
| Code | Signification |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `0` | Scan terminé, aucun problème critique. |
|
|
87
|
+
| `1` | Scan terminé, au moins un problème critique. |
|
|
88
|
+
| `2` | Erreur : option ou domaine invalide, PDF impossible à générer, erreur imprévue ou interruption. |
|
|
89
|
+
| `3` | Refus : mode actif ou agressif sans `--jai-l-autorisation`. |
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# 03 — Conception : comment on va le créer
|
|
2
|
+
|
|
3
|
+
## Principe directeur
|
|
4
|
+
|
|
5
|
+
- **Code en français** : noms de variables, fonctions, classes, commentaires.
|
|
6
|
+
- **Modulaire** : chaque vérification (« check ») est indépendante et renvoie le même
|
|
7
|
+
type d'objet `Resultat`.
|
|
8
|
+
- **Textes séparés du code** : les explications vulgarisées vivent dans des fichiers
|
|
9
|
+
YAML, pas dans le code. Un non-développeur peut les améliorer, et on ajoute une
|
|
10
|
+
langue sans toucher au code.
|
|
11
|
+
- **Sans dépendance lourde** : pas besoin d'installer `nmap` ; le scan de ports se fait
|
|
12
|
+
en Python via `asyncio`.
|
|
13
|
+
|
|
14
|
+
## Langage et bibliothèques
|
|
15
|
+
|
|
16
|
+
| Besoin | Choix | Pourquoi |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Langage | Python 3.11+ | Lisible, riche en libs sécurité, facile à contribuer. |
|
|
19
|
+
| CLI | `typer` | Interface en ligne de commande propre et typée. |
|
|
20
|
+
| TLS | `sslyze` | Analyse fine des certificats et protocoles. |
|
|
21
|
+
| HTTP | `httpx` | Client HTTP moderne, async. |
|
|
22
|
+
| DNS / e-mail | `dnspython` | Requêtes SPF, DMARC, DKIM, MX, DNSSEC. |
|
|
23
|
+
| CVE | API OSV / NVD | Comparer versions CMS aux failles connues. |
|
|
24
|
+
| Ports | `asyncio` (natif) | Connexion TCP sans dépendance externe. |
|
|
25
|
+
| Rapport PDF | `jinja2` + `weasyprint` | HTML → PDF, mise en page soignée. |
|
|
26
|
+
| Tests | `pytest` | Standard Python. |
|
|
27
|
+
| Qualité | `ruff` + `mypy` | Lint et typage. |
|
|
28
|
+
|
|
29
|
+
## Arborescence du projet
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
pmescan/
|
|
33
|
+
├── pyproject.toml # métadonnées + dépendances (packaging PyPI)
|
|
34
|
+
├── README.md
|
|
35
|
+
├── README-mode-agressif.md
|
|
36
|
+
├── LICENSE # MIT
|
|
37
|
+
├── CONTRIBUTING.md
|
|
38
|
+
├── pmescan/
|
|
39
|
+
│ ├── __init__.py
|
|
40
|
+
│ ├── cli.py # point d'entrée Typer
|
|
41
|
+
│ ├── modele.py # classes Resultat, Gravite, Cible
|
|
42
|
+
│ ├── scanner.py # orchestre les checks selon le mode
|
|
43
|
+
│ ├── checks/
|
|
44
|
+
│ │ ├── __init__.py
|
|
45
|
+
│ │ ├── base.py # classe abstraite Verification
|
|
46
|
+
│ │ ├── tls.py
|
|
47
|
+
│ │ ├── entetes.py # en-têtes HTTP
|
|
48
|
+
│ │ ├── dns_mail.py
|
|
49
|
+
│ │ ├── cms.py
|
|
50
|
+
│ │ ├── ports.py
|
|
51
|
+
│ │ └── exposes.py # fichiers sensibles exposés
|
|
52
|
+
│ ├── notation.py # pondération → note A–F
|
|
53
|
+
│ ├── i18n/
|
|
54
|
+
│ │ └── fr.yaml # textes vulgarisés par problème
|
|
55
|
+
│ └── rapport/
|
|
56
|
+
│ ├── modele.html # gabarit Jinja2
|
|
57
|
+
│ └── generateur.py # rendu WeasyPrint → PDF
|
|
58
|
+
└── tests/
|
|
59
|
+
├── test_tls.py
|
|
60
|
+
├── test_entetes.py
|
|
61
|
+
├── test_dns_mail.py
|
|
62
|
+
├── test_notation.py
|
|
63
|
+
└── fixtures/ # réponses enregistrées pour tests hors-ligne
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Modèle de données (cœur du système)
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# pmescan/modele.py
|
|
70
|
+
from dataclasses import dataclass, field
|
|
71
|
+
from enum import Enum
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class Gravite(str, Enum):
|
|
75
|
+
CRITIQUE = "critique"
|
|
76
|
+
ELEVEE = "elevee"
|
|
77
|
+
MOYENNE = "moyenne"
|
|
78
|
+
FAIBLE = "faible"
|
|
79
|
+
INFO = "info"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class Resultat:
|
|
84
|
+
"""Un problème (ou une absence de problème) trouvé par une vérification."""
|
|
85
|
+
identifiant: str # ex. "tls_expire", "dmarc_absent"
|
|
86
|
+
gravite: Gravite
|
|
87
|
+
reussi: bool # True = aucun problème sur ce point
|
|
88
|
+
preuve: str = "" # donnée technique brute (ex. date d'expiration)
|
|
89
|
+
details: dict = field(default_factory=dict)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class Cible:
|
|
94
|
+
domaine: str
|
|
95
|
+
ip: str | None = None
|
|
96
|
+
mode: str = "passif" # "passif" | "actif" | "agressif"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Contrat d'une vérification
|
|
100
|
+
|
|
101
|
+
Chaque check hérite de `Verification` et implémente `executer()`. Il renvoie une liste
|
|
102
|
+
de `Resultat`. Il ne connaît ni la notation ni le rapport : il fait une seule chose.
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# pmescan/checks/base.py
|
|
106
|
+
from abc import ABC, abstractmethod
|
|
107
|
+
from pmescan.modele import Cible, Resultat
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Verification(ABC):
|
|
111
|
+
"""Classe mère de toutes les vérifications."""
|
|
112
|
+
|
|
113
|
+
nom: str = "verification"
|
|
114
|
+
niveau_minimum: str = "passif" # mode minimal qui active ce check
|
|
115
|
+
|
|
116
|
+
@abstractmethod
|
|
117
|
+
async def executer(self, cible: Cible) -> list[Resultat]:
|
|
118
|
+
"""Analyse la cible et renvoie la liste des problèmes trouvés."""
|
|
119
|
+
...
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Séparation texte / code (i18n)
|
|
123
|
+
|
|
124
|
+
Le code produit un `identifiant` (`dmarc_absent`). Le fichier YAML donne la version
|
|
125
|
+
humaine. Pour ajouter le wolof, on copie `fr.yaml` en `wo.yaml`.
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
# pmescan/i18n/fr.yaml
|
|
129
|
+
dmarc_absent:
|
|
130
|
+
titre: "Aucune protection contre l'usurpation d'e-mail"
|
|
131
|
+
risque: >
|
|
132
|
+
N'importe qui peut envoyer des e-mails en se faisant passer pour votre
|
|
133
|
+
entreprise. Vos clients peuvent recevoir de fausses factures à votre nom.
|
|
134
|
+
qui_corrige: "hebergeur"
|
|
135
|
+
effort: "1 h"
|
|
136
|
+
action: "Ajouter un enregistrement DMARC dans votre zone DNS."
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Notation (note A–F)
|
|
140
|
+
|
|
141
|
+
Chaque `Resultat` non réussi retire des points selon sa gravité. La somme donne un
|
|
142
|
+
score sur 100, converti en lettre.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# pmescan/notation.py
|
|
146
|
+
PENALITES = {
|
|
147
|
+
"critique": 40,
|
|
148
|
+
"elevee": 20,
|
|
149
|
+
"moyenne": 10,
|
|
150
|
+
"faible": 3,
|
|
151
|
+
"info": 0,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
def calculer_note(resultats: list[Resultat]) -> tuple[int, str]:
|
|
155
|
+
score = 100
|
|
156
|
+
for r in resultats:
|
|
157
|
+
if not r.reussi:
|
|
158
|
+
score -= PENALITES[r.gravite.value]
|
|
159
|
+
score = max(0, score)
|
|
160
|
+
lettre = (
|
|
161
|
+
"A" if score >= 90 else
|
|
162
|
+
"B" if score >= 75 else
|
|
163
|
+
"C" if score >= 60 else
|
|
164
|
+
"D" if score >= 40 else
|
|
165
|
+
"E" if score >= 20 else "F"
|
|
166
|
+
)
|
|
167
|
+
return score, lettre
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Sécurité et éthique intégrées dès la conception
|
|
171
|
+
|
|
172
|
+
- Le mode passif est le mode par défaut. Les modes actif et agressif exigent le flag
|
|
173
|
+
`--jai-l-autorisation` : sans lui, la CLI refuse avant toute requête, avec le code 3.
|
|
174
|
+
- Chaque scan intrusif autorisé est noté dans un journal local (`~/.pmescan/consent.log`).
|
|
175
|
+
- Défense en profondeur : `scanner()` n'exécute une vérification intrusive que si
|
|
176
|
+
`Cible.autorisation` vaut True. Les checks intrusifs (`ports`, `exposes`) et les
|
|
177
|
+
sondes de chemins du check `cms` vérifient aussi ce champ eux-mêmes. Même un appel
|
|
178
|
+
Python direct ne lance donc aucune requête intrusive sans autorisation explicite.
|
|
179
|
+
- La charge sur la cible est bornée : 20 connexions simultanées au plus, 2 s par
|
|
180
|
+
tentative, une seule tentative par port.
|
|
181
|
+
- Aucune donnée envoyée à un tiers : tout le scan et le rapport restent locaux. La seule
|
|
182
|
+
exception est la vérification CVE optionnelle via OSV, désactivée par défaut et activable
|
|
183
|
+
avec `--verifier-cve`, qui envoie uniquement le nom et la version du CMS détecté à l'API
|
|
184
|
+
publique osv.dev.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# 04 — Étapes à suivre pour créer le projet
|
|
2
|
+
|
|
3
|
+
Plan de construction en 4 semaines pour une version 1 publiable. Chaque étape se
|
|
4
|
+
termine par quelque chose qui marche et qui est testé.
|
|
5
|
+
|
|
6
|
+
## Étape 0 — Mise en place (jour 1)
|
|
7
|
+
|
|
8
|
+
1. Créer le dépôt Git et la structure de dossiers (voir `03-CONCEPTION.md`).
|
|
9
|
+
2. Écrire `pyproject.toml` (dépendances, point d'entrée `pmescan`).
|
|
10
|
+
3. Mettre en place l'environnement : `python -m venv .venv`, `pip install -e .`.
|
|
11
|
+
4. Configurer `ruff`, `mypy`, `pytest`.
|
|
12
|
+
5. Ajouter `LICENSE` (MIT) et un `README.md` minimal.
|
|
13
|
+
|
|
14
|
+
**Fini quand** : `pmescan --version` s'affiche.
|
|
15
|
+
|
|
16
|
+
## Étape 1 — Noyau + TLS + en-têtes (semaine 1)
|
|
17
|
+
|
|
18
|
+
1. Écrire `modele.py` (classes `Resultat`, `Gravite`, `Cible`).
|
|
19
|
+
2. Écrire `checks/base.py` (classe `Verification`).
|
|
20
|
+
3. Écrire `scanner.py` (orchestre les checks selon le mode).
|
|
21
|
+
4. Écrire `checks/tls.py` (expiration, TLS 1.0/1.1, chiffrements faibles, redirection).
|
|
22
|
+
5. Écrire `checks/entetes.py` (HSTS, CSP, X-Frame-Options, cookies, fuite de version).
|
|
23
|
+
6. Écrire `cli.py` avec sortie JSON (`--json`).
|
|
24
|
+
7. Écrire les tests `test_tls.py` et `test_entetes.py` avec des fixtures hors-ligne.
|
|
25
|
+
|
|
26
|
+
**Fini quand** : `pmescan scan exemple.com --json` renvoie les résultats TLS et en-têtes.
|
|
27
|
+
|
|
28
|
+
## Étape 2 — DNS/e-mail + fichiers exposés + notation (semaine 2)
|
|
29
|
+
|
|
30
|
+
1. Écrire `checks/dns_mail.py` (SPF, DMARC, DKIM, MX, DNSSEC).
|
|
31
|
+
2. Écrire `checks/exposes.py` (`/.git/`, `/.env`, `backup.zip`, listing de répertoires).
|
|
32
|
+
3. Écrire `notation.py` (score + note A–F).
|
|
33
|
+
4. Écrire `i18n/fr.yaml` avec les textes des problèmes déjà détectés.
|
|
34
|
+
5. Tests : `test_dns_mail.py`, `test_notation.py`.
|
|
35
|
+
|
|
36
|
+
**Fini quand** : `pmescan scan exemple.com --json` renvoie une note globale A–F.
|
|
37
|
+
|
|
38
|
+
## Étape 3 — Rapport PDF en français (semaine 3)
|
|
39
|
+
|
|
40
|
+
1. Écrire `rapport/modele.html` (gabarit Jinja2 : page de garde, fiches, annexe).
|
|
41
|
+
2. Écrire `rapport/generateur.py` (WeasyPrint → PDF).
|
|
42
|
+
3. Brancher `--pdf` et `--sortie` dans `cli.py`.
|
|
43
|
+
4. Ajouter l'encadré légal (loi 2008-12, CDP).
|
|
44
|
+
5. Soigner la mise en page (couleurs par gravité, note en gros sur la page 1).
|
|
45
|
+
|
|
46
|
+
**Fini quand** : `pmescan scan exemple.com --pdf` produit un PDF lisible par un non-technicien.
|
|
47
|
+
|
|
48
|
+
## Étape 4 — CMS + ports + publication (semaine 4)
|
|
49
|
+
|
|
50
|
+
1. Écrire `checks/cms.py` (WordPress/Joomla/Drupal/PrestaShop + API OSV pour les CVE).
|
|
51
|
+
2. Écrire `checks/ports.py` (~30 ports critiques via `asyncio`, mode actif).
|
|
52
|
+
3. Ajouter la garde `--jai-l-autorisation` (modes actif et agressif) et le journal de
|
|
53
|
+
consentement, puis aligner `README-mode-agressif.md` sur ce comportement.
|
|
54
|
+
4. Finaliser le `README.md` (exemple de sortie, options, codes de sortie). Un exemple de
|
|
55
|
+
PDF et un GIF de démo restent à ajouter.
|
|
56
|
+
5. Publier sur **PyPI** (`python -m build`, `twine upload`).
|
|
57
|
+
6. Écrire `CONTRIBUTING.md`.
|
|
58
|
+
|
|
59
|
+
**Fini quand** : `pip install pmescan` fonctionne pour tout le monde.
|
|
60
|
+
|
|
61
|
+
## Après la version 1 (idées)
|
|
62
|
+
|
|
63
|
+
- Interface web (FastAPI) pour scanner sans ligne de commande.
|
|
64
|
+
- Scans planifiés avec alerte e-mail si la note baisse.
|
|
65
|
+
- Mode multi-sites pour les agences.
|
|
66
|
+
- Traduction wolof et anglais (`wo.yaml`, `en.yaml`).
|
|
67
|
+
- Rapport en marque blanche (logo de l'agence).
|
|
68
|
+
|
|
69
|
+
## Règle de qualité à chaque étape
|
|
70
|
+
|
|
71
|
+
Rien n'est « fini » tant que :
|
|
72
|
+
- les tests passent (`pytest`) ;
|
|
73
|
+
- `ruff` et `mypy` ne signalent aucune erreur ;
|
|
74
|
+
- la fonctionnalité est documentée dans le README ;
|
|
75
|
+
- le code est en français et lisible.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Contribuer à pmescan
|
|
2
|
+
|
|
3
|
+
Merci de votre intérêt ! Toutes les contributions comptent, même sans écrire de code :
|
|
4
|
+
- une explication plus claire dans `fr.yaml` ;
|
|
5
|
+
- une traduction ;
|
|
6
|
+
- un faux positif signalé ;
|
|
7
|
+
- une version de CMS à mettre à jour.
|
|
8
|
+
|
|
9
|
+
## Installer l'environnement de développement
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone <url-du-depot> pmescan
|
|
13
|
+
cd pmescan
|
|
14
|
+
python -m venv .venv
|
|
15
|
+
.venv/bin/pip install -e ".[dev]" # Windows : .venv\Scripts\pip install -e ".[dev]"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Avant chaque proposition (pull request)
|
|
19
|
+
|
|
20
|
+
Les trois commandes suivantes doivent passer sans erreur :
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m pytest -q
|
|
24
|
+
python -m ruff check pmescan tests
|
|
25
|
+
python -m mypy pmescan
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Sous Windows, remplacez `python` par `.venv\Scripts\python.exe`.
|
|
29
|
+
|
|
30
|
+
Un test est ignoré si WeasyPrint ou ses bibliothèques système (Pango, GTK) sont absents.
|
|
31
|
+
C'est normal.
|
|
32
|
+
|
|
33
|
+
## Règles du projet
|
|
34
|
+
|
|
35
|
+
1. **Tout en français.**
|
|
36
|
+
- Les noms de variables, de fonctions et de classes, les commentaires et les messages
|
|
37
|
+
de commit sont en français.
|
|
38
|
+
- Seuls les noms imposés par une bibliothèque restent en anglais (`status_code`,
|
|
39
|
+
`wait_closed`, …).
|
|
40
|
+
2. **Aucun texte destiné à l'utilisateur dans le code.**
|
|
41
|
+
- Un check produit un `Resultat` qui porte un `identifiant` stable, par exemple
|
|
42
|
+
`dmarc_absent`.
|
|
43
|
+
- L'explication en français se trouve dans `pmescan/i18n/fr.yaml`. Chaque entrée a
|
|
44
|
+
cinq clés : `titre`, `risque`, `qui_corrige` (`hebergeur`, `developpeur` ou
|
|
45
|
+
`gerant`), `effort` et `action`.
|
|
46
|
+
3. **Écrire pour un gérant, pas pour un expert.**
|
|
47
|
+
- Le champ `risque` explique la conséquence concrète, sans jargon : par exemple
|
|
48
|
+
« essais de mots de passe en masse » plutôt que « force brute ».
|
|
49
|
+
- Le texte n'exagère pas : on écrit « peut permettre » plutôt que « permet ».
|
|
50
|
+
4. **Tests 100 % hors ligne.** Aucun test ne doit toucher le réseau :
|
|
51
|
+
- les appels HTTP passent par `respx` ;
|
|
52
|
+
- le DNS est simulé (`dns.asyncresolver.resolve`, `VerificationPorts._resoudre`) ;
|
|
53
|
+
- sslyze est remplacé par les faux objets de `tests/conftest.py` ;
|
|
54
|
+
- les connexions TCP passent par `faux_open_connection`.
|
|
55
|
+
5. **Aucune exception ne doit faire planter le scan.**
|
|
56
|
+
- Un check gère lui-même ses erreurs réseau.
|
|
57
|
+
- L'orchestrateur (`scanner.py`) sert de filet de sécurité : il transforme une
|
|
58
|
+
exception imprévue en `<check>_erreur_interne`.
|
|
59
|
+
6. **Respecter les modes.**
|
|
60
|
+
- Toute requête qui va au-delà de la simple lecture de ce que le site publie est
|
|
61
|
+
réservée au mode `actif` : chemins sensibles, fichiers de version, connexions TCP.
|
|
62
|
+
- Le mode `passif` ne doit jamais faire ce type de requête.
|
|
63
|
+
- Une requête intrusive exige **aussi** `cible.autorisation`. `scanner()` ignore les
|
|
64
|
+
vérifications de niveau actif quand `Cible.autorisation` vaut False. C'est la
|
|
65
|
+
valeur par défaut, y compris lorsqu'on appelle pmescan depuis Python.
|
|
66
|
+
- Une nouvelle sonde intrusive placée dans un check passif doit tester
|
|
67
|
+
`cible.autorisation` elle-même, comme le fait `cms.py`.
|
|
68
|
+
7. **Pas de faux positif.**
|
|
69
|
+
- Dans le doute, un check ne signale rien, ou signale en `INFO`, ce qui ne retire
|
|
70
|
+
aucun point.
|
|
71
|
+
- Un problème signalé à tort coûte la confiance du gérant.
|
|
72
|
+
|
|
73
|
+
## Ajouter une vérification
|
|
74
|
+
|
|
75
|
+
1. Créez `pmescan/checks/<nom>.py`, avec une classe qui hérite de `Verification` et
|
|
76
|
+
déclare `nom`, `niveau_minimum` (`"passif"` ou `"actif"`) et `async def executer(self, cible)`.
|
|
77
|
+
2. Ajoutez-la à `VERIFICATIONS` dans `pmescan/scanner.py`.
|
|
78
|
+
3. Ajoutez une entrée dans `fr.yaml` pour **chaque** identifiant non réussi.
|
|
79
|
+
4. Écrivez `tests/test_<nom>.py`, qui couvre le cas « problème », le cas « tout va
|
|
80
|
+
bien » et les erreurs réseau.
|
|
81
|
+
5. Ajoutez un scénario « problème » dans `_SCENARIOS_PAR_NOM` de `tests/test_i18n.py`.
|
|
82
|
+
C'est obligatoire : sans ce scénario, le test échoue. Ce garde-fou assure que tout
|
|
83
|
+
identifiant émis a bien son texte en français.
|
|
84
|
+
|
|
85
|
+
## Ajouter une langue
|
|
86
|
+
|
|
87
|
+
Copiez `pmescan/i18n/fr.yaml` en `<code>.yaml` (par exemple `wo.yaml` pour le wolof) et
|
|
88
|
+
traduisez les valeurs. Les **clés** ne changent pas. Une relecture par une personne de
|
|
89
|
+
langue maternelle est très appréciée.
|
|
90
|
+
|
|
91
|
+
## Mettre à jour les versions de CMS
|
|
92
|
+
|
|
93
|
+
La table `BRANCHES_MAINTENUES` de `pmescan/checks/cms.py` est tenue à la main. Pour la
|
|
94
|
+
corriger :
|
|
95
|
+
- indiquez dans la pull request la source officielle (calendrier de support du CMS) ;
|
|
96
|
+
- mettez à jour la date du commentaire.
|
|
97
|
+
|
|
98
|
+
## Signaler une faille de sécurité dans pmescan
|
|
99
|
+
|
|
100
|
+
Ne publiez pas les détails dans une issue publique. Utilisez le signalement privé de
|
|
101
|
+
vulnérabilité de la plateforme qui héberge le dépôt, ou ouvrez une issue qui demande
|
|
102
|
+
seulement un canal de contact privé.
|
|
103
|
+
|
|
104
|
+
## Éthique
|
|
105
|
+
|
|
106
|
+
pmescan est un outil défensif. Une contribution qui ajoute une capacité offensive
|
|
107
|
+
(exploitation de faille, essais de mots de passe, contournement de protections) ou qui
|
|
108
|
+
affaiblit la garde d'autorisation des modes intrusifs sera refusée.
|
pmescan-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pmescan et ses contributeurs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|