Assessor_2.0/docs/RESULT_CONTRACT.md

33 KiB
Raw Permalink Blame History

RESULT_CONTRACT

schema_version : 1.0.0
status         : verbindlicher Datenvertrag
zielort        : docs/RESULT_CONTRACT.md
gilt_fuer      : Purple Knight, PingCastle, Maester, CIS-CAT Pro, GPO-/Baseline-Analyse
konsumenten    : ASSESSOR UI, lokales LLM, Exporte, weitere Tools

Dieser Vertrag definiert die oberste Klassifikationsschicht und die Graph-Grundlage des Canonical Result Store. Er ist ein versionierter Integrationsvertrag, kein reiner UI-Index. Alle Konsumenten greifen auf dieselbe Semantik zu und müssen nicht wissen, welche native Spalte ein Tool verwendet hat.

Der Vertrag ersetzt IOE als Organisationsprinzip durch result_type. IOE bleibt vollständig erhalten, wird aber zu einer abgeleiteten, indizierten Facette. Alle Zusagen des PingCastle-Arbeitspakets (Dokument 2) für Purple Knight, PingCastle und Maester bleiben unverändert gültig.

Realisierungshinweis: Dieser Vertrag wurde additiv auf das bereits bestehende ASSESSOR-Datenmodell abgebildet (Pfad A, Integrationsentscheidung 2026-07-23). Siehe Abschnitt „Abweichungen der ASSESSOR-Realisierung" am Ende dieses Dokuments für die Stellen, an denen die konkrete Umsetzung von diesem Vertrag abweicht, und warum. Der fachlich verbindliche Umsetzungsstand ist docs/CANONICAL_MODEL.md; dieses Dokument bleibt der unveränderte externe Vertragstext.

Grundprinzip: zwei orthogonale Achsen

Der bisherige Konflikt entsteht, weil IOE zwei verschiedene Fragen vermischt hat. Der Vertrag trennt sie strikt.

result_type beschreibt die Art der Aussage, also die Quellsemantik. Geschlossenes, versioniertes Enum. Stabil.

result_state beschreibt den Ausgang der Aussage, normalisiert und typübergreifend. Dies ist die eine Achse, auf der toolübergreifend sicher gefiltert wird.

Jede weitere Dimension (Severity, IOE, Score) hängt an diesen beiden Achsen, ersetzt sie aber nicht.

result_type (geschlossenes Enum)

exposure_finding   Exposure-Scanner. Aussage: Exposure vorhanden oder nicht.
                   Quellen: Purple Knight, PingCastle RiskRules, Maester Security Tests.
                   Nur dieser Typ kann per Default zu IOE werden.

compliance_check   Benchmark-Pruefung gegen eine Empfehlung. Aussage: konform oder nicht.
                   Quelle: CIS-CAT (XCCDF).

policy_deviation   Setting-Vergleich gegen eine Baseline. Aussage: Sollwert vs. Istwert plus Verdikt.
                   Quelle: GPO-Backup vs. MS Security Baseline.

informational      Nicht-assertive Kontext- oder Inventardaten ohne Pass/Fail-Semantik.

unknown            Rueckfallwert, wenn die Quellsemantik nicht klassifizierbar ist.

Bewusst kein eigener Typ: technical_error ist ein Zustand (result_state = error), keine Art von Aussage. Eine CIS-Prüfung, die nicht ausgeführt werden konnte, bleibt result_type = compliance_check mit result_state = error. Das hält result_type stabil.

result_state (normalisiert, typübergreifend)

fail             negativer Ausgang (Exposure erkannt / Check nicht bestanden / Setting weicht ab)
pass             positiver Ausgang
warning
error            Auswertung oder Ausfuehrung fehlgeschlagen
not_applicable
not_evaluated
informational
unknown

Dies ist die einzige Achse, auf der ein typübergreifender „alle Fails"-Filter zuverlässig arbeitet. Nativwerte bleiben parallel im typspezifischen Block erhalten und werden nie durch die Normalisierung überschrieben.

is_ioe (abgeleitete Facette)

is_ioe wird nicht aus der Quelle übernommen, sondern per expliziter, versionierter Regel abgeleitet und als materialisierte, indizierte boolesche Spalte gespeichert. Nicht in JSONB.

Ableitungsregeln:

exposure_finding + exposure_state = detected   -> is_ioe = true   (Default)
compliance_check                               -> is_ioe = false  (Default)
policy_deviation                               -> is_ioe = false  (Default)

Nur eine explizite, versionierte Elevationsregel darf einen einzelnen Compliance-Check oder eine Policy-Abweichung auf IOE heben. Niemals per Default.

Jede Ableitung trägt ioe_rule_id, ioe_rule_version, ioe_derivation_method.

