diff --git a/README.md b/README.md index 6dd1d5c..b3e3df1 100644 --- a/README.md +++ b/README.md @@ -245,11 +245,36 @@ destinée au poste **WAPT Console uniquement**, pas au paquet agent. L’install dans le répertoire de templates de la Console sous ce nom exact, puis choisir la clé d’audit `hardeningkitty / hardeningkitty`. Elle exploite le helper Mustache WAPT `Items` pour parcourir les dictionnaires de catégories et de -contrôles. Les valeurs variables utilisent les tags échappés `{{...}}`; aucun -JavaScript ni triple-brace non échappé n’est utilisé. Le template est une vue -de synthèse et d’analyse, pas une preuve de conformité indépendante. Sa syntaxe -a été contrôlée statiquement, mais son rendu dans une Console réelle doit être -validé avant diffusion. +contrôles. WAPT expose le document JSON sous `value`; les informations de +machine, comme `computer_fqdn`, restent à la racine du contexte Mustache. Le +template suit cette structure et les collections sont parcourues avec +`Items value.categories` et `Items value.controls`. + +Lors de l’installation du paquet sur Windows, `install()` copie automatiquement +le template avec les helpers WAPT vers +`makepath(programfiles32, "wapt", "templates")`, soit le répertoire standard +de templates de l’agent/console WAPT 32 bits. Le fichier porte le nom exact +`host_audit_hardeningkitty_hardeningkitty.html`. + +Sur une console macOS/Linux dont le répertoire de templates est +`/opt/wapt/templates`, le paquet Windows ne peut pas effectuer cette copie. +Installer ou mettre à jour le fichier depuis la racine du paquet : + +```sh +sudo install -m 0644 \ + console-templates/host_audit_hardeningkitty_hardeningkitty.html \ + /opt/wapt/templates/host_audit_hardeningkitty_hardeningkitty.html +``` + +Après copie, ouvrir le rapport d’audit et sélectionner la clé +`hardeningkitty / hardeningkitty`. Le template est une vue console; il ne +modifie ni la configuration Defender ni les données du rapport. + +Les valeurs variables utilisent les tags Mustache échappés; aucun JavaScript +ni triple-brace non échappé n’est utilisé. Le template est une vue de synthèse +et d’analyse, pas une preuve de conformité indépendante. Son rendu a été testé +avec le moteur Mustache WAPT sur des données synthétiques, mais doit être +contrôlé dans la version réelle de WAPT Console avant diffusion. Les anciennes clés fragmentées restent éventuellement visibles dans l’historique antérieur à cette version; elles ne sont plus émises par le code diff --git a/console-templates/host_audit_hardeningkitty_hardeningkitty.html b/console-templates/host_audit_hardeningkitty_hardeningkitty.html index 5bbbb1b..f03ff3d 100644 --- a/console-templates/host_audit_hardeningkitty_hardeningkitty.html +++ b/console-templates/host_audit_hardeningkitty_hardeningkitty.html @@ -1,236 +1,163 @@ - + -
-
-
-
COMITARI / HARDENINGKITTY
-

Audit de durcissement Windows

-
Analyse des écarts par rapport au référentiel de sécurité
+
+
+

COMITARI / HARDENINGKITTY

+

Audit de durcissement Windows

+

Analyse des écarts par rapport au référentiel de sécurité

