wopt 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.
- wopt-0.1.0/.github/workflows/ci.yml +34 -0
- wopt-0.1.0/.github/workflows/publish.yml +29 -0
- wopt-0.1.0/.gitignore +16 -0
- wopt-0.1.0/LICENSE +21 -0
- wopt-0.1.0/PKG-INFO +112 -0
- wopt-0.1.0/README.md +78 -0
- wopt-0.1.0/pyproject.toml +61 -0
- wopt-0.1.0/requirements-dev.txt +3 -0
- wopt-0.1.0/requirements.txt +5 -0
- wopt-0.1.0/src/wopt/__init__.py +7 -0
- wopt-0.1.0/src/wopt/checks/__init__.py +36 -0
- wopt-0.1.0/src/wopt/checks/base.py +29 -0
- wopt-0.1.0/src/wopt/checks/cookies.py +58 -0
- wopt-0.1.0/src/wopt/checks/cors.py +60 -0
- wopt-0.1.0/src/wopt/checks/exposure.py +60 -0
- wopt-0.1.0/src/wopt/checks/headers.py +118 -0
- wopt-0.1.0/src/wopt/checks/tls.py +100 -0
- wopt-0.1.0/src/wopt/cli.py +141 -0
- wopt-0.1.0/src/wopt/core/__init__.py +0 -0
- wopt-0.1.0/src/wopt/core/http_client.py +139 -0
- wopt-0.1.0/src/wopt/core/scanner.py +49 -0
- wopt-0.1.0/src/wopt/models.py +103 -0
- wopt-0.1.0/src/wopt/report/__init__.py +0 -0
- wopt-0.1.0/src/wopt/report/ai_context_output.py +48 -0
- wopt-0.1.0/src/wopt/report/html_output.py +28 -0
- wopt-0.1.0/src/wopt/report/json_output.py +39 -0
- wopt-0.1.0/src/wopt/report/scoring.py +35 -0
- wopt-0.1.0/src/wopt/report/templates/report.html.j2 +66 -0
- wopt-0.1.0/tests/conftest.py +52 -0
- wopt-0.1.0/tests/test_cookies.py +16 -0
- wopt-0.1.0/tests/test_cors.py +24 -0
- wopt-0.1.0/tests/test_headers.py +32 -0
- wopt-0.1.0/tests/test_scoring.py +38 -0
- wopt-0.1.0/tests/test_tls.py +31 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
pip install -r requirements.txt
|
|
27
|
+
pip install -r requirements-dev.txt
|
|
28
|
+
pip install -e .
|
|
29
|
+
|
|
30
|
+
- name: Lint with ruff
|
|
31
|
+
run: ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Run tests
|
|
34
|
+
run: pytest -v
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Set up Python
|
|
15
|
+
uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install build tools
|
|
20
|
+
run: pip install build twine
|
|
21
|
+
|
|
22
|
+
- name: Build package
|
|
23
|
+
run: python -m build
|
|
24
|
+
|
|
25
|
+
- name: Publish to PyPI
|
|
26
|
+
env:
|
|
27
|
+
TWINE_USERNAME: __token__
|
|
28
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
29
|
+
run: twine upload dist/*
|
wopt-0.1.0/.gitignore
ADDED
wopt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 eloundou nkolo ryan
|
|
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.
|
wopt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wopt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mini audit de sécurité web en ligne de commande : headers, TLS, cookies, CORS, fichiers exposés.
|
|
5
|
+
Project-URL: Homepage, https://github.com/eloundou-nkolo/wopt
|
|
6
|
+
Project-URL: Repository, https://github.com/eloundou-nkolo/wopt
|
|
7
|
+
Project-URL: Issues, https://github.com/eloundou-nkolo/wopt/issues
|
|
8
|
+
Project-URL: Portfolio, https://eloundounkolo.com
|
|
9
|
+
Author-email: Eloundou Nkolo Ryan <ryan@eloundounkolo.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: audit,cli,cors,http-headers,security,tls,web
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: cryptography
|
|
25
|
+
Requires-Dist: httpx
|
|
26
|
+
Requires-Dist: jinja2
|
|
27
|
+
Requires-Dist: rich
|
|
28
|
+
Requires-Dist: typer
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-httpx; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# wopt
|
|
36
|
+
|
|
37
|
+
test audit de sécurité web en ligne de commande — non-intrusif, sans configuration, sans infrastructure.
|
|
38
|
+
|
|
39
|
+
`wopt` scanne un site web et vérifie les points de sécurité les plus fréquemment négligés : en-têtes HTTP, configuration TLS, cookies, CORS, et exposition de fichiers sensibles. Conçu pour s'intégrer facilement à un pipeline CI/CD.
|
|
40
|
+
## Auteurs
|
|
41
|
+
|
|
42
|
+
Développé par [Eloundou Nkolo Ryan](https://eloundounkolo.com) — développeur web & intégration de solutions numériques.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install wopt
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Ou en local depuis le code source :
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git clone https://github.com/eloundou-nkolo/wopt.git
|
|
54
|
+
cd wopt
|
|
55
|
+
pip install -r requirements.txt
|
|
56
|
+
pip install -e .
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Utilisation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Scan simple, affichage table dans le terminal
|
|
63
|
+
wopt exemple.com
|
|
64
|
+
|
|
65
|
+
# Export JSON (pour CI/CD)
|
|
66
|
+
wopt exemple.com --format json --output rapport.json
|
|
67
|
+
|
|
68
|
+
# Export HTML lisible
|
|
69
|
+
wopt exemple.com --format html --output rapport.html
|
|
70
|
+
|
|
71
|
+
# Rapport formaté pour être collé dans un agent IA (Claude Code, Cursor...)
|
|
72
|
+
wopt exemple.com --ai-context
|
|
73
|
+
|
|
74
|
+
# Scan plus léger, sans vérification des chemins sensibles
|
|
75
|
+
wopt exemple.com --no-probes
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Ce que wopt vérifie
|
|
79
|
+
|
|
80
|
+
| Catégorie | Vérifications |
|
|
81
|
+
|---|---|
|
|
82
|
+
| **Headers** | CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, divulgation de stack |
|
|
83
|
+
| **TLS** | Version du protocole, expiration du certificat, redirection HTTPS |
|
|
84
|
+
| **Cookies** | Flags Secure, HttpOnly, SameSite |
|
|
85
|
+
| **CORS** | Combinaison dangereuse wildcard + credentials |
|
|
86
|
+
| **Exposition** | Fichiers sensibles accessibles (`.env`, `.git/HEAD`, backups...) |
|
|
87
|
+
|
|
88
|
+
## Philosophie
|
|
89
|
+
|
|
90
|
+
- **100% passif et non-intrusif** : aucun scan de ports, aucune tentative d'exploitation, aucun brute-force. Uniquement des requêtes HTTP GET standards.
|
|
91
|
+
- **Zéro infrastructure** : tourne entièrement en local, aucune donnée envoyée à un serveur tiers.
|
|
92
|
+
- **Pensé pour l'ère du vibe coding** : conçu pour combler les lacunes de sécurité fréquentes dans le code généré par IA (absence de headers de sécurité, CSRF, CORS mal configuré).
|
|
93
|
+
|
|
94
|
+
## Score
|
|
95
|
+
|
|
96
|
+
Chaque scan produit un score de A à F, calculé par pondération de sévérité (inspiré de l'approche Mozilla Observatory).
|
|
97
|
+
|
|
98
|
+
## Développement
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install -r requirements-dev.txt
|
|
102
|
+
pytest
|
|
103
|
+
ruff check .
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Avertissement
|
|
107
|
+
|
|
108
|
+
`wopt` est un outil d'audit passif destiné à un usage sur des sites que vous possédez ou êtes autorisé à tester. L'auteur décline toute responsabilité en cas d'usage non autorisé.
|
|
109
|
+
|
|
110
|
+
## Licence
|
|
111
|
+
|
|
112
|
+
MIT
|
wopt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# wopt
|
|
2
|
+
|
|
3
|
+
test audit de sécurité web en ligne de commande — non-intrusif, sans configuration, sans infrastructure.
|
|
4
|
+
|
|
5
|
+
`wopt` scanne un site web et vérifie les points de sécurité les plus fréquemment négligés : en-têtes HTTP, configuration TLS, cookies, CORS, et exposition de fichiers sensibles. Conçu pour s'intégrer facilement à un pipeline CI/CD.
|
|
6
|
+
## Auteurs
|
|
7
|
+
|
|
8
|
+
Développé par [Eloundou Nkolo Ryan](https://eloundounkolo.com) — développeur web & intégration de solutions numériques.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install wopt
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Ou en local depuis le code source :
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/eloundou-nkolo/wopt.git
|
|
20
|
+
cd wopt
|
|
21
|
+
pip install -r requirements.txt
|
|
22
|
+
pip install -e .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Utilisation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Scan simple, affichage table dans le terminal
|
|
29
|
+
wopt exemple.com
|
|
30
|
+
|
|
31
|
+
# Export JSON (pour CI/CD)
|
|
32
|
+
wopt exemple.com --format json --output rapport.json
|
|
33
|
+
|
|
34
|
+
# Export HTML lisible
|
|
35
|
+
wopt exemple.com --format html --output rapport.html
|
|
36
|
+
|
|
37
|
+
# Rapport formaté pour être collé dans un agent IA (Claude Code, Cursor...)
|
|
38
|
+
wopt exemple.com --ai-context
|
|
39
|
+
|
|
40
|
+
# Scan plus léger, sans vérification des chemins sensibles
|
|
41
|
+
wopt exemple.com --no-probes
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Ce que wopt vérifie
|
|
45
|
+
|
|
46
|
+
| Catégorie | Vérifications |
|
|
47
|
+
|---|---|
|
|
48
|
+
| **Headers** | CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, divulgation de stack |
|
|
49
|
+
| **TLS** | Version du protocole, expiration du certificat, redirection HTTPS |
|
|
50
|
+
| **Cookies** | Flags Secure, HttpOnly, SameSite |
|
|
51
|
+
| **CORS** | Combinaison dangereuse wildcard + credentials |
|
|
52
|
+
| **Exposition** | Fichiers sensibles accessibles (`.env`, `.git/HEAD`, backups...) |
|
|
53
|
+
|
|
54
|
+
## Philosophie
|
|
55
|
+
|
|
56
|
+
- **100% passif et non-intrusif** : aucun scan de ports, aucune tentative d'exploitation, aucun brute-force. Uniquement des requêtes HTTP GET standards.
|
|
57
|
+
- **Zéro infrastructure** : tourne entièrement en local, aucune donnée envoyée à un serveur tiers.
|
|
58
|
+
- **Pensé pour l'ère du vibe coding** : conçu pour combler les lacunes de sécurité fréquentes dans le code généré par IA (absence de headers de sécurité, CSRF, CORS mal configuré).
|
|
59
|
+
|
|
60
|
+
## Score
|
|
61
|
+
|
|
62
|
+
Chaque scan produit un score de A à F, calculé par pondération de sévérité (inspiré de l'approche Mozilla Observatory).
|
|
63
|
+
|
|
64
|
+
## Développement
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install -r requirements-dev.txt
|
|
68
|
+
pytest
|
|
69
|
+
ruff check .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Avertissement
|
|
73
|
+
|
|
74
|
+
`wopt` est un outil d'audit passif destiné à un usage sur des sites que vous possédez ou êtes autorisé à tester. L'auteur décline toute responsabilité en cas d'usage non autorisé.
|
|
75
|
+
|
|
76
|
+
## Licence
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wopt"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Mini audit de sécurité web en ligne de commande : headers, TLS, cookies, CORS, fichiers exposés."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Eloundou Nkolo Ryan", email="ryan@eloundounkolo.com", website = "eloundounkolo.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["security", "web", "audit", "cli", "http-headers", "tls", "cors"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Security",
|
|
26
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
dependencies = [
|
|
30
|
+
"httpx",
|
|
31
|
+
"typer",
|
|
32
|
+
"rich",
|
|
33
|
+
"jinja2",
|
|
34
|
+
"cryptography",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest",
|
|
40
|
+
"pytest-httpx",
|
|
41
|
+
"ruff",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
wopt = "wopt.cli:main"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://github.com/eloundou-nkolo/wopt"
|
|
49
|
+
Repository = "https://github.com/eloundou-nkolo/wopt"
|
|
50
|
+
Issues = "https://github.com/eloundou-nkolo/wopt/issues"
|
|
51
|
+
Portfolio = "https://eloundounkolo.com"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/wopt"]
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
line-length = 100
|
|
58
|
+
target-version = "py310"
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
wopt.checks
|
|
3
|
+
|
|
4
|
+
Registre central des checks disponibles. Pour ajouter un nouveau
|
|
5
|
+
check : créer la classe dans un module de ce package, puis
|
|
6
|
+
l'ajouter à ALL_CHECKS. Le scanner n'a rien d'autre à connaître.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from wopt.checks.cookies import CookiesCheck
|
|
10
|
+
from wopt.checks.cors import CORSCheck
|
|
11
|
+
from wopt.checks.exposure import ExposurePathsCheck
|
|
12
|
+
from wopt.checks.headers import SecurityHeadersCheck
|
|
13
|
+
from wopt.checks.tls import TLSCheck
|
|
14
|
+
|
|
15
|
+
# Checks "standards" exécutés directement sur le ScanContext
|
|
16
|
+
ALL_CHECKS = [
|
|
17
|
+
SecurityHeadersCheck,
|
|
18
|
+
TLSCheck,
|
|
19
|
+
CookiesCheck,
|
|
20
|
+
CORSCheck,
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# Checks nécessitant des requêtes HTTP additionnelles (probing actif)
|
|
24
|
+
PROBE_CHECKS = [
|
|
25
|
+
ExposurePathsCheck,
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"ALL_CHECKS",
|
|
30
|
+
"PROBE_CHECKS",
|
|
31
|
+
"SecurityHeadersCheck",
|
|
32
|
+
"TLSCheck",
|
|
33
|
+
"CookiesCheck",
|
|
34
|
+
"CORSCheck",
|
|
35
|
+
"ExposurePathsCheck",
|
|
36
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
wopt.checks.base
|
|
3
|
+
|
|
4
|
+
Contrat que tout check de sécurité doit respecter.
|
|
5
|
+
Chaque nouveau check hérite de Check et implémente run().
|
|
6
|
+
Le scanner orchestrateur n'a besoin de rien connaître de la
|
|
7
|
+
logique interne de chaque check : il les découvre et les exécute
|
|
8
|
+
tous de la même façon.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
|
|
15
|
+
from wopt.models import Finding, ScanContext
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Check(ABC):
|
|
19
|
+
"""Classe abstraite : tout check de sécurité l'implémente."""
|
|
20
|
+
|
|
21
|
+
id: str = "base.check"
|
|
22
|
+
name: str = "Base Check"
|
|
23
|
+
category: str = "general" # headers | tls | cookies | cors | exposure
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def run(self, ctx: ScanContext) -> list[Finding]:
|
|
27
|
+
"""Exécute la vérification et retourne une liste de Finding.
|
|
28
|
+
Une liste vide = rien à signaler (tout est conforme)."""
|
|
29
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
wopt.checks.cookies
|
|
3
|
+
|
|
4
|
+
Vérifie que chaque cookie détecté sur la réponse HTTP est
|
|
5
|
+
correctement sécurisé : Secure, HttpOnly, SameSite.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from wopt.checks.base import Check
|
|
11
|
+
from wopt.models import Finding, ScanContext, Severity
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CookiesCheck(Check):
|
|
15
|
+
id = "cookies.flags"
|
|
16
|
+
name = "Cookie Security Flags"
|
|
17
|
+
category = "cookies"
|
|
18
|
+
|
|
19
|
+
def run(self, ctx: ScanContext) -> list[Finding]:
|
|
20
|
+
findings: list[Finding] = []
|
|
21
|
+
|
|
22
|
+
for cookie in ctx.cookies:
|
|
23
|
+
if not cookie.secure:
|
|
24
|
+
findings.append(
|
|
25
|
+
Finding(
|
|
26
|
+
check_id=f"{self.id}.{cookie.name}_not_secure",
|
|
27
|
+
title=f"Cookie '{cookie.name}' sans flag Secure",
|
|
28
|
+
severity=Severity.MEDIUM,
|
|
29
|
+
category=self.category,
|
|
30
|
+
description="Ce cookie peut être transmis en clair sur une connexion non chiffrée.",
|
|
31
|
+
recommendation=f"Ajouter le flag Secure au cookie '{cookie.name}'.",
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
if not cookie.http_only:
|
|
35
|
+
findings.append(
|
|
36
|
+
Finding(
|
|
37
|
+
check_id=f"{self.id}.{cookie.name}_not_httponly",
|
|
38
|
+
title=f"Cookie '{cookie.name}' sans flag HttpOnly",
|
|
39
|
+
severity=Severity.MEDIUM,
|
|
40
|
+
category=self.category,
|
|
41
|
+
description="Ce cookie est accessible via JavaScript, ce qui facilite son vol en cas de XSS.",
|
|
42
|
+
recommendation=f"Ajouter le flag HttpOnly au cookie '{cookie.name}'.",
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
if not cookie.same_site or cookie.same_site.lower() == "none":
|
|
46
|
+
findings.append(
|
|
47
|
+
Finding(
|
|
48
|
+
check_id=f"{self.id}.{cookie.name}_samesite_weak",
|
|
49
|
+
title=f"Cookie '{cookie.name}' avec SameSite absent ou 'None'",
|
|
50
|
+
severity=Severity.LOW,
|
|
51
|
+
category=self.category,
|
|
52
|
+
description="Ce cookie peut être envoyé lors de requêtes cross-site, augmentant le risque CSRF.",
|
|
53
|
+
recommendation=f"Définir 'SameSite=Lax' ou 'Strict' sur le cookie '{cookie.name}'.",
|
|
54
|
+
evidence=cookie.same_site or "absent",
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return findings
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
wopt.checks.cors
|
|
3
|
+
|
|
4
|
+
Détecte les configurations CORS dangereuses, en particulier
|
|
5
|
+
la combinaison Access-Control-Allow-Origin: * associée à
|
|
6
|
+
Access-Control-Allow-Credentials: true, qui permet à n'importe
|
|
7
|
+
quel site tiers de lire des réponses authentifiées.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from wopt.checks.base import Check
|
|
13
|
+
from wopt.models import Finding, ScanContext, Severity
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CORSCheck(Check):
|
|
17
|
+
id = "cors.configuration"
|
|
18
|
+
name = "CORS Configuration"
|
|
19
|
+
category = "cors"
|
|
20
|
+
|
|
21
|
+
def run(self, ctx: ScanContext) -> list[Finding]:
|
|
22
|
+
findings: list[Finding] = []
|
|
23
|
+
headers_lower = {k.lower(): v for k, v in ctx.response_headers.items()}
|
|
24
|
+
|
|
25
|
+
acao = headers_lower.get("access-control-allow-origin")
|
|
26
|
+
acac = headers_lower.get("access-control-allow-credentials", "").lower() == "true"
|
|
27
|
+
|
|
28
|
+
if acao == "*" and acac:
|
|
29
|
+
findings.append(
|
|
30
|
+
Finding(
|
|
31
|
+
check_id=f"{self.id}.wildcard_with_credentials",
|
|
32
|
+
title="CORS critique : origine wildcard combinée aux credentials",
|
|
33
|
+
severity=Severity.CRITICAL,
|
|
34
|
+
category=self.category,
|
|
35
|
+
description=(
|
|
36
|
+
"Access-Control-Allow-Origin est réglé sur '*' alors que "
|
|
37
|
+
"Access-Control-Allow-Credentials est à 'true'. N'importe quel "
|
|
38
|
+
"site tiers peut lire des réponses authentifiées de l'utilisateur."
|
|
39
|
+
),
|
|
40
|
+
recommendation=(
|
|
41
|
+
"Remplacer '*' par une liste blanche explicite d'origines de confiance, "
|
|
42
|
+
"ou désactiver les credentials si le wildcard est nécessaire."
|
|
43
|
+
),
|
|
44
|
+
evidence=f"Access-Control-Allow-Origin: {acao}",
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
elif acao == "*":
|
|
48
|
+
findings.append(
|
|
49
|
+
Finding(
|
|
50
|
+
check_id=f"{self.id}.wildcard_origin",
|
|
51
|
+
title="CORS permissif : origine wildcard",
|
|
52
|
+
severity=Severity.LOW,
|
|
53
|
+
category=self.category,
|
|
54
|
+
description="Access-Control-Allow-Origin accepte toute origine ('*').",
|
|
55
|
+
recommendation="Restreindre aux origines réellement nécessaires si l'API contient des données sensibles.",
|
|
56
|
+
evidence=f"Access-Control-Allow-Origin: {acao}",
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return findings
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
wopt.checks.exposure
|
|
3
|
+
|
|
4
|
+
Vérifie passivement si des chemins sensibles connus répondent
|
|
5
|
+
avec un statut 200 (fichier accessible). Aucune tentative de
|
|
6
|
+
lecture/exploitation du contenu au-delà de la détection : on
|
|
7
|
+
observe uniquement le code de statut HTTP renvoyé.
|
|
8
|
+
|
|
9
|
+
Ce check nécessite des requêtes HTTP additionnelles, effectuées
|
|
10
|
+
par le scanner (pas par ScanContext initial) via check.probe_paths().
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from wopt.checks.base import Check
|
|
16
|
+
from wopt.models import Finding, ScanContext, Severity
|
|
17
|
+
|
|
18
|
+
SENSITIVE_PATHS = {
|
|
19
|
+
"/.env": Severity.CRITICAL,
|
|
20
|
+
"/.git/HEAD": Severity.HIGH,
|
|
21
|
+
"/.git/config": Severity.HIGH,
|
|
22
|
+
"/wp-config.php.bak": Severity.CRITICAL,
|
|
23
|
+
"/config.php.bak": Severity.CRITICAL,
|
|
24
|
+
"/.DS_Store": Severity.LOW,
|
|
25
|
+
"/backup.zip": Severity.MEDIUM,
|
|
26
|
+
"/.aws/credentials": Severity.CRITICAL,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ExposurePathsCheck(Check):
|
|
31
|
+
"""Ce check est particulier : il a besoin de requêtes HTTP
|
|
32
|
+
supplémentaires (une par chemin testé). Le scanner l'invoque
|
|
33
|
+
via `probe_paths()` plutôt que `run()` classique -- voir
|
|
34
|
+
core/scanner.py pour l'intégration."""
|
|
35
|
+
|
|
36
|
+
id = "exposure.sensitive_paths"
|
|
37
|
+
name = "Sensitive Paths Exposure"
|
|
38
|
+
category = "exposure"
|
|
39
|
+
|
|
40
|
+
def paths_to_probe(self) -> dict[str, Severity]:
|
|
41
|
+
return SENSITIVE_PATHS
|
|
42
|
+
|
|
43
|
+
def build_finding(self, path: str, severity: Severity, status_code: int) -> Finding:
|
|
44
|
+
return Finding(
|
|
45
|
+
check_id=f"{self.id}.{path.strip('/').replace('/', '_').replace('.', '_')}",
|
|
46
|
+
title=f"Chemin sensible potentiellement exposé : {path}",
|
|
47
|
+
severity=severity,
|
|
48
|
+
category=self.category,
|
|
49
|
+
description=(
|
|
50
|
+
f"La requête vers '{path}' a retourné un statut {status_code}, "
|
|
51
|
+
"suggérant que ce fichier pourrait être accessible publiquement."
|
|
52
|
+
),
|
|
53
|
+
recommendation=f"Vérifier manuellement '{path}' et bloquer son accès public si confirmé.",
|
|
54
|
+
evidence=f"HTTP {status_code}",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def run(self, ctx: ScanContext) -> list[Finding]:
|
|
58
|
+
# Ce check ne fait rien via run() seul -- voir probe_paths()
|
|
59
|
+
# dans le scanner qui gère les requêtes réseau réelles.
|
|
60
|
+
return []
|