Äquivalenz zum bisherigen Filter aus Dokument 2: findingType = ioe AND exposureState = detected entspricht jetzt is_ioe = true. Der Feinfilter über not_detected, not_evaluated, execution_error läuft weiterhin über exposure_state, das nun im exposure-Block sitzt und nur für exposure_finding existiert.

Quellen-Mapping

Quelle Nativwert result_type Zustandsfeld (Block) result_state is_ioe
Purple Knight IOEs found exposure_finding exposure_state = detected fail true
Purple Knight No IOE found exposure_finding exposure_state = not_detected pass false
PingCastle RiskRule Points > 0 exposure_finding exposure_state = detected fail true
PingCastle RiskRule Points = 0 exposure_finding exposure_state = not_detected pass false
PingCastle nicht auswertbar exposure_finding exposure_state = not_evaluated not_evaluated false
PingCastle Ausfuehrungsfehler exposure_finding exposure_state = execution_error error false
Maester Security Test Failed exposure_finding exposure_state = detected fail true
Maester Passed exposure_finding exposure_state = not_detected pass false
Maester Diagnose / nicht ausgefuehrt informational – not_evaluated false
CIS-CAT XCCDF fail compliance_check xccdf_result = fail fail false
CIS-CAT XCCDF pass compliance_check xccdf_result = pass pass false
CIS-CAT XCCDF error compliance_check xccdf_result = error error false
CIS-CAT XCCDF notapplicable compliance_check xccdf_result = notapplicable not_applicable false
CIS-CAT XCCDF notchecked / notselected compliance_check xccdf_result = notchecked not_evaluated false
CIS-CAT XCCDF informational compliance_check xccdf_result = informational informational false
GPO vs. Baseline Abweichung policy_deviation verdict = deviation fail false
GPO vs. Baseline Uebereinstimmung policy_deviation verdict = match pass false
GPO vs. Baseline Setting fehlt beim Subjekt policy_deviation verdict = missing_on_subject informational false
GPO vs. Baseline Setting nur beim Subjekt policy_deviation verdict = extra_on_subject warning false
GPO vs. Baseline nicht vergleichbar policy_deviation verdict = not_comparable unknown false

Vorbehalte: Welche Maester-Tests als Exposure zählen, entscheidet das versionierte Profil (Dokument 2, Abschnitt 8). Ob ein fehlendes Setting als fail oder not_applicable gilt, ist settingabhängig und wird im GPO-Submodell festgelegt, nicht hier. Die XCCDF-Zustände notchecked, notapplicable und error dürfen nicht kollabiert werden.

Typspezifische Detailblöcke

Der Nativreichtum jeder Quelle bleibt in einem typspezifischen Block erhalten. Die normalisierten Top-Level-Felder sind der Vertrag; die Blöcke sind die Wahrheit der Quelle.

exposure     exposure_state, score_original, score_unit, native_severity
compliance   xccdf_result, benchmark_id, rule_id, profile_id, remediation
deviation    setting_key (kanonisch), baseline_value, subject_value, verdict,
             baseline_ref, baseline_version

Der deviation-Block ist hier nur in seiner Rolle definiert. Sein vollständiges Vergleichs-Submodell inklusive Setting-Identität und Verdikt-Logik ist noch offen (siehe Abschnitt Offene Punkte).

Kanonische Asset-Schicht: CANONICAL_ASSET

Ein Knoten wird ausschließlich über einen autoritativen, stabilen Schlüssel identifiziert. Anzeigenamen führen niemals zu einer Knotenzusammenführung. Dies ist die „keine Textähnlichkeit"-Regel aus Dokument 2, angewandt auf die Knotenidentität. Ohne diese Regel zerfällt der Graph in Dubletten oder verschmilzt falsch. Beides zerstört jede Blast-Radius-Aussage.

CANONICAL_ASSET
────────────────────────────────
asset_id            internes Surrogat-PK
asset_key           deterministischer Dedup-Schluessel, namespaced
asset_key_kind      sid | object_guid | gpo_guid | domain_sid | trust_key | synthetic
asset_type          user | group | computer | domain_controller | domain
                    | ou | gpo | trust | tenant | service_principal | unknown
primary_name        Anzeigename, nicht autoritativ
domain
key_confidence      1.0 bei autoritativem Schluessel, < 1.0 bei synthetic
resolution_method   wie der Schluessel gewonnen wurde
source_tools        welche Tools diese Entitaet gemeldet haben
first_seen_at
last_seen_at
original_fields  jsonb
extensions       jsonb

Schlüsselpriorität, absteigend autoritativ:

objectSID    Security Principals (user, group, computer, DC)   autoritativ
objectGUID   alle AD-Objekte, stabil ueber Umbenennung          autoritativ
gpo_guid     GPOs (der {GUID}-Ordner im Backup, gpreport)       autoritativ
domain_sid   Domaenenknoten                                     autoritativ
trust_key    Trust = (Quell-Domaenen-SID, Ziel-Domaenen-SID)    autoritativ
synthetic    nur-Name-Meldung ohne SID/GUID                     NICHT autoritativ

