33 KiB
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.
Disclosure Policy 3.5.0 (2026-10-02, Nutzerentscheidung) nimmt Microsoft Defender
for IoT als bekanntes Werkzeug auf. Freigegeben wird nur eine formal gueltige
oeffentliche CVE-ID (CVE-JJJJ-NNNN…) als Rule ID und zugleich als Titel; Geraete-,
Site-, Sensor- und Zaehlwerte bleiben wie bisher ausgeschlossen. Das Profil
Defender for IoT Alerts bleibt ein generisches Werkzeug ohne freigegebene Rule ID.
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.