Author SHA1 Message Date
PierreCsn eb4ca133d1 feat(template): install HardeningKitty audit view 2026-10-30 09:12:53 +01:00
7 changed files with 243 additions and 232 deletions

No files matched your search

+30 -5
View File
@@ -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
@@ -1,236 +1,163 @@
<!-- Comitari / HardeningKitty - WAPT Console Mustache audit view.
Section=hardeningkitty; key=hardeningkitty.
Keep this file on WAPT CONSOLE machines, not on audited endpoints.
All dynamic values use HTML-escaped Mustache tags. No JavaScript. -->
<!--
Comitari - Audit de durcissement Windows / HardeningKitty
WAPT: section=hardeningkitty, key=hardeningkitty
WAPT audit rendering context: audit JSON is under value; host metadata is at root.
No JavaScript, external stylesheets or external resources.
-->
<style>
.hk-report {
box-sizing: border-box; background: #fff; color: #202b3b;
font-family: "Segoe UI", Arial, sans-serif; font-size: 13px;
line-height: 1.45; padding: 20px; max-width: 1500px; margin: 0 auto;
}
.hk-report *, .hk-report *:before, .hk-report *:after { box-sizing: border-box; }
.hk-report .hk-header { display:flex; align-items:center; justify-content:space-between; gap:14px; padding-bottom:18px; border-bottom:1px solid #e3e8ee; }
.hk-report .hk-brand { font-size:11px; letter-spacing:2px; font-weight:800; color:#4c6b8b; text-transform:uppercase; }
.hk-report h1 { font-size:23px; color:#132a45; margin:3px 0 2px; line-height:1.2; }
.hk-report h2 { font-size:16px; color:#132a45; margin:24px 0 11px; }
.hk-report p { margin:4px 0 8px; }
.hk-report .hk-muted { color:#65758a; font-size:12px; }
.hk-report .hk-overall { display:inline-block; padding:6px 13px; border-radius:5px; font-size:12px; font-weight:800; letter-spacing:.3px; background:#edf2f7; color:#31465d; }
.hk-report .hk-overall-ERROR { background:#fde8e7; color:#a52a23; }
.hk-report .hk-overall-WARNING { background:#fff1db; color:#8d5400; }
.hk-report .hk-overall-OK { background:#dff5e9; color:#096b3f; }
.hk-report .hk-meta { margin:15px 0 18px; display:flex; flex-wrap:wrap; gap:8px 24px; color:#5d7188; font-size:12px; }
.hk-report .hk-meta strong { color:#243c58; }
.hk-report .hk-score-layout { display:flex; gap:15px; align-items:stretch; flex-wrap:wrap; }
.hk-report .hk-score { background:#f4f7fb; border:1px solid #e5ebf1; border-radius:9px; flex:1 1 270px; padding:17px 18px; min-width:220px; }
.hk-report .hk-score-label { font-size:12px; color:#586a7c; margin:0; }
.hk-report .hk-score-number { font-size:38px; line-height:1.15; font-weight:750; color:#17395e; margin:4px 0 11px; }
.hk-report .hk-score-number span { font-size:19px; font-weight:500; color:#728197; }
.hk-report .hk-track { height:9px; background:#d8e3ee; border-radius:20px; overflow:hidden; }
.hk-report .hk-fill { height:100%; background:#278e65; max-width:100%; }
.hk-report .hk-score-foot { display:flex; justify-content:space-between; gap:12px; margin-top:9px; color:#66798b; font-size:11px; }
.hk-report .hk-kpis { flex:3 1 460px; display:grid; grid-template-columns:repeat(3,minmax(110px,1fr)); gap:10px; }
.hk-report .hk-kpi { border:1px solid #e5ebf1; border-radius:9px; background:#fff; padding:12px 14px; }
.hk-report .hk-kpi strong { display:block; font-size:25px; line-height:1.15; color:#203752; }
.hk-report .hk-kpi span { display:block; margin-top:3px; font-size:11px; color:#687b90; }
.hk-report .hk-kpi.hk-high strong { color:#c23b34; }
.hk-report .hk-kpi.hk-medium strong { color:#b77910; }
.hk-report .hk-kpi.hk-low strong { color:#9d8a22; }
.hk-report .hk-kpi.hk-passed strong { color:#178256; }
.hk-report .hk-banner { padding:12px 15px; border:1px solid #f3d9a2; background:#fff9eb; color:#74531f; border-radius:7px; margin:16px 0; }
.hk-report .hk-banner-error { border-color:#f0bebc; background:#fff1f0; color:#882b26; }
.hk-report .hk-section { border:1px solid #e3e9f0; border-radius:8px; overflow:hidden; margin:14px 0; }
.hk-report .hk-section > summary { cursor:pointer; padding:12px 15px; background:#f7f9fc; color:#243e58; font-size:14px; font-weight:700; }
.hk-report .hk-section > summary:hover { background:#eff5fc; }
.hk-report .hk-section-content { padding:4px 13px 14px; }
.hk-report .hk-scroll { overflow-x:auto; max-width:100%; }
.hk-report .hk-table { width:100%; border-collapse:collapse; border-spacing:0; min-width:690px; text-align:left; }
.hk-report .hk-table th { padding:9px 9px; color:#62758b; background:#f5f8fb; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.35px; border-bottom:1px solid #dfe7ef; }
.hk-report .hk-table td { padding:10px 9px; vertical-align:top; border-bottom:1px solid #edf0f4; overflow-wrap:anywhere; }
.hk-report .hk-table tbody tr:hover { background:#f7fafe; }
.hk-report .hk-table tbody tr:last-child td { border-bottom:0; }
.hk-report .hk-table .hk-id { white-space:nowrap; font-family:Consolas,monospace; color:#54708a; }
.hk-report .hk-table .hk-control-title { font-weight:600; color:#223b54; }
.hk-report .hk-badge { display:inline-block; white-space:nowrap; border-radius:5px; padding:2px 7px; font-size:11px; font-weight:700; background:#edf2f7; color:#54677c; }
.hk-report .hk-sev-high { background:#fce6e6; color:#a52b28; }
.hk-report .hk-sev-medium { background:#fff0d4; color:#9d5e03; }
.hk-report .hk-sev-low { background:#fff9db; color:#947d17; }
.hk-report .hk-sev-unknown { background:#ebeff4; color:#58677c; }
.hk-report .hk-state-passed { color:#118159; font-weight:650; }
.hk-report .hk-state-failed { color:#b04335; font-weight:650; }
.hk-report .hk-state-skipped, .hk-report .hk-state-unknown, .hk-report .hk-state-ignored { color:#8c5810; font-weight:650; }
.hk-report .hk-value { font-family:Consolas,"Courier New",monospace; font-size:11px; color:#344c66; word-break:break-word; }
.hk-report .hk-small { color:#64748b; font-size:11px; }
.hk-report .hk-detail { margin:5px 0 0; }
.hk-report .hk-detail summary { cursor:pointer; display:inline; color:#245b94; font-weight:600; }
.hk-report .hk-detail-content { margin:8px 0 4px; padding:8px 10px; background:#f6f9fc; border-left:3px solid #b7ccde; color:#465c74; font-size:11px; }
.hk-report .hk-detail-content p { margin:4px 0; }
.hk-report .hk-filters { padding:12px 0 8px; }
.hk-report .hk-filters input[type=radio] { position:absolute; opacity:0; height:1px; width:1px; }
.hk-report .hk-filters label { display:inline-block; margin:0 5px 8px 0; padding:5px 12px; border-radius:6px; border:1px solid #dbe4ed; cursor:pointer; color:#556e85; font-weight:600; font-size:12px; }
.hk-report .hk-filters label:hover { background:#f1f5f9; }
.hk-report #hk-filter-all:checked + label,
.hk-report #hk-filter-issues:checked + label,
.hk-report #hk-filter-high:checked + label,
.hk-report #hk-filter-passed:checked + label { background:#275d93; color:white; border-color:#275d93; }
.hk-report #hk-filter-issues:checked ~ .hk-scroll .hk-row-passed,
.hk-report #hk-filter-issues:checked ~ .hk-scroll .hk-row-excluded,
.hk-report #hk-filter-issues:checked ~ .hk-scroll .hk-row-not_applicable { display:none; }
.hk-report #hk-filter-high:checked ~ .hk-scroll .hk-row:not(.hk-row-failed),
.hk-report #hk-filter-high:checked ~ .hk-scroll .hk-row:not(.hk-risk-high) { display:none; }
.hk-report #hk-filter-passed:checked ~ .hk-scroll .hk-row:not(.hk-row-passed) { display:none; }
.hk-report .hk-note { border-left:3px solid #b9c9db; padding:8px 10px; margin:14px 0; font-size:12px; color:#5b6f84; background:#f6f9fc; }
.hk-report .hk-footer { padding-top:11px; margin-top:22px; border-top:1px solid #e6eaf0; color:#73859a; font-size:11px; }
.hk-report .hk-no-data { margin:18px 0; padding:15px; border:1px solid #e8dfba; border-radius:6px; background:#fffaf0; }
@media (max-width:750px) {
.hk-report { padding:10px; }
.hk-report .hk-kpis { grid-template-columns:repeat(2,minmax(110px,1fr)); }
.hk-report h1 { font-size:18px; }
}
.comi-hk { color:#23364f; background:#fff; font-family:Arial,Helvetica,sans-serif; font-size:13px; line-height:1.48; max-width:100%; padding:22px 22px 26px; box-sizing:border-box; }
.comi-hk * { box-sizing:border-box; }
.comi-hk h1,.comi-hk h2,.comi-hk h3,.comi-hk p { margin:0; }
.comi-hk .eyebrow { color:#3d6490; font-size:10px; font-weight:700; letter-spacing:0.065em; text-transform:uppercase; }
.comi-hk h1 { color:#173354; font-size:23px; line-height:1.22; margin:3px 0 4px; }
.comi-hk .sub { color:#5b708b; font-size:12px; }
.comi-hk h2 { color:#193656; font-size:16px; margin:0 0 12px; }
.comi-hk header { padding-bottom:16px; border-bottom:1px solid #e4ebf3; }
.comi-hk .meta { color:#5c7088; padding:11px 0 15px; font-size:11px; }
.comi-hk .meta span { margin-right:12px; display:inline-block; padding-bottom:3px; }
.comi-hk .meta strong { color:#2d4667; }
.comi-hk .section { margin-top:22px; }
.comi-hk .cards { display:flex; flex-wrap:wrap; gap:10px; }
.comi-hk .card { flex:1 1 124px; min-width:108px; border:1px solid #e2eaf3; background:#f8fbfe; border-radius:8px; padding:12px 13px; }
.comi-hk .card .label { font-size:11px; color:#637a94; line-height:1.3; }
.comi-hk .card .num { font-size:26px; font-weight:700; line-height:1.2; color:#193656; margin-top:5px; }
.comi-hk .card.bad .num { color:#b63838; }
.comi-hk .card.good .num { color:#287c60; }
.comi-hk .card.medium .num { color:#b86a27; }
.comi-hk .progress { height:10px; background:#e9eff6; border-radius:20px; overflow:hidden; margin-top:12px; }
.comi-hk .progress-fill { background:#2d8b75; height:10px; border-radius:20px; }
.comi-hk .progress-legend { display:flex; justify-content:space-between; gap:12px; color:#60738e; font-size:11px; margin-top:5px; }
.comi-hk .callout { background:#f2f6fc; border-left:3px solid #3c719d; border-radius:4px; padding:10px 12px; color:#435d78; font-size:12px; margin-top:12px; }
.comi-hk .callout-warn { background:#fff8ed; border-left-color:#d18a30; color:#684e2c; }
.comi-hk .table-wrap { max-width:100%; overflow-x:auto; border:1px solid #e3eaf2; border-radius:7px; }
.comi-hk table { border-collapse:collapse; width:100%; font-size:12px; }
.comi-hk thead th { background:#f2f6fb; border-bottom:1px solid #dfe7f1; padding:9px 10px; text-align:left; font-size:11px; font-weight:700; color:#3d5473; }
.comi-hk tbody td { border-bottom:1px solid #edf1f6; padding:9px 10px; vertical-align:top; }
.comi-hk tbody tr:last-child td { border-bottom:0; }
.comi-hk tbody tr:nth-child(even) { background:#fbfcfe; }
.comi-hk .n { text-align:right; white-space:nowrap; }
.comi-hk .name { font-weight:600; color:#203a5a; }
.comi-hk .muted { color:#75869a; font-size:11px; margin-top:4px; overflow-wrap:anywhere; }
.comi-hk .small { color:#5b708a; font-size:11px; margin-top:4px; overflow-wrap:anywhere; }
.comi-hk .id { font-family:Consolas,"Courier New",monospace; color:#60718b; font-size:11px; white-space:nowrap; }
.comi-hk .pill { display:inline-block; padding:3px 7px; font-size:11px; font-weight:700; border-radius:99px; background:#edf2f7; color:#49617d; white-space:nowrap; }
.comi-hk .pill.severity-high { background:#fde8e8; color:#aa2424; }
.comi-hk .pill.severity-medium { background:#fff1df; color:#985c20; }
.comi-hk .pill.severity-low { background:#eaf2ff; color:#3b6ca9; }
.comi-hk .pill.status-failed { background:#fee9e8; color:#b12f2a; }
.comi-hk .pill.status-passed { background:#e0f5ec; color:#207956; }
.comi-hk .pill.status-ignored,.comi-hk .pill.status-skipped,.comi-hk .pill.status-not_applicable { background:#eff2f5; color:#687988; }
.comi-hk .note { color:#657c93; font-size:11px; margin:10px 0; }
.comi-hk footer { color:#647b96; font-size:11px; margin-top:24px; border-top:1px solid #e5ebf2; padding-top:12px; }
.comi-hk .mono { font-family:Consolas,"Courier New",monospace; overflow-wrap:anywhere; }
.comi-hk .notfound { border:1px solid #e9d09e; background:#fff9ef; border-radius:6px; padding:16px; margin-top:20px; color:#805822; }
</style>
<div class="hk-report">
<div class="hk-header">
<div>
<div class="hk-brand">COMITARI / HARDENINGKITTY</div>
<h1>Audit de durcissement Windows</h1>
<div class="hk-muted">Analyse des &eacute;carts par rapport au r&eacute;f&eacute;rentiel de s&eacute;curit&eacute;</div>
<div class="comi-hk">
<header>
<p class="eyebrow">COMITARI / HARDENINGKITTY</p>
<h1>Audit de durcissement Windows</h1>
<p class="sub">Analyse des écarts par rapport au référentiel de sécurité</p>
</header>
{{#value.summary}}
<div class="meta">
<span><strong>Poste :</strong> {{computer_fqdn}}</span>
<span><strong>Système :</strong> {{value.host.os_caption}} {{value.host.display_version}} (build {{value.host.build}})</span>
<span><strong>Exécuté le :</strong> {{value.scan.date_utc}}</span>
<span><strong>Source :</strong> {{value.scan.source}}</span>
<span><strong>Statut :</strong> {{value.status}}</span>
</div>
<section class="section">
<h2>Vue d'ensemble</h2>
<div class="cards">
<div class="card"><div class="label">Contrôles vérifiés</div><div class="num">{{checked}}</div></div>
<div class="card good"><div class="label">Conformes</div><div class="num">{{passed}}</div></div>
<div class="card bad"><div class="label">Non conformes</div><div class="num">{{failed}}</div></div>
<div class="card"><div class="label">Conformité</div><div class="num">{{compliant_percent}} %</div></div>
<div class="card bad"><div class="label">Criticité élevée</div><div class="num">{{high}}</div></div>
<div class="card medium"><div class="label">Criticité moyenne</div><div class="num">{{medium}}</div></div>
<div class="card"><div class="label">Criticité faible</div><div class="num">{{low}}</div></div>
</div>
<span class="hk-overall hk-overall-{{status}}">{{status}}</span>
</div>
<div class="progress"><div class="progress-fill" style="width:{{compliant_percent}}%;"></div></div>
<div class="progress-legend"><span>Score de conformité observé</span><span>{{passed}} / {{checked}} contrôles conformes</span></div>
<div class="callout callout-warn">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.</div>
</section>
{{/value.summary}}
{{#error}}
<div class="hk-banner hk-banner-error">
<strong>Audit incomplet ou indisponible.</strong>
<p>Code : {{error.code}} — type : {{error.type}}</p>
<p>{{error.description}}</p>
<p>V&eacute;rifier les journaux de l'agent WAPT sur le poste concern&eacute;.</p>
</div>
{{/error}}
{{#value.error}}
<div class="notfound"><strong>Audit en erreur : {{code}}</strong><br>{{description}}<span class="small">Type technique : {{type}}</span></div>
{{/value.error}}
{{#summary}}
<div class="hk-meta">
<span><strong>Machine :</strong> {{host.os_caption}} {{host.display_version}} (build {{host.build}})</span>
<span><strong>R&eacute;f&eacute;rentiel :</strong> {{benchmark.name}}</span>
<span><strong>Source :</strong> {{scan.source}}</span>
<span><strong>Audit UTC :</strong> {{scan.date_utc}}</span>
</div>
<div class="hk-score-layout">
<div class="hk-score">
<p class="hk-score-label">Taux de conformit&eacute;</p>
<div class="hk-score-number">{{compliant_percent}} <span>%</span></div>
<div class="hk-track"><div class="hk-fill" style="width:{{compliant_percent}}%;"></div></div>
<div class="hk-score-foot"><span>{{passed}} conformes</span><span>{{expected}} attendus</span></div>
</div>
<div class="hk-kpis">
<div class="hk-kpi hk-high"><strong>{{high}}</strong><span>&Eacute;carts &eacute;lev&eacute;s</span></div>
<div class="hk-kpi hk-medium"><strong>{{medium}}</strong><span>&Eacute;carts moyens</span></div>
<div class="hk-kpi hk-low"><strong>{{low}}</strong><span>&Eacute;carts faibles</span></div>
<div class="hk-kpi hk-passed"><strong>{{passed}}</strong><span>Conformes</span></div>
<div class="hk-kpi"><strong>{{unknown}}</strong><span>Inconnus</span></div>
<div class="hk-kpi"><strong>{{skipped}}</strong><span>Non ex&eacute;cut&eacute;s</span></div>
</div>
</div>
{{/summary}}
{{^value.summary}}
{{^value.error}}
<div class="notfound"><strong>Données d'audit absentes.</strong> La valeur reçue ne contient ni synthèse ni erreur structurée.</div>
{{/value.error}}
{{/value.summary}}
{{#summary}}
{{#privacy.current_values_published}}
<p class="hk-note">Valeurs constat&eacute;es : publication activ&eacute;e. V&eacute;rifier les droits d'acc&egrave;s aux donn&eacute;es d'audit.</p>
{{/privacy.current_values_published}}
{{^privacy.current_values_published}}
<p class="hk-note">Les valeurs effectivement lues sur le poste ne sont pas publi&eacute;es par d&eacute;faut. Le CSV d&eacute;taill&eacute; reste disponible localement sur le poste audit&eacute;.</p>
{{/privacy.current_values_published}}
<h2>&Eacute;carts &agrave; examiner en priorit&eacute;</h2>
<p class="hk-muted">{{priority_total}} r&eacute;sultats non conformes ou non &eacute;valu&eacute;s. Aper&ccedil;u limit&eacute; &agrave; 25 contr&ocirc;les, class&eacute;s par gravit&eacute; puis cat&eacute;gorie (pas par risque exploitabilit&eacute;).</p>
<div class="hk-section">
<div class="hk-scroll">
<table class="hk-table">
<thead><tr><th>ID</th><th>Cat&eacute;gorie</th><th>Gravit&eacute;</th><th>&Eacute;tat</th></tr></thead>
{{#value.categories}}
<section class="section">
<h2>Répartition par domaine</h2>
<div class="table-wrap">
<table>
<thead><tr><th>Domaine</th><th class="n">Contrôles</th><th class="n">Conformes</th><th class="n">Élevée</th><th class="n">Moyenne</th><th class="n">Faible</th><th class="n">Conformité</th></tr></thead>
<tbody>
{{#priority_findings}}
{{#Items value.categories}}
<tr>
<td class="hk-id">{{id}}</td>
<td>{{category}}</td>
<td><span class="hk-badge hk-sev-{{severity}}">{{severity}}</span></td>
<td><span class="hk-state-{{status}}">{{status}}</span></td>
<td class="name">{{key}}</td>
<td class="n">{{value.expected}}</td>
<td class="n">{{value.passed}}</td>
<td class="n">{{value.high}}</td>
<td class="n">{{value.medium}}</td>
<td class="n">{{value.low}}</td>
<td class="n">{{value.compliant_percent}} %</td>
</tr>
{{/priority_findings}}
{{^priority_findings}}
<tr><td colspan="4" class="hk-muted">Aucun &eacute;cart prioritaire.</td></tr>
{{/priority_findings}}
</tbody>
</table>
</div>
</div>
<details class="hk-section" open>
<summary>R&eacute;partition par cat&eacute;gorie</summary>
<div class="hk-section-content hk-scroll">
<table class="hk-table">
<thead><tr><th>Cat&eacute;gorie</th><th>Contr&ocirc;les</th><th>Conformes</th><th>Haut</th><th>Moyen</th><th>Faible</th><th>Non &eacute;valu&eacute;s</th><th>Conformit&eacute;</th></tr></thead>
<tbody>
{{#Items categories}}
<tr>
<td class="hk-control-title">{{key}}</td>
<td>{{value.expected}}</td>
<td>{{value.passed}}</td>
<td>{{value.high}}</td>
<td>{{value.medium}}</td>
<td>{{value.low}}</td>
<td>{{value.unknown}}</td>
<td><strong>{{value.compliant_percent}} %</strong></td>
</tr>
{{/Items}}
</tbody>
</table>
</div>
</details>
</section>
{{/value.categories}}
<details class="hk-section">
<summary>Tous les contr&ocirc;les du benchmark ({{summary.expected}})</summary>
<div class="hk-section-content">
<p class="hk-muted">S&eacute;lectionner un filtre. Les d&eacute;tails de chaque contr&ocirc;le contiennent la justification et la m&eacute;thode de v&eacute;rification.</p>
<div class="hk-filters">
<input type="radio" id="hk-filter-all" name="hk-filter" checked><label for="hk-filter-all">Tous</label>
<input type="radio" id="hk-filter-issues" name="hk-filter"><label for="hk-filter-issues">&Eacute;carts / Inconnus</label>
<input type="radio" id="hk-filter-high" name="hk-filter"><label for="hk-filter-high">&Eacute;lev&eacute;s uniquement</label>
<input type="radio" id="hk-filter-passed" name="hk-filter"><label for="hk-filter-passed">Conformes</label>
<div class="hk-scroll">
<table class="hk-table">
<thead><tr><th>ID</th><th>Contr&ocirc;le et analyse</th><th>Cat&eacute;gorie</th><th>Gravit&eacute;</th><th>&Eacute;tat</th><th>Valeur cible</th>{{#privacy.current_values_published}}<th>Valeur observ&eacute;e</th>{{/privacy.current_values_published}}</tr></thead>
<tbody>
{{#Items controls}}
<tr class="hk-row hk-row-{{value.status}} hk-risk-{{value.severity}}">
<td class="hk-id">{{key}}</td>
<td>
<div class="hk-control-title">{{value.name}}</div>
<details class="hk-detail">
<summary>Voir l'analyse</summary>
<div class="hk-detail-content">
<p>{{value.explanation}}</p>
{{#value.reason}}<p><strong>Justification :</strong> {{value.reason}}</p>{{/value.reason}}
{{#value.method}}<p><strong>M&eacute;thode :</strong> {{value.method}}</p>{{/value.method}}
{{#value.method_argument}}<p><strong>Argument :</strong> <span class="hk-value">{{value.method_argument}}</span></p>{{/value.method_argument}}
{{#value.registry_path}}<p><strong>Registre :</strong> <span class="hk-value">{{value.registry_path}}</span></p>{{/value.registry_path}}
{{#value.registry_item}}<p><strong>Valeur registre :</strong> <span class="hk-value">{{value.registry_item}}</span></p>{{/value.registry_item}}
{{#value.operator}}<p><strong>Comparaison :</strong> {{value.operator}}</p>{{/value.operator}}
</div>
</details>
</td>
<td>{{value.category}}</td>
<td><span class="hk-badge hk-sev-{{value.severity}}">{{value.severity_label}}{{^value.severity_label}}{{value.severity}}{{/value.severity_label}}</span></td>
<td><span class="hk-state-{{value.status}}">{{value.status_label}}{{^value.status_label}}{{value.status}}{{/value.status_label}}</span></td>
<td class="hk-value">{{value.recommended}}</td>
{{#privacy.current_values_published}}<td class="hk-value">{{value.current}}</td>{{/privacy.current_values_published}}
</tr>
{{/Items}}
</tbody>
</table>
</div>
</div>
{{#value.controls}}
<section class="section">
<h2>Détail des contrôles ({{ Count value.controls }})</h2>
<p class="note">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.</p>
<div class="table-wrap">
<table>
<thead><tr><th>ID</th><th style="min-width:260px">Contrôle et diagnostic</th><th>Criticité</th><th>État</th><th>Valeur cible</th>{{#value.privacy.current_values_published}}<th>Valeur observée</th>{{/value.privacy.current_values_published}}</tr></thead>
<tbody>
{{#Items value.controls}}
<tr>
<td class="id">{{value.id}}</td>
<td>
<div class="name">{{value.name}}</div>
<div class="muted">{{value.category}} &bull; {{value.method}}</div>
{{#value.explanation}}<div class="small">{{value.explanation}}</div>{{/value.explanation}}
{{#value.registry_path}}<div class="muted">Registre : <span class="mono">{{value.registry_path}} {{value.registry_item}}</span></div>{{/value.registry_path}}
{{#value.method_argument}}<div class="muted">Paramètre : <span class="mono">{{value.method_argument}}</span></div>{{/value.method_argument}}
</td>
<td><span class="pill severity-{{value.severity}}">{{value.severity_label}}</span></td>
<td><span class="pill status-{{value.status}}">{{value.status_label}}</span></td>
<td class="mono">{{value.recommended}}</td>
{{#value.privacy.current_values_published}}<td class="mono">{{value.current}}</td>{{/value.privacy.current_values_published}}
</tr>
{{/Items}}
</tbody>
</table>
</div>
</details>
{{/summary}}
</section>
{{/value.controls}}
<div class="hk-footer">HardeningKitty / Comitari &middot; Synth&egrave;se informative uniquement, aucune correction automatique &middot; R&eacute;f&eacute;rentiel : {{benchmark.name}}</div>
{{#value.summary}}
{{^value.controls}}
<p class="notfound">Aucun contrôle HardeningKitty disponible dans le contexte de rendu.</p>
{{/value.controls}}
{{/value.summary}}
<footer>
HardeningKitty / Comitari &middot; Synthèse informative uniquement, aucune correction automatique
&middot; Référentiel : <strong>{{value.benchmark.name}}</strong>
&middot; Profil : <strong>{{value.scan.profile}}</strong>
</footer>
</div>
+2 -1
View File
@@ -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",
+26
View File
@@ -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):
+32 -6
View File
@@ -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('<script', template.lower())
def test_windows_install_copies_console_template_using_wapt_path(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
source = make_source(root / 'package')
prior = (pkg._IS_WINDOWS, pkg.programfiles32)
try:
pkg._IS_WINDOWS = True
pkg.programfiles32 = str(root / 'Program Files (x86)')
pkg._install_console_template(str(source))
installed = (root / 'Program Files (x86)' / 'wapt' / 'templates' /
'host_audit_hardeningkitty_hardeningkitty.html')
self.assertEqual(installed.read_bytes(),
(source / pkg.CONSOLE_TEMPLATE).read_bytes())
finally:
pkg._IS_WINDOWS, pkg.programfiles32 = prior
def test_structured_error_does_not_publish_exception_details(self):
events = []
class FakeWapt:
@@ -540,7 +564,9 @@ class PackageTests(unittest.TestCase):
verify.verify_source(source)
archive = Path(tmp, 'synthetic.wapt')
with zipfile.ZipFile(archive, 'w') as zf:
for name in ('setup.py', 'runner.ps1', 'audit_config.json', 'package-manifest.json'):
for name in ('setup.py', 'runner.ps1', 'audit_config.json',
'console-templates/host_audit_hardeningkitty_hardeningkitty.html',
'package-manifest.json'):
zf.write(source / name, name)
zf.write(source / 'WAPT' / 'control', 'WAPT/control')
for path in (source / 'vendor').rglob('*'):
+3
View File
@@ -114,6 +114,9 @@ class AuditWaptTest(unittest.TestCase):
'publish_to_wapt':True,'retain_audit_runs':3,'timeout_seconds':120,
'max_findings_in_log':10,
}))
template = self.source/'console-templates'/'host_audit_hardeningkitty_hardeningkitty.html'
template.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(WORK/'console-templates'/'host_audit_hardeningkitty_hardeningkitty.html', template)
self.source.joinpath('package-manifest.json').write_text('{"stale":true}')
(self.source/'setup.py').write_text('# synthetic setup\n')
(self.source/'tools').mkdir()
+4 -1
View File
@@ -19,9 +19,12 @@ except ImportError:
UPSTREAM_COMMIT = _vendor_module.UPSTREAM_COMMIT
REQUIRED = _vendor_module.REQUIRED
PACKAGE_FILES = ('setup.py', 'WAPT/control', 'runner.ps1', 'audit_config.json', 'vendor/vendor-manifest.json')
CONSOLE_TEMPLATE = 'console-templates/host_audit_hardeningkitty_hardeningkitty.html'
PACKAGE_FILES = ('setup.py', 'WAPT/control', 'runner.ps1', 'audit_config.json',
CONSOLE_TEMPLATE, 'vendor/vendor-manifest.json')
REQUIRED_WAPT = (
'setup.py', 'WAPT/control', 'runner.ps1', 'audit_config.json', 'package-manifest.json',
CONSOLE_TEMPLATE,
'vendor/HardeningKitty.psm1',
'vendor/HardeningKitty.psd1', 'vendor/vendor-manifest.json',
)