Merge-Regel (hart): Zwei Zeilen werden nur zusammengeführt, wenn sie denselben autoritativen Schlüssel teilen. Namensgleichheit löst niemals einen Merge aus.

Konservative Wahl für synthetic (in Version 1.0.0 verbindlich): Ein synthetischer Knoten verschmilzt nie automatisch mit einem autoritativen Knoten, auch nicht bei exaktem Namens- und Domänen-Match. Eine Bindung erfolgt ausschließlich über eine explizite, versionierte, protokollierte Rekonziliationsregel. Das erzeugt sichtbare Dubletten, bis eine solche Regel greift; diese Dubletten sind auditierbar und werden einer stillen Fehlverschmelzung vorgezogen. Kein erfundener SID.

Kantenschicht: ASSET_EDGE

Kanten sind eine eigenständige Entität, getrennt von Findings. Ein Finding ist ein Knoten-Attribut; eine Kante ist eine Beziehung zwischen zwei Knoten. Diese Trennung ist die Voraussetzung dafür, dass ein Attack Path überhaupt berechenbar wird.

ASSET_EDGE
────────────────────────────────
edge_id
source_asset_id       FK CANONICAL_ASSET
target_asset_id       FK CANONICAL_ASSET
edge_type             siehe Katalog
direction             gerichtet: source -> target
edge_source            Tool + Import-Artefakt, das die Kante erzeugt hat
collection_method     z. B. pingcastle-html-trust-table, gpo-backup-dacl
collector_version
source_locator
mapping_method
mapping_rule_version
confidence
first_seen_at
last_seen_at
properties       jsonb   kantentyp-spezifische Attribute
original_fields  jsonb

edge_source, collection_method und collector_version sind verpflichtend. Sie sind die Grundlage der ehrlichen Blast-Radius-Deklaration: Nur wenn jede Kante ihre Herkunft trägt, ist ableitbar, welche Kantentypen erhoben wurden und welche fehlen.

Kantentyp-Katalog

Der Katalog ist selbst versioniert und Teil des Schemas. „Nicht erhoben" ist damit wohldefiniert: Katalog minus erhobene Typen.

edge_type Quelle im Bestand Stufe Status
MemberOf PingCastle / PK Detailtabellen A extrahierbar
Trusts PingCastle Trust-Bericht A extrahierbar
AllowedToDelegate PingCastle (Constrained Delegation) A extrahierbar
AllowedToActOnBehalfOf PingCastle (RBCD) A extrahierbar
GpLink GPO-Backup / gpreport A extrahierbar
Contains (OU -> Objekt) GPO-Backup / gpreport A teilweise
GPO-DACL (WriteDacl, WriteOwner, GenericAll, GenericWrite) GPO-Backup ACL A extrahierbar
HasSession – B nicht in den Quellen
AdminTo (lokaler Admin) – B nicht in den Quellen
CanRDP / CanPSRemote / ExecuteDCOM – B nicht in den Quellen
Objekt-DACL (alle Objekttypen) – B nicht in den Quellen

Die Stufe-B-Zeilen sind die BloodHound-/SharpHound-Kanten, aus denen die meisten realen Pfade entstehen. Sie sind der Grund, warum Stufe A eine Angriffsflächen-Topologie liefert, aber noch keinen vollständigen Attack Path. Ihre Ingestion ist eine eigenständige, noch nicht eingeplante Datenerfassung.

Finding zu Graph-Rolle

Nicht jedes Finding ist eine Kante. Die Abbildung erfolgt über eine versionierte Regel-Tabelle in genau drei Rollen und wird nie per Import erfunden.

Knoten-Eigenschaft: AS-REP-Roastable (DONT_REQ_PREAUTH), Kerberoastable (SPN gesetzt), Unconstrained Delegation, „Passwort läuft nie ab". Das sind Fähigkeiten eines Knotens, keine Beziehungen. Sie landen als Property am CANONICAL_ASSET, referenziert auf das erzeugende Finding.

Kante: RBCD erzeugt eine AllowedToActOnBehalfOf-Kante, Constrained Delegation eine AllowedToDelegate-Kante, eine GPO-ACL-Berechtigung eine GPO-DACL-Kante. Diese Findings haben strukturelle Bedeutung im Graphen.

Reines Overlay: Ein CIS-Härtungs-Fail oder eine Policy-Abweichung sitzt auf einem Knoten (etwa einem DC oder GPO), erzeugt aber weder Kante noch angriffsrelevante Eigenschaft. Kontext auf dem Knoten.