+
+ + {{#value.summary}} +
+ Poste : {{computer_fqdn}} + Système : {{value.host.os_caption}} {{value.host.display_version}} (build {{value.host.build}}) + Exécuté le : {{value.scan.date_utc}} + Source : {{value.scan.source}} + Statut : {{value.status}} +
+ +
+

Vue d'ensemble

+
+
Contrôles vérifiés
{{checked}}
+
Conformes
{{passed}}
+
Non conformes
{{failed}}
+
Conformité
{{compliant_percent}} %
+
Criticité élevée
{{high}}
+
Criticité moyenne
{{medium}}
+
Criticité faible
{{low}}
- {{status}} -
+
+
Score de conformité observé{{passed}} / {{checked}} contrôles conformes
+
Ce rapport est informatif : aucune correction automatique n'a été appliquée. Les écarts sont évalués par rapport au profil sélectionné, pas nécessairement contre une configuration adaptée à tous les environnements.
+ + {{/value.summary}} - {{#error}} -
- Audit incomplet ou indisponible. -

Code : {{error.code}} — type : {{error.type}}

-

{{error.description}}

-

Vérifier les journaux de l'agent WAPT sur le poste concerné.

-
- {{/error}} + {{#value.error}} +
Audit en erreur : {{code}}
{{description}}Type technique : {{type}}
+ {{/value.error}} - {{#summary}} -
- Machine : {{host.os_caption}} {{host.display_version}} (build {{host.build}}) - Référentiel : {{benchmark.name}} - Source : {{scan.source}} - Audit UTC : {{scan.date_utc}} -
-
-
-

Taux de conformité

-
{{compliant_percent}} %
-
-
{{passed}} conformes{{expected}} attendus
-
-
-
{{high}}Écarts élevés
-
{{medium}}Écarts moyens
-
{{low}}Écarts faibles
-
{{passed}}Conformes
-
{{unknown}}Inconnus
-
{{skipped}}Non exécutés
-
-
- {{/summary}} + {{^value.summary}} + {{^value.error}} +
Données d'audit absentes. La valeur reçue ne contient ni synthèse ni erreur structurée.
+ {{/value.error}} + {{/value.summary}} - {{#summary}} - {{#privacy.current_values_published}} -

Valeurs constatées : publication activée. Vérifier les droits d'accès aux données d'audit.

- {{/privacy.current_values_published}} - {{^privacy.current_values_published}} -

Les valeurs effectivement lues sur le poste ne sont pas publiées par défaut. Le CSV détaillé reste disponible localement sur le poste audité.

- {{/privacy.current_values_published}} - -

Écarts à examiner en priorité

-

{{priority_total}} résultats non conformes ou non évalués. Aperçu limité à 25 contrôles, classés par gravité puis catégorie (pas par risque exploitabilité).

-
-
- - + {{#value.categories}} +
+

Répartition par domaine

+
+
IDCatégorieGravitéÉtat
+ - {{#priority_findings}} + {{#Items value.categories}} - - - - + + + + + + + - {{/priority_findings}} - {{^priority_findings}} - - {{/priority_findings}} - -
DomaineContrôlesConformesÉlevéeMoyenneFaibleConformité
{{id}}{{category}}{{severity}}{{status}}{{key}}{{value.expected}}{{value.passed}}{{value.high}}{{value.medium}}{{value.low}}{{value.compliant_percent}} %
Aucun écart prioritaire.
-
-
- -
- Répartition par catégorie -
- - - - {{#Items categories}} - - - - - - - - - - {{/Items}}
CatégorieContrôlesConformesHautMoyenFaibleNon évaluésConformité
{{key}}{{value.expected}}{{value.passed}}{{value.high}}{{value.medium}}{{value.low}}{{value.unknown}}{{value.compliant_percent}} %
-
+ + {{/value.categories}} -
- Tous les contrôles du benchmark ({{summary.expected}}) -
-

Sélectionner un filtre. Les détails de chaque contrôle contiennent la justification et la méthode de vérification.

-
- - - - -
- - {{#privacy.current_values_published}}{{/privacy.current_values_published}} - - {{#Items controls}} - - - - - - - - {{#privacy.current_values_published}}{{/privacy.current_values_published}} - - {{/Items}} - -
IDContrôle et analyseCatégorieGravitéÉtatValeur cibleValeur observée
{{key}} -
{{value.name}}
-
- Voir l'analyse -
-

{{value.explanation}}

- {{#value.reason}}

Justification : {{value.reason}}

{{/value.reason}} - {{#value.method}}

Méthode : {{value.method}}

{{/value.method}} - {{#value.method_argument}}

Argument : {{value.method_argument}}

{{/value.method_argument}} - {{#value.registry_path}}

Registre : {{value.registry_path}}

{{/value.registry_path}} - {{#value.registry_item}}

Valeur registre : {{value.registry_item}}

{{/value.registry_item}} - {{#value.operator}}

Comparaison : {{value.operator}}

{{/value.operator}} -
-
-
{{value.category}}{{value.severity_label}}{{^value.severity_label}}{{value.severity}}{{/value.severity_label}}{{value.status_label}}{{^value.status_label}}{{value.status}}{{/value.status_label}}{{value.recommended}}{{value.current}}
-
-
+ {{#value.controls}} +
+

Détail des contrôles ({{ Count value.controls }})

+

Les contrôles sont classés par identifiant HardeningKitty. Les valeurs observées ne sont affichées que lorsque leur publication est activée dans audit_config.json.

+
+ + {{#value.privacy.current_values_published}}{{/value.privacy.current_values_published}} + + {{#Items value.controls}} + + + + + + + {{#value.privacy.current_values_published}}{{/value.privacy.current_values_published}} + + {{/Items}} + +
IDContrôle et diagnosticCriticitéÉtatValeur cibleValeur observée
{{value.id}} +
{{value.name}}
+
{{value.category}} • {{value.method}}
+ {{#value.explanation}}
{{value.explanation}}
{{/value.explanation}} + {{#value.registry_path}}
Registre : {{value.registry_path}} {{value.registry_item}}
{{/value.registry_path}} + {{#value.method_argument}}
Paramètre : {{value.method_argument}}
{{/value.method_argument}} +
{{value.severity_label}}{{value.status_label}}{{value.recommended}}{{value.current}}
-
- {{/summary}} + + {{/value.controls}} - + {{#value.summary}} + {{^value.controls}} +

Aucun contrôle HardeningKitty disponible dans le contexte de rendu.

+ {{/value.controls}} + {{/value.summary}} + +
diff --git a/package-manifest.json b/package-manifest.json index 81364ac..95d2f21 100644 --- a/package-manifest.json +++ b/package-manifest.json @@ -2,8 +2,9 @@ "files": { "WAPT/control": "4c3eef3800f9994b90cefb06ec35e019ba0c8fb8ffda8a216c7b6ca9eba457d4", "audit_config.json": "9fc18f556fd491e8e00e3a8b1efd864ad8036682e293a02c1f81f3411304a69b", + "console-templates/host_audit_hardeningkitty_hardeningkitty.html": "50d39d6414ee620d05b71539754b8ff7ecb4fab06fb5c3467798478107679df1", "runner.ps1": "5e9787f5cc19d0fc4c0ede01a63188ed71de6ef49df596fbce7b2603093231cd", - "setup.py": "0f4be0d4f2c42ace380dd6305a210c5a7d595508dbc566f652d86f3c7d874d43", + "setup.py": "d0b4ee0c444d1d2c0c698cc87047b4764d58a623ec647dfcf4c6d186f55b4d26", "vendor/HardeningKitty.psd1": "94378764787cd0c7125c5c3ce928803b46ee7d7d3fe21c2b27ddeb23c11377a7", "vendor/HardeningKitty.psm1": "b799ec1b17de5ce63f7a8ea34af3beba1d24fcaddf7e3774f797096f5c43e2a0", "vendor/LICENSE": "a879ba6ccdcd2ea55da97b999edd2c401c8589ca946f238236bf93aa40e74d56", diff --git a/setup.py b/setup.py index 4f75f18..6ddd02e 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,8 @@ REQUIRED_VENDOR_FILES = ( # A staged/installed directory stores the vendor files at its root (NOT /vendor). RUNTIME_FILES = ('runner.ps1', 'audit_config.json') +CONSOLE_TEMPLATE = 'console-templates/host_audit_hardeningkitty_hardeningkitty.html' +_IS_WINDOWS = os.name == 'nt' # Old package versions included this file. It is no longer required or trusted. LEGACY_FILES = ('package-manifest.json',) @@ -200,6 +202,7 @@ def install(): # A security operation can change ACLs. Verify readability and all # checksums AFTER the final ACL change, before replacing a working install. _verify_vendor(staging, runtime=True) + _install_console_template(source) mkdirs(REPORT_ROOT) _secure_directory(REPORT_ROOT) if isdir(INSTALL_ROOT): @@ -224,6 +227,29 @@ def install(): (manifest['upstream_commit'], len(manifest['files']))) +def _install_console_template(source): + """Install the WAPT Console view on Windows using WAPT setuphelpers paths.""" + if not _IS_WINDOWS: + return + template_source = makepath(source, *CONSOLE_TEMPLATE.split('/')) + if not isfile(template_source): + raise RuntimeError('WAPT Console audit template is missing from the package.') + # WAPT setuphelpers exposes programfiles32 as the x86 Program Files path. + template_dir = makepath(programfiles32, 'wapt', 'templates') + mkdirs(template_dir) + target = makepath(template_dir, os.path.basename(CONSOLE_TEMPLATE)) + staged = target + '.new-' + uuid.uuid4().hex + try: + # setuphelpers filecopyto stages on the destination volume; os.replace + # swaps the template atomically and leaves an existing file intact if + # copying fails. + filecopyto(template_source, staged) + os.replace(staged, target) + finally: + if isfile(staged): + remove_file(staged) + + def uninstall(): if os.path.lexists(INSTALL_ROOT): if not _owned_install(INSTALL_ROOT): diff --git a/tests/test_package.py b/tests/test_package.py index d522e39..db73564 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -34,6 +34,7 @@ setuphelpers_stub.system32 = lambda: os.path.join(os.environ.get('SystemRoot', ' setuphelpers_stub.run = lambda *args, **kwargs: '' setuphelpers_stub.wget = lambda *args, **kwargs: (_ for _ in ()).throw( AssertionError('unexpected network download in synthetic tests')) +setuphelpers_stub.programfiles32 = '/synthetic/Program Files (x86)' sys.modules.setdefault('setuphelpers', setuphelpers_stub) spec = importlib.util.spec_from_file_location('wapt_hardeningkitty_setup', ROOT / 'setup.py') pkg = importlib.util.module_from_spec(spec) @@ -75,6 +76,9 @@ def make_source(root): (root / 'setup.py').write_text('# synthetic setup\n', encoding='utf-8') (root / 'runner.ps1').write_text('Invoke-HardeningKitty -Mode Audit\n', encoding='utf-8') (root / 'audit_config.json').write_text('{}\n', encoding='utf-8') + template = root / pkg.CONSOLE_TEMPLATE + template.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(ROOT / pkg.CONSOLE_TEMPLATE, template) asset_root = root / 'vendor' assets = {x: ('sample ' + x).encode('utf-8') for x in vendor.REQUIRED} assets['HardeningKitty.psd1'] = b"@{\n ModuleVersion = '0.9.5'\n}\n" @@ -344,14 +348,34 @@ class PackageTests(unittest.TestCase): def test_console_template_matches_schema_and_uses_escaped_tags(self): template = (ROOT / 'console-templates' / 'host_audit_hardeningkitty_hardeningkitty.html').read_text(encoding='utf-8') - self.assertIn('{{#Items categories}}', template) - self.assertIn('{{#Items controls}}', template) - self.assertIn('{{error.code}}', template) - self.assertIn('{{error.description}}', template) - self.assertIn('{{summary.expected}}', template) + # WAPT wraps the audit JSON under `value`; host metadata stays at root. + self.assertIn('{{#value.summary}}', template) + self.assertIn('{{#Items value.categories}}', template) + self.assertIn('{{#Items value.controls}}', template) + self.assertIn('{{#value.error}}', template) + self.assertIn('{{code}}', template) + self.assertIn('{{description}}', template) + self.assertIn('{{checked}}', template) + self.assertIn('{{computer_fqdn}}', template) self.assertNotIn('{{{', template) self.assertNotIn('