Add hardering

This commit is contained in:
Johnny
2026-04-06 08:37:54 +02:00
parent 4980d8cf3c
commit c0412d1150
27 changed files with 1527 additions and 82 deletions

75
CLAUDE.md Normal file
View File

@@ -0,0 +1,75 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commandes de développement
```bash
# Lancer l'application (mode interactif, demande sudo automatiquement)
python3 -m securecheck
# Lancer sans sudo (pour tester sans élévation)
SECURECHECK_SKIP_SUDO=1 python3 -m securecheck
# Mode non interactif
python3 -m securecheck --dry-run --tasks system_update,lynis_audit
python3 -m securecheck --scenario baseline_workstation --run
python3 -m securecheck --list-scenarios
# Avec sudo (recommandé pour les tâches système réelles)
sudo -E python3 -m securecheck
# Installation locale
pip install .
# Build du binaire autonome (nécessite .venv avec pyinstaller)
python3 -m venv .venv
.venv/bin/pip install pyinstaller
./build_executable.sh
# Résultat dans dist/securecheck
```
## Architecture
### Flux d'exécution principal
`__main__.py:main()` orchestre tout :
1. `ensure_root()` — ré-exécute automatiquement avec `sudo -E` si non root (sauf `SECURECHECK_SKIP_SUDO=1`)
2. `detect_system()` — détecte distro, gestionnaire de paquets, user invocant
3. `task_catalog()` + `builtin_scenarios()` — charge tâches et scénarios
4. En mode interactif : `SecureCheckTUI` → sélection → `execute_tasks()``RunSummaryTUI`
5. En mode non interactif : résolution des tâches via `--tasks`/`--scenario``execute_tasks()` → affichage terminal
### Modules clés
- **`models.py`** — dataclasses centrales : `TaskDefinition`, `TaskResult`, `Scenario`
- **`catalog.py`** — registre de toutes les tâches (`task_catalog()`) et scénarios builtin (`builtin_scenarios()`). Utilise `bind()` pour attacher les handlers.
- **`tasks.py`** — implémentation de chaque tâche (une fonction par tâche). Chaque fonction reçoit `(context: ExecutionContext, task: TaskDefinition)` et retourne `TaskResult`.
- **`executor.py`** — `ExecutionContext` (contexte partagé par toutes les tâches) et `CommandRunner` (abstraction pour toutes les opérations système : paquets, fichiers, services, shell). `execute_tasks()` itère sur les tâches et capture les exceptions.
- **`app.py`** — TUI curses : `SecureCheckTUI` (menu principal) et `RunSummaryTUI` (résumé post-exécution)
- **`config.py`** — `AppPaths` et `build_paths()` : résolution XDG des chemins (config, state, logs). Logs dans `/var/log/securecheck` si root, sinon `~/.local/state/securecheck/logs`.
- **`storage.py`** — `ScenarioStore` : lecture/écriture des scénarios utilisateur dans `~/.config/securecheck/scenarios.json`
- **`system_info.py`** — `SystemInfo` + `detect_system()` : détecte OS, package manager, user réel (via `SUDO_USER`), home, uid/gid
- **`status.py`** — `collect_status()` : sonde les services/composants pour le tableau d'état LED
- **`assets.py`** — accès aux fichiers embarqués dans `securecheck/assets/` (banner.txt, p10k.zsh, icônes)
### Ajouter une nouvelle tâche
1. Écrire la fonction dans `tasks.py` avec la signature `(context: ExecutionContext, task: TaskDefinition) -> TaskResult`
2. Enregistrer dans `catalog.py` : ajouter un `TaskDefinition` dans `task_catalog()` et mapper son handler dans `handlers`
3. L'ajouter optionnellement à un `builtin_scenarios()` existant
### Conventions importantes
- `CommandRunner` est le seul point d'entrée pour les opérations système (jamais `subprocess` directement dans `tasks.py`)
- `context.runner.write_text_file()` est idempotent : ne réécrit pas si le contenu est identique
- `context.runner.update_package_index()` est appelé une seule fois par exécution (flag `_package_index_updated`)
- Le `target_user` dans `SystemInfo` est l'utilisateur réel ayant lancé `sudo`, pas root
- `--dry-run` : toutes les opérations système sont simulées, les commandes sont loggées sans être exécutées
### Emplacements runtime
- Scénarios : `~/.config/securecheck/scenarios.json`
- Logs : `/var/log/securecheck/` (root) ou `~/.local/state/securecheck/logs/`
- Rapports par exécution : `<log_dir>/run-YYYYMMDD-HHMMSS.log`
- Rapports tâches (lynis, rootkits) : `<log_dir>/reports/`

Binary file not shown.

View File