Die Zuordnung Finding-Typ zu Graph-Rolle trägt mapping_rule_version und bleibt damit prüfbar und austauschbar, analog zur Score-to-Severity-Sperre.

EDGE_COVERAGE und ehrlicher Blast Radius

Ein Blast Radius über eine unvollständige Kantenmenge unterschätzt die reale Erreichbarkeit. Ihn als vollständig darzustellen wäre ein Prüffehler. Deshalb wird die Kantenabdeckung explizit modelliert.

EDGE_COVERAGE
────────────────────────────────
assessment_id / scan_run_id
edge_type
edge_source
collected           true | false
collector_version

Jede Erreichbarkeits- oder Blast-Radius-Berechnung läuft über einen deklarierten Kantentyp-Filter und liefert das Ergebnis mit zwei Pflichtangaben: computed_over_edge_types und uncollected_edge_types (Katalog minus erhoben). Jede Graph-Aussage trägt damit sichtbar, worauf sie beruht und was ihr fehlt.

Deterministische Graph-Operationen

Diese Operationen sind reine Graph-Algorithmen, damit assertionssicher und ohne Kennzeichnungspflicht.

Erreichbarkeit    erreichbare Knotenmenge von X unter Kantenfilter E
Kuerzester Pfad   zwischen Knoten A und B unter E
Transitive Huelle Blast Radius = erreichbare Menge von X unter E
Finding-Overlay   welches Finding sitzt auf welchem Knoten

Nicht Teil dieser Schicht, weil heuristisch und kennzeichnungspflichtig: Pfad-Ranking, Risikobewertung eines Pfades, Blast Radius als Impact-Score statt roher Erreichbarkeitsmenge, das Verketten von ATT&CK-Techniken zu einer Angriffserzählung, sowie jede Cross-Tool-Knotenidentität, die auf etwas anderem als SID oder GUID beruht.

Enrichment: ATT&CK und ANSSI

MITRE ATT&CK und ANSSI-Härtungsstufen sind eine Taxonomie der Findings, kein Graph der Umgebung. Als Knoten- und Finding-Annotation sowie als Filter- und Gruppierungsachse sind sie deterministisch und zulässig. Als Pfadgenerator sind sie unzulässig, da das Verketten von Techniken eine generierte Angriffserzählung wäre.

Was nicht Knoten ist

Registry-Werte, einzelne Policy-Settings und CIS-Regeln sind keine Graph-Knoten. Sie bleiben Inhalt von Findings beziehungsweise des deviation-Blocks. Ein Policy-Deviation-Finding kann einen GPO referenzieren; der GPO ist ein Knoten, das Setting darin nicht.

Invarianten

1.  result_type ist geschlossen und versioniert. Keine Quelle fuehrt Ad-hoc-Typen ein.
2.  result_state wird abgeleitet, nie roh aus einem Quell-String oder Spaltennamen uebernommen.
3.  is_ioe wird per expliziter, versionierter Regel abgeleitet; nie aus der Quelle uebernommen;
    nie fuer compliance_check oder policy_deviation per Default true.
4.  Nativwerte bleiben parallel vollstaendig erhalten (typspezifischer Block plus original_fields).
5.  Jede Ableitung traegt mapping_method und mapping_rule_version.
6.  Fehlend ist NULL bzw. unknown, nie erfunden.
7.  Tooluebergreifend vergleichbar sind nur result_type, result_state, severity_normalized, is_ioe.
    score ist tooluebergreifend nicht vergleichbar und darf typuebergreifend nicht sortiert
    oder schwellwertgefiltert werden.
8.  Knotenidentitaet nur ueber autoritativen Schluessel; Name fuehrt nie zu Merge.
9.  synthetic-Knoten verschmelzen nie automatisch mit autoritativen Knoten.
10. Kanten sind erste Klasse, getrennt von Findings.
11. Jede Kante traegt edge_source, collection_method, collector_version.
12. Erreichbarkeit nur ueber deklarierten Kantentyp-Filter; Ergebnis nennt uncollected_edge_types.
13. Kanten sind gerichtet; Richtung ist semantisch.
14. Finding zu Graph-Rolle nur ueber versionierte Regel, nie per Import erfunden.

Filter-Semantik

[ IOE ]           primaer: is_ioe = true. Semantisch unveraendert gegenueber Dokument 2.
[ Abweichung ]    ASSESSOR-Ergaenzung, nicht Teil des Vertrags: is_ioe OR outcome = warning.
[ result_type ]   oberste Facette: Exposure / Compliance / Policy / Informational.
[ result_state ]  typuebergreifend: Fail / Pass / Warning / Error / N/A / Not evaluated.
[ Severity ]      orthogonal, kategorial, typuebergreifend.

Typspezifische Facetten: exposure_state nur bei Exposure, xccdf_result nur bei Compliance, verdict nur bei Policy. Alle Facetten sind kombinierbar.

