diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md index 387fc1e..a7652f9 100644 --- a/docs/IMPLEMENTATION_PLAN.md +++ b/docs/IMPLEMENTATION_PLAN.md @@ -3979,6 +3979,12 @@ Festgelegtes Betriebsrisiko vom 2026-09-06: Verzeichnisabhaengige Compose-Projek ## 12. Änderungsprotokoll 2026-10-05 - Phase 7 / Management Report (Version 2.16.6): Zahlen an Client Info angeglichen (Report-Engine-Arbeit mit Core-Freigabe Evaluation, Core Tests und Governance; per Test und am Dummy-Client verifiziert). Nutzerentscheidung: Datengrundlage der Reports ist Raw Data der Work-Eintraege, die in Client Info angezeigten Zahlen sind verbindlich (neuer Abschnitt `Report numbers` in AGENTS.md). Behoben: Die Tabelle `Technische Domain-Auswertung` zaehlte Ergebniszeilen (Dummy: 998 bewertet/579 abweichend) statt Work-Eintraege; sie zaehlt jetzt mit demselben Klassifikator und denselben Ergebniszustaenden wie Client Info auf dem vollstaendigen Raw-Data-Blatt. Der Report-Datensatz liest dafuer dieselben aktuellen GPO-Bewertungen wie Client Info (`listCurrentPolicyEvaluationsForMetrics`). Dummy: 759/477 und je Domain 112/54/19/245/7/40 Abweichungen, identisch mit Client Info. Die vier Themen stehen in Summary, Uebersicht, Diagramm und Themenblaettern einheitlich nach Anzahl absteigend. Heatmap an der Basis behoben (Context Engine 1.11.1, Engine-Arbeit): GPO-Einstellungen mit katalogisierter Wertsemantik (Dummy: MinimumPasswordLength, PasswordComplexity, LockoutBadCount) waren ihrem Problemfeld zugeordnet, ihr ASSESSOR-Urteil `Fail` wurde aber nicht gelesen; die Heatmap zaehlte deshalb 474 statt 477. Die Engine fuehrt diese Urteile jetzt unveraendert als `declaredDeviations`; Heatmap, Client Info und Report zeigen 477 (Kennwortrichtlinie 22), `Ohne Problemkategorie` entfaellt. Synthetischer Regressionstest in `tests/assessment-context/context-engine.test.ts`. Zweite Pruefung am Dummy-Client (gleicher Release-Stand): Zuordnung in ASSESSOR vollstaendig (477 Abweichungen je genau ein Problemfeld und ein Thema, Domains gleich Client Info). Im Report korrigiert: nicht ausgefuehrt/Fehler/nicht anwendbar/unbekannt nach Work-Zustaenden (171/2/3/19 statt 170/2/3/0), Erhebungen nur Scan- und GPO-Erhebungslaeufe mit Timeslot-Datum der Quelldateien (5 im Timeslot 22.7.2026 statt 38 Verarbeitungsschritte), Risikoregister wie Client Info (1 erfasst, 0 aktuell gueltig statt 'dokumentiert'). Offen fuer Core: CIS-CAT `Manual` ist normalisiert `informational`, in Work aber `not run`. Versionsentscheidung: Patch 2.16.5 -> 2.16.6 (rueckwaertskompatible Korrekturen an Report- und Heatmap-Zahlen); Versionstests und beide Produktions-Builds bestanden. +2026-10-07 - Ingest / PingCastle-HTML-Gesamtstruktur aus Configuration-DN (Nutzerentscheidung Option 2; Core-Freigabe Ingest, Normalization, Core Tests; Produktversion 2.19.1): Der PingCastle-HTML-Bericht enthaelt kein Feld fuer den Gesamtstrukturnamen, nur die Gesamtstruktur-Funktionsebene. Vollstaendige DNs `CN=Configuration,DC=...` stehen nur in PingCastles eingebetteten Graphdaten (`'; + const withGraphs = (...blocks: string[]) => pingCastleHtml.replace('', blocks.join('') + ''); + const forests = async (html: string) => { + const dataset = await importPingCastle(html, 'ad_hc_synthetic.example.html', 'text/html'); + return { + observations: (dataset.extraction?.environmentObservations ?? []).filter(item => item.property === 'forest.name'), + diagnostics: (dataset.extraction?.diagnostics ?? []).filter(item => item.code === 'pingcastle-forest-root-conflict') + }; + }; + + const agreeing = await forests(withGraphs( + graph('Data_Synthetic_A', ['CN=Services,CN=Configuration,DC=Forest,DC=Synthetic,DC=Example', 'CN=Domain Admins,CN=Users,DC=child,DC=forest,DC=synthetic,DC=example']), + graph('Data_Synthetic_B', ['CN=Configuration,DC=forest,DC=synthetic,DC=example']) + )); + expect(agreeing.diagnostics).toEqual([]); + expect(agreeing.observations).toEqual([expect.objectContaining({ + label: 'Forest', originalValue: 'forest.synthetic.example', state: 'observed', + source: expect.objectContaining({ objectPath: 'html/embedded-data/Data_Synthetic_A/nodes/0/name' }), + attributes: expect.objectContaining({ derivation: 'configuration-partition-dn', occurrenceCount: 2 }) + })]); + + const conflicting = await forests(withGraphs(graph('Data_Synthetic_A', [ + 'CN=Configuration,DC=one,DC=example', 'CN=Services,CN=Configuration,DC=two,DC=example' + ]))); + expect(conflicting.observations).toEqual([]); + expect(conflicting.diagnostics).toEqual([expect.objectContaining({ severity: 'warning', details: { rootCount: 2 } })]); + + // No full DN, a truncated visible DN, a non-JSON script, unparsable JSON or a domain DN never yield a forest. + const absent = await forests(withGraphs( + '
CN=Templates,CN=Configuration,DC=visible,DC=example
', + '', + '', + graph('Data_Synthetic_C', ['CN=Configuration', 'CN=Domain Admins,CN=Users,DC=domain,DC=example']) + )); + expect(absent).toEqual({ observations: [], diagnostics: [] }); + }); + + it('maps the top-level XML domain facts to the same environment properties the HTML import uses', async () => { + const xml = pingCastleXml.replace('