@@ -31,6 +31,9 @@
'DATA'), 'DATA'),
('securecheck/assets/securecheck-icon.svg', ('securecheck/assets/securecheck-icon.svg',
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg', '/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
'DATA'),
('securecheck/assets/system_hardening.sh',
'/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
'DATA')], 'DATA')],
'3.13.7 (main, Mar 3 2026, 12:19:54) [GCC 15.2.0]', '3.13.7 (main, Mar 3 2026, 12:19:54) [GCC 15.2.0]',
[('pyi_rth_inspect', [('pyi_rth_inspect',
@@ -189,12 +192,12 @@
('opcode', '/usr/lib/python3.13/opcode.py', 'PYMODULE'), ('opcode', '/usr/lib/python3.13/opcode.py', 'PYMODULE'),
('_opcode_metadata', '/usr/lib/python3.13/_opcode_metadata.py', 'PYMODULE'), ('_opcode_metadata', '/usr/lib/python3.13/_opcode_metadata.py', 'PYMODULE'),
('ast', '/usr/lib/python3.13/ast.py', 'PYMODULE'), ('ast', '/usr/lib/python3.13/ast.py', 'PYMODULE'),
('_py_abc', '/usr/lib/python3.13/_py_abc.py', 'PYMODULE'),
('stringprep', '/usr/lib/python3.13/stringprep.py', 'PYMODULE'),
('_colorize', '/usr/lib/python3.13/_colorize.py', 'PYMODULE'),
('tracemalloc', '/usr/lib/python3.13/tracemalloc.py', 'PYMODULE'),
('subprocess', '/usr/lib/python3.13/subprocess.py', 'PYMODULE'), ('subprocess', '/usr/lib/python3.13/subprocess.py', 'PYMODULE'),
('signal', '/usr/lib/python3.13/signal.py', 'PYMODULE'), ('signal', '/usr/lib/python3.13/signal.py', 'PYMODULE'),
('stringprep', '/usr/lib/python3.13/stringprep.py', 'PYMODULE'),
('tracemalloc', '/usr/lib/python3.13/tracemalloc.py', 'PYMODULE'),
('_colorize', '/usr/lib/python3.13/_colorize.py', 'PYMODULE'),
('_py_abc', '/usr/lib/python3.13/_py_abc.py', 'PYMODULE'),
('securecheck.__main__', ('securecheck.__main__',
'/home/tuxgyver/scripts/securecheck/securecheck/__main__.py', '/home/tuxgyver/scripts/securecheck/securecheck/__main__.py',
'PYMODULE'), 'PYMODULE'),
@@ -250,6 +253,9 @@
('securecheck.app', ('securecheck.app',
'/home/tuxgyver/scripts/securecheck/securecheck/app.py', '/home/tuxgyver/scripts/securecheck/securecheck/app.py',
'PYMODULE'), 'PYMODULE'),
('securecheck.summary_utils',
'/home/tuxgyver/scripts/securecheck/securecheck/summary_utils.py',
'PYMODULE'),
('curses', '/usr/lib/python3.13/curses/__init__.py', 'PYMODULE'), ('curses', '/usr/lib/python3.13/curses/__init__.py', 'PYMODULE'),
('curses.has_key', '/usr/lib/python3.13/curses/has_key.py', 'PYMODULE'), ('curses.has_key', '/usr/lib/python3.13/curses/has_key.py', 'PYMODULE'),
('__future__', '/usr/lib/python3.13/__future__.py', 'PYMODULE')], ('__future__', '/usr/lib/python3.13/__future__.py', 'PYMODULE')],
@@ -307,8 +313,8 @@
('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so', ('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
'/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so', '/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
'EXTENSION'), 'EXTENSION'),
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'), ('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'),
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'), ('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'),
('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'), ('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'),
('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'), ('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'),
@@ -333,33 +339,49 @@
('securecheck/assets/securecheck-icon.svg', ('securecheck/assets/securecheck-icon.svg',
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg', '/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/METADATA', ('securecheck/assets/system_hardening.sh',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA', '/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
'DATA'),
('wheel-0.46.1.dist-info/WHEEL',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
'DATA'),
('wheel-0.46.1.dist-info/INSTALLER',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/entry_points.txt', ('wheel-0.46.1.dist-info/entry_points.txt',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt', '/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/INSTALLER',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
'DATA'),
('wheel-0.46.1.dist-info/WHEEL',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
'DATA'),
('wheel-0.46.1.dist-info/METADATA',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
'DATA'),
('base_library.zip', ('base_library.zip',
'/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip', '/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip',
'DATA')], 'DATA')],
[('functools', '/usr/lib/python3.13/functools.py', 'PYMODULE'), [('locale', '/usr/lib/python3.13/locale.py', 'PYMODULE'),
('sre_compile', '/usr/lib/python3.13/sre_compile.py', 'PYMODULE'), ('abc', '/usr/lib/python3.13/abc.py', 'PYMODULE'),
('copyreg', '/usr/lib/python3.13/copyreg.py', 'PYMODULE'), ('ntpath', '/usr/lib/python3.13/ntpath.py', 'PYMODULE'),
('operator', '/usr/lib/python3.13/operator.py', 'PYMODULE'),
('genericpath', '/usr/lib/python3.13/genericpath.py', 'PYMODULE'),
('keyword', '/usr/lib/python3.13/keyword.py', 'PYMODULE'),
('types', '/usr/lib/python3.13/types.py', 'PYMODULE'), ('types', '/usr/lib/python3.13/types.py', 'PYMODULE'),
('sre_parse', '/usr/lib/python3.13/sre_parse.py', 'PYMODULE'), ('linecache', '/usr/lib/python3.13/linecache.py', 'PYMODULE'),
('os', '/usr/lib/python3.13/os.py', 'PYMODULE'), ('posixpath', '/usr/lib/python3.13/posixpath.py', 'PYMODULE'),
('_collections_abc', '/usr/lib/python3.13/_collections_abc.py', 'PYMODULE'), ('collections', '/usr/lib/python3.13/collections/__init__.py', 'PYMODULE'),
('io', '/usr/lib/python3.13/io.py', 'PYMODULE'), ('io', '/usr/lib/python3.13/io.py', 'PYMODULE'),
('locale', '/usr/lib/python3.13/locale.py', 'PYMODULE'), ('re._parser', '/usr/lib/python3.13/re/_parser.py', 'PYMODULE'),
('re._constants', '/usr/lib/python3.13/re/_constants.py', 'PYMODULE'),
('re._compiler', '/usr/lib/python3.13/re/_compiler.py', 'PYMODULE'),
('re._casefix', '/usr/lib/python3.13/re/_casefix.py', 'PYMODULE'),
('re', '/usr/lib/python3.13/re/__init__.py', 'PYMODULE'),
('operator', '/usr/lib/python3.13/operator.py', 'PYMODULE'),
('_collections_abc', '/usr/lib/python3.13/_collections_abc.py', 'PYMODULE'),
('sre_constants', '/usr/lib/python3.13/sre_constants.py', 'PYMODULE'),
('reprlib', '/usr/lib/python3.13/reprlib.py', 'PYMODULE'),
('keyword', '/usr/lib/python3.13/keyword.py', 'PYMODULE'),
('codecs', '/usr/lib/python3.13/codecs.py', 'PYMODULE'),
('genericpath', '/usr/lib/python3.13/genericpath.py', 'PYMODULE'),
('enum', '/usr/lib/python3.13/enum.py', 'PYMODULE'),
('sre_parse', '/usr/lib/python3.13/sre_parse.py', 'PYMODULE'),
('_weakrefset', '/usr/lib/python3.13/_weakrefset.py', 'PYMODULE'),
('copyreg', '/usr/lib/python3.13/copyreg.py', 'PYMODULE'),
('weakref', '/usr/lib/python3.13/weakref.py', 'PYMODULE'),
('encodings.zlib_codec', ('encodings.zlib_codec',
'/usr/lib/python3.13/encodings/zlib_codec.py', '/usr/lib/python3.13/encodings/zlib_codec.py',
'PYMODULE'), 'PYMODULE'),
@@ -590,23 +612,10 @@
('encodings.ascii', '/usr/lib/python3.13/encodings/ascii.py', 'PYMODULE'), ('encodings.ascii', '/usr/lib/python3.13/encodings/ascii.py', 'PYMODULE'),
('encodings.aliases', '/usr/lib/python3.13/encodings/aliases.py', 'PYMODULE'), ('encodings.aliases', '/usr/lib/python3.13/encodings/aliases.py', 'PYMODULE'),
('encodings', '/usr/lib/python3.13/encodings/__init__.py', 'PYMODULE'), ('encodings', '/usr/lib/python3.13/encodings/__init__.py', 'PYMODULE'),
('enum', '/usr/lib/python3.13/enum.py', 'PYMODULE'),
('weakref', '/usr/lib/python3.13/weakref.py', 'PYMODULE'),
('_weakrefset', '/usr/lib/python3.13/_weakrefset.py', 'PYMODULE'),
('warnings', '/usr/lib/python3.13/warnings.py', 'PYMODULE'),
('sre_constants', '/usr/lib/python3.13/sre_constants.py', 'PYMODULE'),
('heapq', '/usr/lib/python3.13/heapq.py', 'PYMODULE'),
('codecs', '/usr/lib/python3.13/codecs.py', 'PYMODULE'),
('traceback', '/usr/lib/python3.13/traceback.py', 'PYMODULE'), ('traceback', '/usr/lib/python3.13/traceback.py', 'PYMODULE'),
('linecache', '/usr/lib/python3.13/linecache.py', 'PYMODULE'), ('stat', '/usr/lib/python3.13/stat.py', 'PYMODULE'),
('re._parser', '/usr/lib/python3.13/re/_parser.py', 'PYMODULE'), ('warnings', '/usr/lib/python3.13/warnings.py', 'PYMODULE'),
('re._constants', '/usr/lib/python3.13/re/_constants.py', 'PYMODULE'), ('os', '/usr/lib/python3.13/os.py', 'PYMODULE'),
('re._compiler', '/usr/lib/python3.13/re/_compiler.py', 'PYMODULE'), ('sre_compile', '/usr/lib/python3.13/sre_compile.py', 'PYMODULE'),
('re._casefix', '/usr/lib/python3.13/re/_casefix.py', 'PYMODULE'), ('heapq', '/usr/lib/python3.13/heapq.py', 'PYMODULE'),
('re', '/usr/lib/python3.13/re/__init__.py', 'PYMODULE'), ('functools', '/usr/lib/python3.13/functools.py', 'PYMODULE')])
('posixpath', '/usr/lib/python3.13/posixpath.py', 'PYMODULE'),
('reprlib', '/usr/lib/python3.13/reprlib.py', 'PYMODULE'),
('abc', '/usr/lib/python3.13/abc.py', 'PYMODULE'),
('collections', '/usr/lib/python3.13/collections/__init__.py', 'PYMODULE'),
('ntpath', '/usr/lib/python3.13/ntpath.py', 'PYMODULE'),
('stat', '/usr/lib/python3.13/stat.py', 'PYMODULE')])