Beispiele:

is_ioe = true
is_ioe = true AND source_tool = pingcastle
is_ioe = true AND severity_normalized = high
result_type = compliance_check AND result_state = fail
result_type = policy_deviation AND verdict = deviation
result_state = fail

Verhältnis zu bestehenden Dokumenten

Additiv. result_type, result_state und is_ioe ergänzen die dokumentierte Trennung von Originalwert und normalisiertem Wert (CANONICAL_MODEL.md). RESULT_ASSET referenziert künftig canonical_asset_id statt Namen inline zu speichern; dies ist der einzige Eingriff in die bestehende Struktur und zugleich der Punkt, an dem Deduplizierung entsteht. Der gesamte PingCastle-Extraktions- und Verknüpfungspfad aus Dokument 2 bleibt unverändert und speist Knoten und Stufe-A-Kanten aus den bereits extrahierten Detailtabellen.

Offene Punkte (nicht Teil von 1.0.0)

- GPO-/Baseline-Vergleichs-Submodell: Setting-Identitaet, Verdikt-Logik,
  Behandlung von "nicht konfiguriert" vs. "abwesend", baseline_ref-Versionierung.
- CIS-CAT-Adapter: Festlegung XCCDF/ARF vs. JSON nach Verifikation der Lizenzausgabe.
- Elevationsregeln, die einzelne Compliance-/Policy-Befunde auf is_ioe = true heben.
- Rekonziliationsregeln fuer synthetic-Knoten (Bindung an autoritative Knoten).
- Stufe B: Ingestion von BloodHound-/SharpHound-Klasse-Kanten (Session, AdminTo, DACL).
- Mandanten-/Isolationsmodell ueber project_id hinaus (Row-Level Security o. ae.).

Changelog

1.0.0   Erstfassung. result_type als oberster Diskriminator, result_state normalisiert,
        is_ioe als abgeleitete indizierte Facette. Typspezifische Bloecke (exposure,
        compliance, deviation). Asset-Schicht (CANONICAL_ASSET) mit autoritativer
        Schluesselprioritaet und konservativer Merge-Regel. Kantenschicht (ASSET_EDGE),
        Kantentyp-Katalog, EDGE_COVERAGE, deterministische Graph-Operationen.

Abweichungen der ASSESSOR-Realisierung

Dieser Abschnitt ist kein Teil des externen Vertrags, sondern dokumentiert, wo und warum die konkrete Umsetzung in ASSESSOR von diesem Text abweicht. Grundlage ist die Integrationsentscheidung „Pfad A" vom 2026-07-23: additive Abbildung auf das bestehende ASSESSOR-Datenmodell statt einer zweiten, parallelen Resulttabelle.

Abweichung: eigenes Praedikat neben is_ioe, nicht statt is_ioe

is_ioe bleibt exakt wie im Vertrag definiert: exposure_finding mit exposure_state = detected, ohne Elevationsregel. Die Arbeitsansichten brauchen darueber hinaus eine breitere Frage — „verlangt dieses Ergebnis Aufmerksamkeit?" —, weil ein Werkzeug ein Ergebnis melden kann, das weder bestanden noch fehlgeschlagen ist. Maester meldet dafuer Investigate, ASSESSOR normalisiert das auf outcome_normalized = warning.

ASSESSOR fuehrt dafuer ein eigenes versioniertes Praedikat (src/domain/deviation-predicate.ts, Version 1.0.0):

is_deviation := is_ioe OR outcome_normalized = 'warning'

Es ist nicht persistiert, sondern wird bei der Projektion abgeleitet; die PostgreSQL-Vorfilterung bildet dieselbe Formel ab. Es liest ausschliesslich bereits normalisierte Achsen und gilt damit ohne Aenderung fuer jedes weitere Werkzeug. Ausdruecklich nicht enthalten sind pass, not_run, not_applicable und execution_error; ein Ausfuehrungsfehler ist ein technisches Problem des Laufs und hat mit technical_execution_state eine eigene Achse.

Offen bleibt, dass deriveExposureState fuer outcome = warning weiterhin unknown liefert. Ein eigener Zustand (etwa indeterminate) wuerde den Vertragsenum, die Check-Constraint aus Migration 007 und damit eine Migration samt Versionssprung beruehren; er ist bewusst noch nicht umgesetzt.

result_state statt eigener Spalte: abgeleitete Projektion

ASSESSOR trennt seit Migration 007_unified_result_read_model.sql bewusst zwei Dimensionen, die dieser Vertrag in einer Spalte result_state zusammenführt:

  • outcome_normalized — das fachliche Ergebnis (fail, pass, warning, informational, execution_error, not_run, not_applicable, not_evaluated, unknown)
  • technical_execution_state — ob die Prüfung technisch überhaupt durchlief (completed, failed, not-run, unknown)

Begründung im Umsetzungsplan: „Ein technischer Parser-/Toolfehler ist kein fachlich fehlgeschlagener Check." Ein result_state = error würde diese Unterscheidung wieder aufheben. ASSESSOR führt deshalb keine physische result_state-Spalte. Wo ein Konsument den Vertragswert braucht, wird er per versionierter Regel aus beiden Bestandsachsen projiziert (projectResultState in src/domain/result-contract-mapping.ts, RESULT_CONTRACT_MAPPING_VERSION). Die Bestandsachsen bleiben die persistierte Wahrheit.

source_tool bleibt offen statt geschlossenes Enum

tool_id ist in ASSESSOR ein freier Wert, kein geschlossenes Enum. Grund ist der generische Fallback (Leitprinzip „unbekannte Quellen müssen importierbar bleiben"): ein geschlossenes source_tool-Enum würde jede noch nicht katalogisierte Quelle auf unknown zwingen und die tatsächliche Tool-Kennung verlieren. source_tool im Sinne dieses Vertrags ist daher eine Projektion über tool_id, kein Speicherformat.

review_status bleibt zweidimensional

ASSESSOR trennt reviewStatus (Workflow-Zustand) und reviewDisposition (Entscheidung bei Abschluss) mit eigener ReviewWorkflowStatus-Achse (7 Werte, inkl. optimistischer Revision je Review). Der einwertige review_status dieses Vertrags wird bei Bedarf daraus abgeleitet, nicht umgekehrt gespeichert.

Keine eigene canonical_result-Tabelle

result_type, is_ioe, ioe_rule_id, ioe_rule_version, ioe_derivation_method sowie exposure_state sind additive Spalten auf der bestehenden Occurrence-Tabelle (assessor_finding_occurrences, Migration 008_result_contract_facets.sql), nicht Spalten einer neuen Haupttabelle. result_compliance und result_deviation wurden nicht als neue Haupttabellen angelegt. GPO-/Baseline-Vergleiche stammen aus den versionierten Policy-Evaluation-Entitäten und werden nur im Read-Modell vereinheitlicht.

Datenschutzgrenze der Result DB

Die Result DB ist eine fail-closed, LLM-faehige Offenlegungsprojektion und kein Rohdaten- oder Kundenkontextspeicher. Seit Disclosure Policy 2.1.0 duerfen regulaere kanonische Findings und GPO-Vergleiche keinen personen- oder kundenidentifizierenden Inhalt transportieren. Bei den explizit bekannten Scannerprofilen CIS-CAT, Maester, PingCastle und Purple Knight bleiben jedoch regelbezogene Titel, Beschreibungen und Empfehlungen erhalten, damit die Result DB fachlich und fuer ein LLM nutzbar bleibt. Vor der Ausgabe ersetzt die Projektion exakte private Begleitwerte und feste Identifierklassen durch typisierte Platzhalter und wandelt HTML-Regelprosa in Klartext um. Assets, Personen-/Reviewfelder, Tenant-/Host-/GPO-/Backupidentitaeten, Locators, Quellzeitpunkte und interne Artefaktprovenienz bleiben ausschliesslich im internen Analysepfad. Unbekannte Werkzeuge und generische Quellen fallen weiterhin auf kontrollierte Bezeichnungen ohne Quellprosa zurueck.

Disclosure Policy 2.5.0 erweitert die freigegebenen PingCastle-Rule-IDs auf den oeffentlich dokumentierten Regelkatalog. Die Validierung ist gross-/kleinschreibungsunabhaengig, die Ausgabe behaelt die native Schreibweise; unbekannte IDs bleiben aus der LLM-faehigen Result DB ausgeschlossen.

Disclosure Policy 2.5.0 zieht fuer GPO-Vergleiche die verbindliche Grenze zwischen technischer Abweichung und Quellenidentitaet. Titel, technische Setting-ID, Policy-Pfad, Value Name, Operation, Registry-Typ, konfigurierter Wert, erwarteter Wert, Comparator-Relation, Status, oeffentliche Baselinebezeichnung/-version, oeffentliche Beschreibung/Empfehlung, belegte Diagnosen, Verarbeitungsstaende und die Aussagegrenze des Backupvergleichs duerfen vollstaendig erscheinen. Kunden-/Projektname, Domainlabel, kundenspezifischer GPO-Name, GPO-/Backup-GUID, Datei-/Sharepfad, interne Projekt-/Assessment-/Run-/Import-/Artefaktidentitaet, Source Locator, Zeitpunkte und Review-/Risikoakzeptanzdaten bleiben ausgeschlossen. policyIdentityKey und interne Baseline-IDs werden nicht ausgegeben; die technische Setting-ID steht ausschliesslich als kontrollierte Source Rule ID. Automatisch erzeugte gpo-policy-identity-*-Hashes sind niemals oeffentliche Regelidentitaeten.

Wohlbekannte oeffentliche Principals wie S-1-5-18, S-1-5-32-544 und BUILTIN\\Administrators bleiben technisch lesbar. Domain-SIDs verlieren ihre eindeutigen Domainkomponenten (S-1-5-21-[DOMAIN]-<RID>), bekannte Domainrollen werden als [DOMAIN]\\<Rolle> dargestellt und sonstige konkrete Benutzer/Gruppen erhalten stabile, nur innerhalb des einzelnen Resultats gueltige Platzhalter [DOMAIN_PRINCIPAL_n]. Dieselbe Reduktion gilt rekursiv fuer Baselinevarianten. Die Originalzuordnung bleibt ausschliesslich in Work und GPO Analysis erhalten.

Die fachlichen Bewertungsachsen bleiben erhalten: normalisierte Severity und Outcome, Result-Typ, Abweichung/IOE, kontrollierte GPO-Klassifikation, Comparator-Relation sowie nicht identifizierende Aggregate und technische Versionsmetadaten. Damit bleibt die Sicherheitsbewertung fuer ein LLM nutzbar, ohne Kunden- oder konkrete GPO-Quellenidentitaet weiterzugeben. Die Aussage bezieht sich auf Identifizierbarkeit; ein Finding beschreibt weiterhin den anonymisierten Sicherheitszustand des aktiv ausgewaehlten Scopes.

Ergaenzende Scannerbeobachtungen durchlaufen vor der Unified-Result-Projektion die versionierte, fail-closed Disclosure Policy result-db-supplemental-disclosure 1.0.0. Eine Beobachtung wird nur ausgegeben, wenn Werkzeug, Property und ein enger Datentyp-/Wertebereich ausdruecklich freigegeben sind. Titel, Subject-Klasse und Property werden aus der kontrollierten Regel neu aufgebaut. Subject-Identitaet, Originalwert, Attribute, betroffener Assetname, Abschnitt und Source Locator werden niemals aus der Beobachtung in die Result DB uebernommen. Unbekannte Werkzeuge und neue Zusatzfelder bleiben vollstaendig in der internen Persistenz, sind aber bis zu einer bewussten Klassifikation nicht Teil der Result DB. Das betrifft nicht ihre regulaeren kanonischen Findings.

Rohe GPO-Policy-Beobachtungen sind ebenfalls kein Result-DB-Inhalt. Sie bleiben im expliziten internal-analysis-Lesemodus fuer Work und GPO Analysis erhalten; versioniert evaluierte Soll-/Ist-Vergleiche erreichen die normale Result DB als policy_deviation. Ohne freigegebene Risikoregel bleibt die Severity unknown; die Finding Factory darf nur belegte Ergebnisse anreichern und erzeugt keine doppelte Result-Zeile. Der JSONL-Export verwendet Schema 2.0.0 und Disclosure Policy 2.5.0. Er wendet die allgemeine Projektion nochmals an, verlangt fuer jede Environment-Zeile weiterhin eine Disclosure-Klassifikation und entfernt zusaetzlich interne Result-, Projekt-, Assessment-, Run-, Snapshot-, Evaluation- und Artefaktidentitaeten sowie Zeitpunkte. Manifest-Scope und Filter werden nur als Boolesche beziehungsweise Mengenangaben ausgegeben; Suchtext, Assetfilter und konkrete Scope-IDs werden nie serialisiert.

Disclosure Policy 2.6.0 (2026-09-28) schliesst eine Luecke zur obigen Grenze: reviewStatus, reviewDisposition (einschliesslich akzeptierter Risiken) und reviewPriority gelangten zuvor in Result DB API und JSONL. Die Projektion gibt jetzt nur noch Felder aus, die in RESULT_DB_FIELD_DISCLOSURE ausdruecklich als disclose klassifiziert sind; ein neues Projektionsfeld verhindert die Kompilierung, bis es klassifiziert ist, und zur Laufzeit hinzugefuegte Felder fallen weg. Review-Status und -Disposition sind deshalb keine Result-DB-Filter mehr; Work und die interne Analyse behalten den Review unveraendert. JSONL-Schema 2.1.0 ergaenzt im Manifest handling mit der Klassifikation confidential-security-posture: Die Result DB ist anonymisiert, beschreibt aber weiterhin ausnutzbare Schwaechen der bewerteten Umgebung und ist bei Weitergabe an externe LLM-Anbieter oder Dritte entsprechend zu behandeln.

Disclosure Policy 3.0.0 und JSONL-Schema 3.0.0 (2026-09-28, verbindliche Nutzeranforderung, ersetzt die Entscheidung vom 2026-09-25 zum vollstaendigen Ist-/Sollwert): Die Result DB ist datenschutzkonform und keine Angriffsanleitung. Sie enthaelt keine Werte, die die bewertete Umgebung wiedererkennbar machen oder den Angriffsaufwand kalibrieren. Der GPO-Istwert samt Principals, die Anzahl der GPO-Quellen und konkrete Baseline-Releases entfallen; es bleiben technische Setting-ID, Sollwert, Comparator-Relation und die Baseline-Familie. Identische Baseline-Erwartungen werden zusammengefasst. In Scannerprosa ausser CIS-CAT werden exakte Anzahlen, Datums-, Zeit- und Dauerangaben zu [count], [date], [time] und [duration]; oeffentliche technische Kennungen (Versionen, CVE/KB, Regel- und ATT&CK-IDs, Produktnamen, Ports, Event-IDs, oeffentliche SIDs) bleiben. CIS-CAT-Prosa ist Benchmarktext und behaelt ihre oeffentlichen Empfehlungswerte. Die Zusatzbeobachtungs-Policy 2.0.0 laesst nur noch binaere Konfigurationszustaende zu; Anzahlen, Scores, Funktionsebenen und Versionen bleiben intern. Das JSONL-Manifest enthaelt statt generatedAt nur generatedOn (Kalendertag), der Dateiname nur das Datum. Work und interne Analyse behalten alle Originalwerte.

Disclosure Policy 3.1.0 (2026-09-28, Nutzerkorrektur nach Pruefung des Dummy-Exports) begrenzt die Schwaerzung auf echte Umgebungswerte: Nummerierte Schritte bleiben; nur das Empfehlungsfeld behaelt Hersteller-Schwellenwerte (at least 8 characters, more than 180 days, 15 or more), Titel und Beschreibung bleiben vollstaendig geschwaerzt. Gruppenrichtlinien- und Registry-Pfade sowie dokumentierte Windows-Autoritaeten (NT SERVICE, RESTRICTED SERVICES u. a.) gelten nicht als Konten. [ip] betrifft nur alleinstehende IPv4-Adressen, nie CIS-Regelnummern, Rule-Verweise oder dokumentierte Bereichsgrenzen. Der Handling-Hinweis nennt jetzt ausdruecklich, dass oeffentliche Baseline-Erwartungen, Herstellerhinweise und Schwellenwerte erhalten bleiben.

Disclosure Policy 3.2.0 (2026-09-28, Nutzerkorrektur nach Pruefung des gefilterten Dummy-Exports) erhaelt Inline-Aufzaehlungen (Folge ab 1 mit mindestens zwei Punkten, eine wiederholte oder uebersprungene Quellnummer toleriert), dokumentierte Attributflags (adminCount=1) und im Empfehlungsfeld Zielwerte (set to 1). Links und Hostnamen von Hersteller- und Standard- dokumentation bleiben, solange sie weder @, Tenant-Merkmale (onmicrosoft, sharepoint), private Literale noch weitere nicht oeffentliche Hosts enthalten. Tenant-Hosts und alle uebrigen Hosts bleiben geschwaerzt. Dateinamen, ScubaGear-Kennungen, Graph-Berechtigungen, Code-Namespaces und -Bezeichner sowie Satzverbindungen ohne Leerzeichen gelten nicht als Domain.

Gespeicherte Result DB (2026-09-28, verbindliche Nutzeranforderung, Migration 024): Die Zeilen werden beim Erzeugen offengelegt, geprueft und in assessor_result_db_rows gespeichert, mit Statussatz assessor_result_db_state (Revision, Policy- und Waechterversion, Zeilen, Ersetzungen, gesperrte Zeilen). Der Aufbau laeuft nach dem Start und nach jeder gespeicherten Aenderung; Lesezugriffe warten auf einen aktuellen Stand. Jede Zeile traegt disclosureTrace (Feld, Regel, Anzahl, zurueckgehaltene Felder) ohne Originalwerte. Der Identitaetswaechter 1.3.0 entfernt die eigenen Namen des Clients aus allen Zeilen und sperrt ein Feld, wenn ein Name in anderer Schreibweise ueberlebt. GET /api/v1/results/status liefert den Pruefstand; die Oberflaeche zeigt ihn und im Zeilendetail das Protokoll mit Open original in Work.

Asset-/Kantenschicht: Struktur ohne Producer

CANONICAL_ASSET, ASSET_EDGE, ASSET_PROPERTY und EDGE_COVERAGE wurden strukturgetreu als assessor_canonical_assets, assessor_asset_edges, assessor_asset_properties und assessor_edge_coverage übernommen (Migration 009_asset_graph.sql), mit text-Primärschlüsseln statt uuid gemäß Bestandskonvention. In diesem Integrationsschritt existiert noch kein Producer, kein Merge- und kein Rekonziliationscode — die Tabellen sind bewusst leer nutzbar angelegt, nicht befüllt.