View File

@@ -90,8 +90,8 @@
('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so', ('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
'/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so', '/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
'EXTENSION'), 'EXTENSION'),
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'), ('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'),
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'), ('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'),
('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'), ('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'),
('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'), ('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'),
@@ -114,17 +114,20 @@
('securecheck/assets/securecheck-icon.svg', ('securecheck/assets/securecheck-icon.svg',
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg', '/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/METADATA', ('securecheck/assets/system_hardening.sh',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA', '/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/WHEEL', ('wheel-0.46.1.dist-info/entry_points.txt',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL', '/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/INSTALLER', ('wheel-0.46.1.dist-info/INSTALLER',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER', '/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/entry_points.txt', ('wheel-0.46.1.dist-info/WHEEL',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt', '/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
'DATA'),
('wheel-0.46.1.dist-info/METADATA',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
'DATA'), 'DATA'),
('base_library.zip', ('base_library.zip',
'/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip', '/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip',
@@ -132,7 +135,7 @@
[], [],
False, False,
False, False,
1775421404, 1775457449,
[('run', [('run',
'/home/tuxgyver/.local/lib/python3.13/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run', '/home/tuxgyver/.local/lib/python3.13/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run',
'EXECUTABLE')], 'EXECUTABLE')],

View File

@@ -85,8 +85,8 @@
('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so', ('python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
'/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so', '/usr/lib/python3.13/lib-dynload/_curses.cpython-313-x86_64-linux-gnu.so',
'EXTENSION'), 'EXTENSION'),
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'), ('libexpat.so.1', '/lib/x86_64-linux-gnu/libexpat.so.1', 'BINARY'),
('libz.so.1', '/lib/x86_64-linux-gnu/libz.so.1', 'BINARY'),
('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'), ('libcrypto.so.3', '/lib/x86_64-linux-gnu/libcrypto.so.3', 'BINARY'),
('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'), ('libzstd.so.1', '/lib/x86_64-linux-gnu/libzstd.so.1', 'BINARY'),
('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'), ('liblzma.so.5', '/lib/x86_64-linux-gnu/liblzma.so.5', 'BINARY'),
@@ -109,17 +109,20 @@
('securecheck/assets/securecheck-icon.svg', ('securecheck/assets/securecheck-icon.svg',
'/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg', '/home/tuxgyver/scripts/securecheck/securecheck/assets/securecheck-icon.svg',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/METADATA', ('securecheck/assets/system_hardening.sh',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA', '/home/tuxgyver/scripts/securecheck/securecheck/assets/system_hardening.sh',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/WHEEL', ('wheel-0.46.1.dist-info/entry_points.txt',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL', '/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/INSTALLER', ('wheel-0.46.1.dist-info/INSTALLER',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER', '/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/INSTALLER',
'DATA'), 'DATA'),
('wheel-0.46.1.dist-info/entry_points.txt', ('wheel-0.46.1.dist-info/WHEEL',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/entry_points.txt', '/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/WHEEL',
'DATA'),
('wheel-0.46.1.dist-info/METADATA',
'/usr/lib/python3/dist-packages/wheel-0.46.1.dist-info/METADATA',
'DATA'), 'DATA'),
('base_library.zip', ('base_library.zip',
'/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip', '/home/tuxgyver/scripts/securecheck/build/securecheck/base_library.zip',

Binary file not shown.

View File

@@ -178,6 +178,9 @@
('securecheck.storage', ('securecheck.storage',
'/home/tuxgyver/scripts/securecheck/securecheck/storage.py', '/home/tuxgyver/scripts/securecheck/securecheck/storage.py',
'PYMODULE'), 'PYMODULE'),
('securecheck.summary_utils',
'/home/tuxgyver/scripts/securecheck/securecheck/summary_utils.py',
'PYMODULE'),
('securecheck.system_info', ('securecheck.system_info',
'/home/tuxgyver/scripts/securecheck/securecheck/system_info.py', '/home/tuxgyver/scripts/securecheck/securecheck/system_info.py',
'PYMODULE'), 'PYMODULE'),

Binary file not shown.

Binary file not shown.

View File

@@ -18,8 +18,8 @@ missing module named _frozen_importlib_external - imported by importlib._bootstr
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level) excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named 'collections.abc' - imported by traceback (top-level), typing (top-level), inspect (top-level), logging (top-level), importlib.resources.readers (top-level), selectors (top-level), tracemalloc (top-level), http.client (top-level), pkg_resources (top-level), setuptools (top-level), setuptools._distutils.filelist (top-level), setuptools._distutils.util (top-level), jaraco.functools (top-level), more_itertools.more (top-level), more_itertools.recipes (top-level), setuptools._distutils._modified (top-level), setuptools._distutils.compat (top-level), setuptools._distutils.spawn (top-level), typing_extensions (top-level), asyncio.base_events (top-level), asyncio.coroutines (top-level), setuptools._distutils.compilers.C.base (top-level), setuptools._distutils.fancy_getopt (top-level), setuptools._reqs (top-level), setuptools.discovery (top-level), setuptools.dist (top-level), setuptools._distutils.command.bdist (top-level), setuptools._distutils.core (top-level), setuptools._distutils.cmd (top-level), setuptools._distutils.dist (top-level), configparser (top-level), setuptools._distutils.extension (top-level), setuptools.config.setupcfg (top-level), setuptools.config.expand (top-level), setuptools.config.pyprojecttoml (top-level), setuptools.config._apply_pyprojecttoml (top-level), tomllib._parser (top-level), setuptools._vendor.tomli._parser (top-level), setuptools.command.egg_info (top-level), setuptools._distutils.command.build (top-level), setuptools._distutils.command.sdist (top-level), setuptools.glob (top-level), setuptools.command._requirestxt (top-level), setuptools.command.bdist_wheel (top-level), platformdirs.api (conditional), platformdirs.windows (conditional), platformdirs.unix (conditional), setuptools._distutils.command.build_ext (top-level), _pyrepl.types (top-level), _pyrepl.readline (top-level), setuptools._distutils.compilers.C.msvc (top-level) missing module named 'collections.abc' - imported by traceback (top-level), typing (top-level), inspect (top-level), logging (top-level), importlib.resources.readers (top-level), selectors (top-level), tracemalloc (top-level), http.client (top-level), pkg_resources (top-level), setuptools (top-level), setuptools._distutils.filelist (top-level), setuptools._distutils.util (top-level), jaraco.functools (top-level), more_itertools.more (top-level), more_itertools.recipes (top-level), setuptools._distutils._modified (top-level), setuptools._distutils.compat (top-level), setuptools._distutils.spawn (top-level), typing_extensions (top-level), asyncio.base_events (top-level), asyncio.coroutines (top-level), setuptools._distutils.compilers.C.base (top-level), setuptools._distutils.fancy_getopt (top-level), setuptools._reqs (top-level), setuptools.discovery (top-level), setuptools.dist (top-level), setuptools._distutils.command.bdist (top-level), setuptools._distutils.core (top-level), setuptools._distutils.cmd (top-level), setuptools._distutils.dist (top-level), configparser (top-level), setuptools._distutils.extension (top-level), setuptools.config.setupcfg (top-level), setuptools.config.expand (top-level), setuptools.config.pyprojecttoml (top-level), setuptools.config._apply_pyprojecttoml (top-level), tomllib._parser (top-level), setuptools._vendor.tomli._parser (top-level), setuptools.command.egg_info (top-level), setuptools._distutils.command.build (top-level), setuptools._distutils.command.sdist (top-level), setuptools.glob (top-level), setuptools.command._requirestxt (top-level), setuptools.command.bdist_wheel (top-level), platformdirs.api (conditional), platformdirs.windows (conditional), platformdirs.unix (conditional), setuptools._distutils.command.build_ext (top-level), _pyrepl.types (top-level), _pyrepl.readline (top-level), setuptools._distutils.compilers.C.msvc (top-level)
missing module named winreg - imported by importlib._bootstrap_external (conditional), mimetypes (optional), urllib.request (delayed, conditional, optional), platform (delayed, optional), platformdirs.windows (delayed, optional), setuptools._distutils.compilers.C.msvc (top-level), setuptools.msvc (conditional) missing module named winreg - imported by importlib._bootstrap_external (conditional), mimetypes (optional), urllib.request (delayed, conditional, optional), platform (delayed, optional), platformdirs.windows (delayed, optional), setuptools._distutils.compilers.C.msvc (top-level), setuptools.msvc (conditional)
missing module named nt - imported by shutil (conditional), importlib._bootstrap_external (conditional), _colorize (delayed, conditional, optional), os (delayed, conditional, optional), ntpath (optional), ctypes (delayed, conditional), _pyrepl.windows_console (delayed, optional) missing module named nt - imported by shutil (conditional), importlib._bootstrap_external (conditional), ntpath (optional), _colorize (delayed, conditional, optional), os (delayed, conditional, optional), ctypes (delayed, conditional), _pyrepl.windows_console (delayed, optional)
missing module named _winapi - imported by encodings (delayed, conditional, optional), shutil (conditional), subprocess (conditional), ntpath (optional), mimetypes (optional), sysconfig (delayed), multiprocessing.connection (optional), multiprocessing.spawn (delayed, conditional), multiprocessing.reduction (conditional), multiprocessing.shared_memory (conditional), multiprocessing.heap (conditional), multiprocessing.popen_spawn_win32 (top-level), asyncio.windows_events (top-level), asyncio.windows_utils (top-level) missing module named _winapi - imported by encodings (delayed, conditional, optional), shutil (conditional), ntpath (optional), subprocess (conditional), mimetypes (optional), sysconfig (delayed), multiprocessing.connection (optional), multiprocessing.spawn (delayed, conditional), multiprocessing.reduction (conditional), multiprocessing.shared_memory (conditional), multiprocessing.heap (conditional), multiprocessing.popen_spawn_win32 (top-level), asyncio.windows_events (top-level), asyncio.windows_utils (top-level)
missing module named msvcrt - imported by subprocess (optional), getpass (optional), multiprocessing.spawn (delayed, conditional), multiprocessing.popen_spawn_win32 (top-level), asyncio.windows_events (top-level), asyncio.windows_utils (top-level), _pyrepl.windows_console (top-level) missing module named msvcrt - imported by subprocess (optional), getpass (optional), multiprocessing.spawn (delayed, conditional), multiprocessing.popen_spawn_win32 (top-level), asyncio.windows_events (top-level), asyncio.windows_utils (top-level), _pyrepl.windows_console (top-level)
missing module named win32evtlog - imported by logging.handlers (delayed, optional) missing module named win32evtlog - imported by logging.handlers (delayed, optional)
missing module named win32evtlogutil - imported by logging.handlers (delayed, optional) missing module named win32evtlogutil - imported by logging.handlers (delayed, optional)

View File

@@ -352,6 +352,7 @@ imported by:
&#8226; <a href="#securecheck.models">securecheck.models</a> &#8226; <a href="#securecheck.models">securecheck.models</a>
&#8226; <a href="#securecheck.status">securecheck.status</a> &#8226; <a href="#securecheck.status">securecheck.status</a>
&#8226; <a href="#securecheck.storage">securecheck.storage</a> &#8226; <a href="#securecheck.storage">securecheck.storage</a>
&#8226; <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
&#8226; <a href="#securecheck.system_info">securecheck.system_info</a> &#8226; <a href="#securecheck.system_info">securecheck.system_info</a>
&#8226; <a href="#securecheck.tasks">securecheck.tasks</a> &#8226; <a href="#securecheck.tasks">securecheck.tasks</a>
&#8226; <a href="#setuptools">setuptools</a> &#8226; <a href="#setuptools">setuptools</a>
@@ -10444,9 +10445,12 @@ imported by:
&#8226; <a href="#random">random</a> &#8226; <a href="#random">random</a>
&#8226; <a href="#run.py">run.py</a> &#8226; <a href="#run.py">run.py</a>
&#8226; <a href="#runpy">runpy</a> &#8226; <a href="#runpy">runpy</a>
&#8226; <a href="#securecheck.__main__">securecheck.__main__</a>
&#8226; <a href="#securecheck.config">securecheck.config</a> &#8226; <a href="#securecheck.config">securecheck.config</a>
&#8226; <a href="#securecheck.executor">securecheck.executor</a> &#8226; <a href="#securecheck.executor">securecheck.executor</a>
&#8226; <a href="#securecheck.logging_utils">securecheck.logging_utils</a>
&#8226; <a href="#securecheck.system_info">securecheck.system_info</a> &#8226; <a href="#securecheck.system_info">securecheck.system_info</a>
&#8226; <a href="#securecheck.tasks">securecheck.tasks</a>
&#8226; <a href="#setuptools">setuptools</a> &#8226; <a href="#setuptools">setuptools</a>
&#8226; <a href="#setuptools._core_metadata">setuptools._core_metadata</a> &#8226; <a href="#setuptools._core_metadata">setuptools._core_metadata</a>
&#8226; <a href="#setuptools._distutils.archive_util">setuptools._distutils.archive_util</a> &#8226; <a href="#setuptools._distutils.archive_util">setuptools._distutils.archive_util</a>
@@ -11840,7 +11844,7 @@ imported by:
&#8226; <a href="#re._parser">re._parser</a> &#8226; <a href="#re._parser">re._parser</a>
&#8226; <a href="#rlcompleter">rlcompleter</a> &#8226; <a href="#rlcompleter">rlcompleter</a>
&#8226; <a href="#run.py">run.py</a> &#8226; <a href="#run.py">run.py</a>
&#8226; <a href="#securecheck.app">securecheck.app</a> &#8226; <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
&#8226; <a href="#securecheck.tasks">securecheck.tasks</a> &#8226; <a href="#securecheck.tasks">securecheck.tasks</a>
&#8226; <a href="#setuptools">setuptools</a> &#8226; <a href="#setuptools">setuptools</a>
&#8226; <a href="#setuptools._distutils.cmd">setuptools._distutils.cmd</a> &#8226; <a href="#setuptools._distutils.cmd">setuptools._distutils.cmd</a>
@@ -12115,6 +12119,7 @@ imported by:
&#8226; <a href="#securecheck.models">securecheck.models</a> &#8226; <a href="#securecheck.models">securecheck.models</a>
&#8226; <a href="#securecheck.status">securecheck.status</a> &#8226; <a href="#securecheck.status">securecheck.status</a>
&#8226; <a href="#securecheck.storage">securecheck.storage</a> &#8226; <a href="#securecheck.storage">securecheck.storage</a>
&#8226; <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
&#8226; <a href="#securecheck.system_info">securecheck.system_info</a> &#8226; <a href="#securecheck.system_info">securecheck.system_info</a>
&#8226; <a href="#securecheck.tasks">securecheck.tasks</a> &#8226; <a href="#securecheck.tasks">securecheck.tasks</a>
@@ -12130,6 +12135,7 @@ imports:
<a href="#__future__">__future__</a> <a href="#__future__">__future__</a>
&#8226; <a href="#argparse">argparse</a> &#8226; <a href="#argparse">argparse</a>
&#8226; <a href="#datetime">datetime</a> &#8226; <a href="#datetime">datetime</a>
&#8226; <a href="#os">os</a>
&#8226; <a href="#securecheck">securecheck</a> &#8226; <a href="#securecheck">securecheck</a>
&#8226; <a href="#securecheck.app">securecheck.app</a> &#8226; <a href="#securecheck.app">securecheck.app</a>
&#8226; <a href="#securecheck.catalog">securecheck.catalog</a> &#8226; <a href="#securecheck.catalog">securecheck.catalog</a>
@@ -12159,12 +12165,12 @@ imports:
&#8226; <a href="#collections">collections</a> &#8226; <a href="#collections">collections</a>
&#8226; <a href="#curses">curses</a> &#8226; <a href="#curses">curses</a>
&#8226; <a href="#dataclasses">dataclasses</a> &#8226; <a href="#dataclasses">dataclasses</a>
&#8226; <a href="#re">re</a>
&#8226; <a href="#securecheck">securecheck</a> &#8226; <a href="#securecheck">securecheck</a>
&#8226; <a href="#securecheck.assets">securecheck.assets</a> &#8226; <a href="#securecheck.assets">securecheck.assets</a>
&#8226; <a href="#securecheck.models">securecheck.models</a> &#8226; <a href="#securecheck.models">securecheck.models</a>
&#8226; <a href="#securecheck.status">securecheck.status</a> &#8226; <a href="#securecheck.status">securecheck.status</a>
&#8226; <a href="#securecheck.storage">securecheck.storage</a> &#8226; <a href="#securecheck.storage">securecheck.storage</a>
&#8226; <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
&#8226; <a href="#securecheck.system_info">securecheck.system_info</a> &#8226; <a href="#securecheck.system_info">securecheck.system_info</a>
&#8226; <a href="#textwrap">textwrap</a> &#8226; <a href="#textwrap">textwrap</a>
&#8226; <a href="#typing">typing</a> &#8226; <a href="#typing">typing</a>
@@ -12280,6 +12286,7 @@ imports:
<a href="#__future__">__future__</a> <a href="#__future__">__future__</a>
&#8226; <a href="#logging">logging</a> &#8226; <a href="#logging">logging</a>
&#8226; <a href="#logging.handlers">logging.handlers</a> &#8226; <a href="#logging.handlers">logging.handlers</a>
&#8226; <a href="#os">os</a>
&#8226; <a href="#pathlib">pathlib</a> &#8226; <a href="#pathlib">pathlib</a>
&#8226; <a href="#securecheck">securecheck</a> &#8226; <a href="#securecheck">securecheck</a>
@@ -12311,6 +12318,7 @@ imported by:
&#8226; <a href="#securecheck.catalog">securecheck.catalog</a> &#8226; <a href="#securecheck.catalog">securecheck.catalog</a>
&#8226; <a href="#securecheck.executor">securecheck.executor</a> &#8226; <a href="#securecheck.executor">securecheck.executor</a>
&#8226; <a href="#securecheck.storage">securecheck.storage</a> &#8226; <a href="#securecheck.storage">securecheck.storage</a>
&#8226; <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
&#8226; <a href="#securecheck.tasks">securecheck.tasks</a> &#8226; <a href="#securecheck.tasks">securecheck.tasks</a>
</div> </div>
@@ -12361,6 +12369,26 @@ imported by:
</div> </div>
<div class="node">
<a name="securecheck.summary_utils"></a>
<a target="code" href="/home/tuxgyver/scripts/securecheck/securecheck/summary_utils.py" type="text/plain"><tt>securecheck.summary_utils</tt></a>
<span class="moduletype">SourceModule</span> <div class="import">
imports:
<a href="#__future__">__future__</a>
&#8226; <a href="#re">re</a>
&#8226; <a href="#securecheck">securecheck</a>
&#8226; <a href="#securecheck.models">securecheck.models</a>
&#8226; <a href="#typing">typing</a>
</div>
<div class="import">
imported by:
<a href="#securecheck.app">securecheck.app</a>
</div>
</div>
<div class="node"> <div class="node">
<a name="securecheck.system_info"></a> <a name="securecheck.system_info"></a>
<a target="code" href="/home/tuxgyver/scripts/securecheck/securecheck/system_info.py" type="text/plain"><tt>securecheck.system_info</tt></a> <a target="code" href="/home/tuxgyver/scripts/securecheck/securecheck/system_info.py" type="text/plain"><tt>securecheck.system_info</tt></a>
@@ -12394,12 +12422,15 @@ imports:
<a href="#__future__">__future__</a> <a href="#__future__">__future__</a>
&#8226; <a href="#datetime">datetime</a> &#8226; <a href="#datetime">datetime</a>
&#8226; <a href="#json">json</a> &#8226; <a href="#json">json</a>
&#8226; <a href="#os">os</a>
&#8226; <a href="#pathlib">pathlib</a> &#8226; <a href="#pathlib">pathlib</a>
&#8226; <a href="#re">re</a> &#8226; <a href="#re">re</a>
&#8226; <a href="#securecheck">securecheck</a> &#8226; <a href="#securecheck">securecheck</a>
&#8226; <a href="#securecheck.assets">securecheck.assets</a> &#8226; <a href="#securecheck.assets">securecheck.assets</a>
&#8226; <a href="#securecheck.executor">securecheck.executor</a> &#8226; <a href="#securecheck.executor">securecheck.executor</a>
&#8226; <a href="#securecheck.models">securecheck.models</a> &#8226; <a href="#securecheck.models">securecheck.models</a>
&#8226; <a href="#stat">stat</a>
&#8226; <a href="#tempfile">tempfile</a>
</div> </div>
<div class="import"> <div class="import">
@@ -16098,6 +16129,7 @@ imported by:
&#8226; <a href="#posixpath">posixpath</a> &#8226; <a href="#posixpath">posixpath</a>
&#8226; <a href="#run.py">run.py</a> &#8226; <a href="#run.py">run.py</a>
&#8226; <a href="#securecheck.executor">securecheck.executor</a> &#8226; <a href="#securecheck.executor">securecheck.executor</a>
&#8226; <a href="#securecheck.tasks">securecheck.tasks</a>
&#8226; <a href="#setuptools._core_metadata">setuptools._core_metadata</a> &#8226; <a href="#setuptools._core_metadata">setuptools._core_metadata</a>
&#8226; <a href="#setuptools._distutils.file_util">setuptools._distutils.file_util</a> &#8226; <a href="#setuptools._distutils.file_util">setuptools._distutils.file_util</a>
&#8226; <a href="#setuptools._shutil">setuptools._shutil</a> &#8226; <a href="#setuptools._shutil">setuptools._shutil</a>
@@ -16612,6 +16644,7 @@ imported by:
&#8226; <a href="#pkg_resources">pkg_resources</a> &#8226; <a href="#pkg_resources">pkg_resources</a>
&#8226; <a href="#securecheck.config">securecheck.config</a> &#8226; <a href="#securecheck.config">securecheck.config</a>
&#8226; <a href="#securecheck.executor">securecheck.executor</a> &#8226; <a href="#securecheck.executor">securecheck.executor</a>
&#8226; <a href="#securecheck.tasks">securecheck.tasks</a>
&#8226; <a href="#setuptools._core_metadata">setuptools._core_metadata</a> &#8226; <a href="#setuptools._core_metadata">setuptools._core_metadata</a>
&#8226; <a href="#setuptools._distutils.compilers.C.base">setuptools._distutils.compilers.C.base</a> &#8226; <a href="#setuptools._distutils.compilers.C.base">setuptools._distutils.compilers.C.base</a>
&#8226; <a href="#setuptools._distutils.util">setuptools._distutils.util</a> &#8226; <a href="#setuptools._distutils.util">setuptools._distutils.util</a>
@@ -17166,6 +17199,7 @@ imported by:
&#8226; <a href="#platformdirs.windows">platformdirs.windows</a> &#8226; <a href="#platformdirs.windows">platformdirs.windows</a>
&#8226; <a href="#securecheck.app">securecheck.app</a> &#8226; <a href="#securecheck.app">securecheck.app</a>
&#8226; <a href="#securecheck.models">securecheck.models</a> &#8226; <a href="#securecheck.models">securecheck.models</a>
&#8226; <a href="#securecheck.summary_utils">securecheck.summary_utils</a>
&#8226; <a href="#setuptools">setuptools</a> &#8226; <a href="#setuptools">setuptools</a>
&#8226; <a href="#setuptools._distutils._modified">setuptools._distutils._modified</a> &#8226; <a href="#setuptools._distutils._modified">setuptools._distutils._modified</a>
&#8226; <a href="#setuptools._distutils.archive_util">setuptools._distutils.archive_util</a> &#8226; <a href="#setuptools._distutils.archive_util">setuptools._distutils.archive_util</a>

BIN
dist/securecheck vendored

Binary file not shown.

View File

@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import os
import sys import sys
from datetime import datetime from datetime import datetime
@@ -14,6 +15,20 @@ from .storage import ScenarioStore
from .system_info import detect_system from .system_info import detect_system
def ensure_root() -> None:
if os.geteuid() == 0:
return
if os.environ.get("SECURECHECK_SKIP_SUDO") == "1":
return
args = sys.argv[1:]
if getattr(sys, "frozen", False):
target = sys.argv[0]
cmd = ["sudo", "-E", target, *args]
else:
cmd = ["sudo", "-E", sys.executable, "-m", "securecheck", *args]
os.execvp("sudo", cmd)
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="SecureCheck - console semi-graphique pour contrôles sécurité Linux") parser = argparse.ArgumentParser(description="SecureCheck - console semi-graphique pour contrôles sécurité Linux")
parser.add_argument("--dry-run", action="store_true", help="Simule les commandes sans modifier le système") parser.add_argument("--dry-run", action="store_true", help="Simule les commandes sans modifier le système")
@@ -66,6 +81,7 @@ def print_summary(results, run_log_path, system) -> None:
def main() -> int: def main() -> int:
ensure_root()
args = parse_args() args = parse_args()
paths = build_paths() paths = build_paths()
ensure_app_dirs(paths) ensure_app_dirs(paths)
@@ -77,6 +93,7 @@ def main() -> int:
store = ScenarioStore(paths.scenario_file, builtin_scenarios()) store = ScenarioStore(paths.scenario_file, builtin_scenarios())
if args.list_scenarios: if args.list_scenarios:
print(f"Scénarios stockés dans {paths.scenario_file}")
for scenario in store.list_all(): for scenario in store.list_all():
print(f"{scenario.name}: {scenario.description}") print(f"{scenario.name}: {scenario.description}")
return 0 return 0

Binary file not shown.

View File

@@ -1,7 +1,6 @@
from __future__ import annotations from __future__ import annotations
import curses import curses
import re
import textwrap import textwrap
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
@@ -11,6 +10,7 @@ from .assets import banner_text
from .models import Scenario, TaskDefinition, TaskResult from .models import Scenario, TaskDefinition, TaskResult
from .status import StatusItem from .status import StatusItem
from .storage import ScenarioStore from .storage import ScenarioStore
from .summary_utils import SCORE_PREFIXES, clean_text, collect_details
from .system_info import SystemInfo from .system_info import SystemInfo
@@ -334,18 +334,12 @@ class SecureCheckTUI:
class RunSummaryTUI: class RunSummaryTUI:
ANSI_RE = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")
def __init__(self, results: list[TaskResult], status_items: list[StatusItem], run_log_path: str) -> None: def __init__(self, results: list[TaskResult], status_items: list[StatusItem], run_log_path: str) -> None:
self.results = results self.results = results
self.status_items = status_items self.status_items = status_items
self.run_log_path = run_log_path self.run_log_path = run_log_path
self.scroll_offset = 0 self.scroll_offset = 0
@classmethod
def _clean(cls, text: str) -> str:
return cls.ANSI_RE.sub("", text)
def run(self) -> None: def run(self) -> None:
curses.wrapper(self._main) curses.wrapper(self._main)
@@ -368,8 +362,7 @@ class RunSummaryTUI:
height, width = stdscr.getmaxyx() height, width = stdscr.getmaxyx()
ok_count = sum(1 for result in self.results if result.success) ok_count = sum(1 for result in self.results if result.success)
ko_count = len(self.results) - ok_count ko_count = len(self.results) - ok_count
score_lines: list[str] = [] score_lines, notif_lines = collect_details(self.results)
notif_lines: list[str] = []
entries: list[tuple[str, int]] = [] entries: list[tuple[str, int]] = []
entries.append((f"OK: {ok_count} | ECHEC: {ko_count} | Appuie sur une touche pour revenir", curses.color_pair(Palette.HEADER) | curses.A_BOLD)) entries.append((f"OK: {ok_count} | ECHEC: {ko_count} | Appuie sur une touche pour revenir", curses.color_pair(Palette.HEADER) | curses.A_BOLD))
for result in self.results: for result in self.results:
@@ -377,12 +370,10 @@ class RunSummaryTUI:
color = curses.color_pair(Palette.SUCCESS if result.success else Palette.ERROR) color = curses.color_pair(Palette.SUCCESS if result.success else Palette.ERROR)
entries.append((f"{status:<4} {result.label} ({result.duration_seconds:.1f}s)", color | curses.A_BOLD)) entries.append((f"{status:<4} {result.label} ({result.duration_seconds:.1f}s)", color | curses.A_BOLD))
for detail in result.details: for detail in result.details:
clean = self._clean(detail) clean = clean_text(detail).strip()
if clean.startswith("Score Lynis") or clean.startswith("Hardening index"): if any(clean.startswith(prefix) for prefix in SCORE_PREFIXES):
score_lines.append(clean)
continue continue
if clean.startswith("Modifications") or clean.strip().startswith(""): if clean.startswith("Modifications") or clean.startswith(""):
notif_lines.append(clean)
continue continue
wrapped = textwrap.wrap(clean, width - 9) or [""] wrapped = textwrap.wrap(clean, width - 9) or [""]
for line in wrapped: for line in wrapped:

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ from .tasks import (
log_rotation, log_rotation,
lynis_audit, lynis_audit,
rootkit_check, rootkit_check,
system_hardening,
system_update, system_update,
utilities_setup, utilities_setup,
zram_setup, zram_setup,
@@ -98,6 +99,14 @@ def task_catalog() -> list[TaskDefinition]:
handler=lambda context: None, handler=lambda context: None,
default_selected=False, default_selected=False,
), ),
TaskDefinition(
key="system_hardening",
label="Durcissement système (hardening)",
description="Applique un durcissement complet : sysctl, SSH, PAM, modules noyau, permissions, AIDE, bannières, limites de sécurité.",
category="Sécurité",
handler=lambda context: None,
default_selected=False,
),
] ]
handlers = { handlers = {
@@ -111,6 +120,7 @@ def task_catalog() -> list[TaskDefinition]:
"zram_setup": zram_setup, "zram_setup": zram_setup,
"firewall_setup": firewall_setup, "firewall_setup": firewall_setup,
"docker_setup": docker_setup, "docker_setup": docker_setup,
"system_hardening": system_hardening,
} }
return [bind(task, handlers[task.key]) for task in base] return [bind(task, handlers[task.key]) for task in base]

View File

@@ -1,15 +1,28 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
import os
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from pathlib import Path from pathlib import Path
_LOG_FILE_MODE = 0o644
def _prepare_log_file(path: Path) -> None:
"""Crée le fichier de log s'il n'existe pas et fixe ses permissions à 644."""
path.parent.mkdir(parents=True, exist_ok=True)
if not path.exists():
path.touch()
os.chmod(path, _LOG_FILE_MODE)
def setup_logging(log_file: Path) -> logging.Logger: def setup_logging(log_file: Path) -> logging.Logger:
logger = logging.getLogger("securecheck") logger = logging.getLogger("securecheck")
if logger.handlers: if logger.handlers:
return logger return logger
_prepare_log_file(log_file)
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
logger.propagate = False logger.propagate = False
formatter = logging.Formatter( formatter = logging.Formatter(
@@ -24,6 +37,8 @@ def setup_logging(log_file: Path) -> logging.Logger:
def attach_run_handler(logger: logging.Logger, run_log_file: Path) -> RotatingFileHandler: def attach_run_handler(logger: logging.Logger, run_log_file: Path) -> RotatingFileHandler:
_prepare_log_file(run_log_file)
formatter = logging.Formatter( formatter = logging.Formatter(
fmt="%(asctime)s | %(levelname)s | %(message)s", fmt="%(asctime)s | %(levelname)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S", datefmt="%Y-%m-%d %H:%M:%S",

View File

@@ -91,12 +91,10 @@ def collect_status(system: SystemInfo) -> list[StatusItem]:
docker_active = _command_exists("docker") and _service_active("docker.service") docker_active = _command_exists("docker") and _service_active("docker.service")
fail2ban_active = _command_exists("fail2ban-client") and _service_active("fail2ban.service") fail2ban_active = _command_exists("fail2ban-client") and _service_active("fail2ban.service")
services.append(StatusItem("Services", "Service Docker", docker_active, "actif" if docker_active else "inactif")) services.append(StatusItem("Services", "Docker", docker_active, "actif" if docker_active else "inactif"))
services.append(StatusItem("Services", "Service fail2ban", fail2ban_active, "actif" if fail2ban_active else "inactif")) services.append(StatusItem("Services", "fail2ban", fail2ban_active, "actif" if fail2ban_active else "inactif"))
avahi_running = _command_exists("avahi-daemon") and _service_active("avahi-daemon") avahi_running = _command_exists("avahi-daemon") and _service_active("avahi-daemon")
services.append(StatusItem("Services", "Avahi", not avahi_running, "désactivé" if not avahi_running else "actif")) services.append(StatusItem("Services", "Avahi", not avahi_running, "désactivé" if not avahi_running else "actif"))
services.append(_binary_status("Services", "Docker", "docker"))
services.append(_binary_status("Services", "fail2ban", "fail2ban-client"))
poste.extend([ poste.extend([
_binary_status("Poste", "zsh", "zsh"), _binary_status("Poste", "zsh", "zsh"),

View File

@@ -0,0 +1,41 @@
from __future__ import annotations
import re
from typing import Sequence
from .models import TaskResult
ANSI_RE = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")
SCORE_PREFIXES = ("Score Lynis", "Hardening index")
RECOMMENDATION_PREFIXES = (
"Modifications recommandées",
"",
)
def clean_text(text: str) -> str:
return ANSI_RE.sub("", text)
def collect_details(results: Sequence[TaskResult]) -> tuple[list[str], list[str]]:
score_lines: list[str] = []
recommendation_lines: list[str] = []
seen_scores: set[str] = set()
seen_recommendations: set[str] = set()
for result in results:
for detail in result.details:
line = clean_text(detail).strip()
if not line:
continue
if any(line.startswith(prefix) for prefix in SCORE_PREFIXES) and line not in seen_scores:
seen_scores.add(line)
score_lines.append(line)
continue
if any(line.startswith(prefix) for prefix in RECOMMENDATION_PREFIXES) and line not in seen_recommendations:
seen_recommendations.add(line)
recommendation_lines.append(line)
return score_lines, recommendation_lines

View File

@@ -1,7 +1,10 @@
from __future__ import annotations from __future__ import annotations
import json import json
import os
import re import re
import stat
import tempfile
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
@@ -195,8 +198,21 @@ def rootkit_check(context: ExecutionContext, task: TaskDefinition) -> TaskResult
report_path = context.paths.report_dir / f"{datetime.now().strftime('%Y%m%d-%H%M%S')}-rootkit-report.json" report_path = context.paths.report_dir / f"{datetime.now().strftime('%Y%m%d-%H%M%S')}-rootkit-report.json"
context.runner.write_text_file(report_path, json.dumps(report_payload, indent=2) + "\n", mode=0o640, requires_root=False) context.runner.write_text_file(report_path, json.dumps(report_payload, indent=2) + "\n", mode=0o640, requires_root=False)
details.append(f"Rapport rootkits: {report_path}") details.append(f"Rapport rootkits: {report_path}")
success = rkhunter_result.returncode == 0 and chkrootkit_result.returncode == 0
return context.make_result(task, success=success, changed=changed, started_at=started_at, details=details, error=None if success else "Vérification rootkit incomplète ou avec alertes") # rkhunter: rc=0 clean, rc=1 warnings trouvés, rc=2+ erreur critique (outil n'a pas pu tourner)
rkhunter_ran = rkhunter_result.returncode <= 1
rkhunter_warnings = [line for line in rkhunter_result.stdout.splitlines() if line.startswith("Warning:")]
if rkhunter_warnings:
details.append(f"rkhunter: {len(rkhunter_warnings)} warning(s) à vérifier dans le rapport")
# chkrootkit: rc=0 signifie que l'outil a tourné (pas qu'il n'y a pas d'infection — les INFECTED sont dans stdout)
chkrootkit_ran = chkrootkit_result.returncode == 0
infected_lines = [line for line in chkrootkit_result.stdout.splitlines() if "INFECTED" in line]
if infected_lines:
details.append(f"chkrootkit: {len(infected_lines)} détection(s) à vérifier dans le rapport")
success = rkhunter_ran and chkrootkit_ran
error = None if success else "Les outils de vérification n'ont pas pu s'exécuter correctement"
return context.make_result(task, success=success, changed=changed, started_at=started_at, details=details, error=error)
def log_rotation(context: ExecutionContext, task: TaskDefinition) -> TaskResult: def log_rotation(context: ExecutionContext, task: TaskDefinition) -> TaskResult:
@@ -434,7 +450,6 @@ def utilities_setup(context: ExecutionContext, task: TaskDefinition) -> TaskResu
context.runner.run(["cp", "-f", str(aide_db_new), "/var/lib/aide/aide.db"], requires_root=True, check=False) context.runner.run(["cp", "-f", str(aide_db_new), "/var/lib/aide/aide.db"], requires_root=True, check=False)
details.append("Base AIDE mise à jour") details.append("Base AIDE mise à jour")
if context.runner.command_exists("systemctl"): if context.runner.command_exists("systemctl"):
context.runner.run(["systemctl", "enable", "--now", "aidecheck.timer"], requires_root=True, check=False)
context.runner.run(["systemctl", "enable", "--now", "dailyaidecheck.timer"], requires_root=True, check=False) context.runner.run(["systemctl", "enable", "--now", "dailyaidecheck.timer"], requires_root=True, check=False)
details.append("Timers AIDE activés") details.append("Timers AIDE activés")
@@ -560,6 +575,83 @@ def docker_setup(context: ExecutionContext, task: TaskDefinition) -> TaskResult:
return _result(context, task, started_at, changed=changed, details=details) return _result(context, task, started_at, changed=changed, details=details)
def system_hardening(context: ExecutionContext, task: TaskDefinition) -> TaskResult:
started_at = datetime.now()
details: list[str] = []
script_content = asset_text("system_hardening.sh")
tmp_dir = context.paths.state_dir
tmp_dir.mkdir(parents=True, exist_ok=True)
script_path: Path | None = None
try:
with tempfile.NamedTemporaryFile(
mode="w", suffix=".sh", delete=False, dir=tmp_dir, encoding="utf-8"
) as fh:
fh.write(script_content)
script_path = Path(fh.name)
current_mode = stat.S_IMODE(script_path.stat().st_mode)
script_path.chmod(current_mode | 0o111)
env = os.environ.copy()
env.update({
"AUTO_YES": "yes",
"AUTO_SSH_PORT": "22",
"AUTO_CHANGE_ROOT_PWD": "no",
"AUTO_DISABLE_ROOT_LOGIN": "no",
"AUTO_SKIP_LYNIS": "no",
"AUTO_ENABLE_FAIL2BAN": "yes",
"AUTO_ENABLE_UFW": "yes",
"AUTO_ENABLE_AIDE": "yes",
"AUTO_ENABLE_CLAMAV": "yes",
"AUTO_SKIP_PORTS_DETECTION": "no",
"DEBIAN_FRONTEND": "noninteractive",
})
result = context.runner.run(
["bash", str(script_path), "--unattended"],
requires_root=True,
check=False,
capture_output=True,
env=env,
)
ok_steps = [line for line in result.stdout.splitlines() if "[OK]" in line]
warn_steps = [line for line in result.stdout.splitlines() if "[WARN]" in line]
err_steps = [line for line in result.stdout.splitlines() if "[ERR]" in line]
details.append(f"{len(ok_steps)} étape(s) réussie(s)")
if warn_steps:
details.append(f"{len(warn_steps)} avertissement(s)")
if err_steps:
details.append(f"{len(err_steps)} erreur(s)")
for line in err_steps[:5]:
details.append(f" {line.strip()}")
score_line = next(
(line for line in result.stdout.splitlines() if "Score:" in line and "Lynis" in line),
None,
)
if score_line:
details.append(score_line.strip())
log_path = Path("/var/log/system_hardening.log")
if log_path.exists():
details.append(f"Log complet: {log_path}")
backup_path = next(Path("/root").glob("backup_hardening_*"), None) if Path("/root").exists() else None
if backup_path:
details.append(f"Sauvegardes: {backup_path}")
success = result.returncode == 0 and not err_steps
error = None if success else f"Le script s'est terminé avec le code {result.returncode}"
return context.make_result(task, success=success, changed=True, started_at=started_at, details=details, error=error)
finally:
if script_path and script_path.exists():
script_path.unlink(missing_ok=True)
def bind(task: TaskDefinition, func) -> TaskDefinition: def bind(task: TaskDefinition, func) -> TaskDefinition:
return TaskDefinition( return TaskDefinition(
key=task.key, key=task.key,