349 lines
16 KiB
Markdown
349 lines
16 KiB
Markdown
# Umsetzungsauftrag: Report A (Management) und Report B (Technical)
|
||
|
||
Adressat: Codex. Dieser Auftrag ist verbindlich und abschließend formuliert.
|
||
Wo dieser Auftrag schweigt, gelten `AGENTS.md`, `docs/REPORT_STRATEGY.md`,
|
||
`docs/SCORING_MODEL.md` und `.claude/skills/assessor-ui/SKILL.md` unverändert
|
||
weiter. Bei einem Widerspruch zwischen diesem Auftrag und einer dieser Quellen:
|
||
**anhalten und den Widerspruch melden, nicht eigenmächtig auflösen.**
|
||
|
||
Plan-Zuordnung: Die Artefakte A–D sind in `docs/REPORT_STRATEGY.md` als
|
||
Deliverable ohne zugewiesene Plan-Phase festgehalten. Dieser Auftrag setzt A und
|
||
B um und ändert die Strategie inhaltlich nicht.
|
||
|
||
## 0. Ausgangszustand im Code (verifiziert)
|
||
|
||
| Baustein | Zustand |
|
||
| --- | --- |
|
||
| `src/domain/report-projection.ts` | vorhanden, `REPORT_PROJECTION_VERSION = '1.0.0'`, getestet |
|
||
| `src/exports/management-overview-report.ts` | vorhanden, `buildManagementOverviewHtml`, Renderer-Version `'1.0.0'`, getestet |
|
||
| Produktiver Aufrufer von `buildReportProjection` | **existiert nicht** — nur Tests |
|
||
| Produktiver Aufrufer von `buildManagementOverviewHtml` | **existiert nicht** — nur Tests |
|
||
| Report B (Technical) | **existiert nicht** |
|
||
| Security-Domain-Zuordnung über `UnifiedResultProjection` | **existiert nicht** (Katalog arbeitet auf `FindingRecord`) |
|
||
| Framework-Referenzen (MITRE ATT&CK / D3FEND) in der Unified-Projektion | **nicht enthalten** (nur `targetRole === 'category'` wird übernommen) |
|
||
|
||
Die beiden vorhandenen Bausteine sind korrekt und werden **nicht neu geschrieben**,
|
||
sondern additiv erweitert.
|
||
|
||
## 1. Lieferumfang
|
||
|
||
Acht Arbeitspakete, in dieser Reihenfolge. Jedes Paket ist einzeln lauffähig,
|
||
getestet und wird einzeln committet.
|
||
|
||
### AP1 — Security-Domain-Zuordnung für `UnifiedResultProjection`
|
||
|
||
**Datei:** `src/features/dashboard/security-domains.ts` (erweitern, nicht ersetzen)
|
||
|
||
Neue Funktion:
|
||
|
||
```ts
|
||
export function securityDomainIdForUnifiedResult(result: UnifiedResultProjection): string
|
||
```
|
||
|
||
Bindende Semantik:
|
||
|
||
- Es gilt **derselbe** `securityDomainCatalog` in **derselben** Reihenfolge.
|
||
Kein zweiter Katalog, keine zweite Regex-Menge, keine report-eigene Zuordnung.
|
||
- Evidenztext ist: `title`, `description` sowie `categoryLabel` aller Einträge
|
||
aus `result.categories`.
|
||
- Die explizite GPO-Erkennung geht wie bisher der Regex-Prüfung vor: erfüllt,
|
||
wenn `result.sourceKind` einer von `'policy-evaluation' | 'policy-observation'`
|
||
ist **oder** ein `categoryLabel` exakt `GPO`/`GPOs` lautet (case-insensitiv).
|
||
- Kein Treffer ⇒ `unclassifiedSecurityDomain.id`.
|
||
|
||
**Akzeptanzkriterium (Pflichttest):** Für einen synthetischen Fall, der sowohl
|
||
als `FindingRecord` als auch als `UnifiedResultProjection` gebaut wird, liefern
|
||
`securityDomainIdForRecord` und `securityDomainIdForUnifiedResult` **dieselbe**
|
||
Domain — für alle acht Katalogeinträge, für den GPO-Sonderfall und für
|
||
`unclassified`. Grund: Client-Info-Drilldown und Reports müssen dieselben
|
||
Datensatzidentitäten auswählen (`AGENTS.md`, Abschnitt *UI Work filter context*).
|
||
|
||
### AP2 — Framework-Referenzen in der Unified-Projektion sichtbar machen
|
||
|
||
**Datei:** `src/domain/unified-result.ts`
|
||
|
||
Neu:
|
||
|
||
```ts
|
||
export interface UnifiedResultFrameworkReference {
|
||
namespace: string; // Tool-ID der Quelle
|
||
value: string; // normalizedValue der Mapping-Eintragung, unverändert
|
||
sourceFieldKey?: string; // sourceFieldKeys[0], falls vorhanden
|
||
sourceLabel?: string; // Original-Spaltenüberschrift, falls vorhanden
|
||
}
|
||
```
|
||
|
||
und additiv auf `UnifiedResultProjection`:
|
||
|
||
```ts
|
||
frameworkReferences: UnifiedResultFrameworkReference[];
|
||
```
|
||
|
||
Befüllung analog zu `scannerResultCategories`, aber aus den Mappings mit
|
||
`targetRole === 'framework-reference'`. Leere Werte werden verworfen, alle
|
||
übrigen **unverändert und unnormalisiert** übernommen.
|
||
|
||
Bindende Randbedingungen:
|
||
|
||
- Die Ableitung erfolgt **zur Projektionszeit aus bereits persistierten
|
||
`fieldMappings`**. Es wird **keine** neue Spalte, Tabelle oder Migration
|
||
angelegt und **kein** Parsing geändert.
|
||
- Damit ist dies presentation-only über bereits gespeicherte Fakten:
|
||
**kein** neuer Processing-Manifest-Eintrag, **keine** Reimport-Markierung
|
||
(`docs/REIMPORT_POLICY.md`).
|
||
- Sollte sich beim Umsetzen zeigen, dass eine Persistenzänderung nötig wäre:
|
||
**anhalten und melden**, nicht persistieren.
|
||
|
||
**Akzeptanzkriterium:** Synthetische Occurrence mit zwei
|
||
`framework-reference`-Mappings und einem `category`-Mapping ⇒ zwei
|
||
`frameworkReferences`, eine `category`, keine Vermischung, Originalwerte identisch.
|
||
|
||
### AP3 — Korrelationsmodul (Domain, plattformneutral)
|
||
|
||
**Neue Datei:** `src/domain/report-correlation.ts`
|
||
**Konstante:** `export const REPORT_CORRELATION_VERSION = '1.0.0';`
|
||
|
||
Kein Storage, keine Uhr, kein Netzwerk, keine Präsentationstypen. Drei Ergebnisse:
|
||
|
||
**(a) Quellenkonvergenz je Security Domain**
|
||
|
||
```ts
|
||
export interface ReportDomainConvergence {
|
||
domainId: string;
|
||
sources: Array<{ toolId: string; assessed: number; deviations: number }>;
|
||
sourceCount: number; // Quellen mit mindestens einer Abweichung
|
||
evidence: 'shared-framework-reference' | 'domain-only';
|
||
}
|
||
```
|
||
|
||
`evidence` ist `'shared-framework-reference'` **nur dann**, wenn mindestens zwei
|
||
Quellen in dieser Domain eine **exakt gleiche, nicht leere**
|
||
`frameworkReferences`-Zeichenkette tragen. Andernfalls `'domain-only'`.
|
||
|
||
**(b) Maßnahmenbündel**
|
||
|
||
```ts
|
||
export interface ReportRemediationBundle {
|
||
key: string; // die Framework-Referenz, unverändert
|
||
namespace: string;
|
||
members: string[]; // resultProjectionIds
|
||
toolIds: string[];
|
||
domainIds: string[];
|
||
}
|
||
```
|
||
|
||
Gebildet über identische `frameworkReferences`-Werte von Abweichungen. Bündel
|
||
mit weniger als zwei Mitgliedern entfallen. Sortierung: Mitgliederzahl absteigend,
|
||
danach `key` alphabetisch — stabil, damit Ausgaben reproduzierbar sind.
|
||
|
||
**(c) GPO-Einzelquellen-Maßnahmen**
|
||
|
||
```ts
|
||
export interface ReportSingleSourcePolicyAction {
|
||
policyEntryId: string;
|
||
backupInstanceId: string;
|
||
}
|
||
```
|
||
|
||
Genau die `ReportPolicyEntry` mit `verdict === 'Fail'`, `conflict === false`,
|
||
`duplicate === false` und **exakt einem** Eintrag in `backupInstanceIds`.
|
||
|
||
Bindende Verbote für AP3:
|
||
|
||
- **Keine** Titel-, Wort-, Fuzzy- oder Ähnlichkeitsheuristik über Quellen hinweg.
|
||
- **Kein** quellenübergreifender Score, Rang oder normalisierter Schweregrad.
|
||
- **Keine** Aussage „gleicher Befund" — die Aussage lautet ausschließlich
|
||
„dieselbe Domain" bzw. „dieselbe Framework-Referenz".
|
||
- **Keine** Aufwands-, Kosten- oder Quick-Win-Schätzung. Ein Bündel trägt eine
|
||
Mitgliederzahl, keine Bewertung.
|
||
|
||
### AP4 — Projektion erweitern
|
||
|
||
**Datei:** `src/domain/report-projection.ts`
|
||
**Version:** `REPORT_PROJECTION_VERSION` von `'1.0.0'` auf `'1.1.0'`.
|
||
|
||
Additiv auf `ReportProjection`:
|
||
|
||
```ts
|
||
convergence: ReportDomainConvergence[];
|
||
bundles: ReportRemediationBundle[];
|
||
singleSourcePolicyActions: ReportSingleSourcePolicyAction[];
|
||
```
|
||
|
||
Bestehende Felder, Typen und Semantiken bleiben unverändert. Die bestehenden
|
||
Zusicherungen in `tests/domain/report-projection.test.ts` müssen ohne Änderung
|
||
grün bleiben; neue Tests kommen hinzu.
|
||
|
||
### AP5 — Report-Eingabe zusammenstellen
|
||
|
||
**Neue Datei:** `src/features/reports/report-input-assembler.ts`
|
||
|
||
```ts
|
||
export async function assembleReportProjectionInput(
|
||
repository: AssessorRepository,
|
||
scope: { projectId: string; assessmentId?: string; generatedAt: string }
|
||
): Promise<ReportProjectionInput>
|
||
```
|
||
|
||
Quellen, ausschließlich:
|
||
|
||
- Ergebnisse: `repository.queryUnifiedResults` über den Scope.
|
||
- `domainOf`: `securityDomainIdForUnifiedResult` aus AP1.
|
||
- `priorityBases`: `reportPriorityBases(...)` über die registrierten Profile.
|
||
- `scoreSources` / `overallScore`: die vorhandene Scoring-Schicht
|
||
(`src/domain/security-scoring.ts`,
|
||
`src/features/dashboard/dashboard-security-score.ts`).
|
||
**Keine eigene Score-Arithmetik.**
|
||
- `environment`: normalisierte Fakten aus dem Environment-Snapshot. Fehlende
|
||
Fakten bleiben **absent** und werden nie zu `0`.
|
||
- `policyEntries` / `policyScopeLimited`: aus der Evaluations- und
|
||
Aggregationsschicht mit dem **autoritativen, hochgerollten** Verdict.
|
||
**Kein** Verdict wird hier neu abgeleitet.
|
||
- `reviews`, `diagnostics`, `provenance`: aus den vorhandenen Repository-Pfaden.
|
||
- `delta`: nur, wenn ein **gespeicherter** Timeslot-Vergleich existiert. Es wird
|
||
**keine** Referenzlauf-Auswahl und **keine** Paarung im Export gebildet.
|
||
|
||
Die Zusammenstellung läuft **nur beim Export**, nie beim Laden eines Workspace.
|
||
|
||
### AP6 — Management-Renderer erweitern
|
||
|
||
**Datei:** `src/exports/management-overview-report.ts`
|
||
**Version:** `MANAGEMENT_OVERVIEW_RENDERER_VERSION` auf `'1.1.0'`.
|
||
|
||
Bestehende Abschnitte bleiben unverändert. Zwei neue Abschnitte:
|
||
|
||
1. **„Konvergenz der Quellen"** — direkt nach `Security Domains`. Je Domain:
|
||
Anzahl unabhängiger Quellen mit Abweichungen, die Quellen namentlich, und eine
|
||
sichtbare Unterscheidung zwischen belegter Konvergenz
|
||
(`shared-framework-reference`) und thematischer Nähe (`domain-only`). Letztere
|
||
trägt im Fließtext ausdrücklich den Hinweis auf **Validierungsbedarf**.
|
||
2. **„Maßnahmenbündel"** — direkt nach `Priority Findings`. Die Bündel aus AP3(b)
|
||
mit Referenz und Mitgliederzahl sowie die Anzahl der Einzelquellen-GPO-
|
||
Maßnahmen aus AP3(c). **Keine** Aufwands- oder Nutzenaussage.
|
||
|
||
Der Abschnitt „Abdeckung und Lücken" wird **vor** „Priority Findings" gezogen und
|
||
zeigt zusätzlich je Quelle den Anteil `not_run` an der Gesamtzahl. Begründung:
|
||
Die Aussagegrenze muss vor der Priorisierung stehen.
|
||
|
||
Unverändert bindend: keine Registry-Keys, SIDs, Evidenzpfade, Parser- oder
|
||
Manifest-Versionen in Report A.
|
||
|
||
### AP7 — Report B (Technical)
|
||
|
||
**Neue Datei:** `src/exports/technical-report.ts`
|
||
**Konstante:** `export const TECHNICAL_REPORT_RENDERER_VERSION = '1.0.0';`
|
||
**Signatur:** `export function buildTechnicalReportHtml(projection: ReportProjection): string`
|
||
|
||
Abschnitte in dieser Reihenfolge, gemäß `docs/REPORT_STRATEGY.md` Report B:
|
||
|
||
1. Scope und Provenance — Importe, Processing-, Analyzer- und Manifest-Versionen,
|
||
Reimport-Status, Aggregations- und Evaluations-Run-IDs.
|
||
2. Ergebnisinventar — Totals je Status, Severity, Domain und Tool, inklusive der
|
||
score-neutralen Klassen; Passed erscheint als Zahl.
|
||
3. Findings im Detail — gruppiert je Domain, innerhalb der Gruppe nach Severity.
|
||
4. GPO-Policy-Register — alle sieben Verdicts vollständig; die score-neutralen
|
||
sind **am Ort der Anzeige** als score-neutral gekennzeichnet.
|
||
5. Konvergenz-Nachweis — die Paare aus AP3(a) mit ihrer Begründung, damit die
|
||
Managementaussage aus AP6 nachprüfbar ist.
|
||
6. Scope-Grenzen — ausdrücklich: kein RSoP, keine Aussage über wirksame
|
||
Endpoint-Konfiguration, GPO-Aussagen gelten für das importierte Backup-Set,
|
||
`not_run` ist kein Pass.
|
||
7. Diagnostik — jede Beobachtung **einmal**, mit ihrem autoritativen Resolver-Grund.
|
||
8. Anhang — vollständiger Ergebnisindex.
|
||
|
||
### AP8 — Auslösung in der Anwendung
|
||
|
||
Zwei Aktionen in der bestehenden Oberfläche, die den Scope des aktuellen Projekts
|
||
bzw. Assessments verwenden und über `src/exports/download-adapter.ts` bzw.
|
||
`src/exports/text-download.ts` ausliefern. Keine neue Navigationsebene, keine neue
|
||
Shell, keine neuen Farb- oder Geometrie-Tokens. Es gilt
|
||
`.claude/skills/assessor-ui/SKILL.md` und der 20px-Panel-Inset.
|
||
|
||
## 2. Gestaltungsvorgaben für beide Dateien
|
||
|
||
- Sprache: Deutsch. Etablierte englische Fachbegriffe bleiben englisch
|
||
(`Deviation`, `Passed`, `Not configured`, `No baseline rule`, `Not comparable`,
|
||
`Conflicts`, `Duplicates`, `Finding`, `Baseline`, `Score`, `Pass Rate`,
|
||
Security-Domain-Namen, `Group Policy`, `Registry`, `Domain Controller`).
|
||
- Zahlen und Daten in deutscher Formatierung. Unbekanntes ist ein Geviertstrich,
|
||
niemals eine `0`.
|
||
- Getrennte Dateien. Report A enthält keine technischen Identifikatoren.
|
||
- Vollständig eigenständig: alle Styles inline, kein CDN, keine externe Font,
|
||
kein Skript, kein Bild, kein Netzwerkzugriff beim Öffnen. Druckbar nach PDF
|
||
ohne Layoutverlust.
|
||
- Typografie-Rollen, Donut-Geometrie, Tile- und Segment-Grammatik,
|
||
Statusfarbregeln und Panel-Inset wie in der Anwendung. Statusfarbe nur in
|
||
Akzentlinie, Marker, Arc oder Score-Node — nie als Flächenfüllung.
|
||
|
||
## 3. Harte Verbote
|
||
|
||
Nicht verhandelbar, auch dann nicht, wenn sie eine Lücke schließen würden:
|
||
|
||
1. Kein quellenübergreifender, normalisierter Schweregrad, Rang oder Risiko-Score.
|
||
2. Keine Aussage über wirksame Endpoint-Konfiguration aus einem Backup-Vergleich.
|
||
3. Kein GPO-Verdict aus Vergleichsprosa, Historie, Baseline-Text, Guidance oder
|
||
Evidenz — ausschließlich das autoritative hochgerollte Verdict plus die
|
||
expliziten Conflict- und Duplicate-Flags.
|
||
4. Ein selbst gemeldeter Review-Status wird nie zu einem gelösten Finding.
|
||
5. Kein Delta ohne gespeicherten Vergleich; keine Referenzlauf-Auswahl im Export.
|
||
6. Keine Score-Arithmetik, kein quellenspezifisches Parsing und kein
|
||
Datenbankzugriff in einem Renderer.
|
||
7. `Not configured`, `No baseline rule` und `Not comparable` bleiben score-neutral
|
||
und erscheinen nie in den Priority Findings.
|
||
8. Keine Titel- oder Ähnlichkeitsheuristik als Beleg für „gleicher Befund".
|
||
9. Keine echten Kunden-, Mandanten- oder Scan-Inhalte in Tests, Fixtures, Logs
|
||
oder Commit-Nachrichten. Ausschließlich synthetische Fixtures.
|
||
|
||
## 4. Tests (Pflicht, synthetisch)
|
||
|
||
| Paket | Testdatei | Muss nachweisen |
|
||
| --- | --- | --- |
|
||
| AP1 | `tests/features/security-domain-unified.test.ts` | Gleichstand beider Klassifizierer über alle acht Domains, GPO-Sonderfall, `unclassified` |
|
||
| AP2 | `tests/domain/unified-result-framework-references.test.ts` | Trennung `category` / `framework-reference`, Originalwerte unverändert, leere Werte verworfen |
|
||
| AP3 | `tests/domain/report-correlation.test.ts` | `shared-framework-reference` nur bei exakter Übereinstimmung über mindestens zwei Quellen; Bündel unter zwei Mitgliedern entfallen; stabile Sortierung; GPO-Einzelquelle schließt Conflict, Duplicate und Mehrfach-Backups aus |
|
||
| AP4 | Erweiterung `tests/domain/report-projection.test.ts` | Neue Felder vorhanden, bestehende Zusicherungen unverändert grün |
|
||
| AP5 | `tests/features/report-input-assembler.test.ts` | Kein Verdict wird neu abgeleitet; fehlende Fakten bleiben absent; `delta` bleibt ohne gespeicherten Vergleich `undefined`; Zusammenstellung nur beim Export |
|
||
| AP6 | Erweiterung `tests/exports/management-overview-report.test.ts` | Beide neuen Abschnitte vorhanden; `domain-only` trägt den Validierungshinweis; keine Registry-Keys, SIDs, Pfade oder Versionen im Output; Abdeckung steht vor Priority Findings |
|
||
| AP7 | `tests/exports/technical-report.test.ts` | Alle acht Abschnitte; score-neutrale Verdicts als solche gekennzeichnet; Diagnostik je Beobachtung genau einmal; Scope-Grenzen-Abschnitt vorhanden |
|
||
| AP8 | Verhaltenstest der Auslösung | Export läuft nur auf Klick, nicht beim Laden des Workspace |
|
||
|
||
Zusätzlich für Report A und B je ein Selbstgenügsamkeits-Test: der erzeugte
|
||
HTML-String enthält kein `<script`, kein `@import`, keine externe Font-URL und
|
||
keine `http`- oder `https`-Ressourcenreferenz in `src` oder `href`.
|
||
|
||
## 5. Verifikation
|
||
|
||
```bash
|
||
npm test
|
||
npm run build
|
||
```
|
||
|
||
Beide müssen grün sein. Zusätzlich ist zu belegen, dass die bestehenden Tests zu
|
||
`report-projection` und `management-overview-report` ohne inhaltliche Änderung
|
||
weiter bestehen.
|
||
|
||
## 6. Nicht Teil dieses Auftrags
|
||
|
||
- **Artefakt C (Arbeitsgrundlage)** und **Artefakt D (Delta Report)**. D ist
|
||
mangels gespeicherter Vergleiche ohnehin nicht lieferbar.
|
||
- **Framework-Abdeckung aus Maester-Tags (CISA SCuBA, CIS M365, Lizenzstufen).**
|
||
Grund: Die Tag-Liste ist heute als **eine** opake Category-Zeichenkette
|
||
gespeichert, teils doppelt in zwei Serialisierungen. Eine belastbare Tag-Sicht
|
||
erfordert eine Profil- und Importer-Änderung mit Reimport-Pflicht nach
|
||
`docs/REIMPORT_POLICY.md` und ist als eigener Auftrag zu beschließen. Bis dahin
|
||
wird die Zeichenkette **nicht** geraten, gesplittet oder interpretiert.
|
||
- **Asset- und Angriffspfad-Korrelation.** `assessor_canonical_assets` und
|
||
`assessor_asset_edges` sind leer, `affectedAsset` ist nur bei einer Quelle und
|
||
dort nahezu konstant belegt. Ein Pfad-Graph wäre eine Erfindung.
|
||
- Änderungen an Data Hub, Work, Pathfinder oder Client Info, die über die
|
||
Auslösung aus AP8 hinausgehen.
|
||
|
||
## 7. Abschluss
|
||
|
||
Nach Abschluss aller Pakete ist `docs/REPORT_STRATEGY.md` im selben Change um
|
||
einen Change-Log-Eintrag zu ergänzen, der festhält: Report A auf Renderer-Version
|
||
`1.1.0` erweitert, Report B in Version `1.0.0` implementiert, Projektion auf
|
||
`1.1.0`, Korrelationsmodul in `1.0.0` eingeführt, Reimport nicht erforderlich.
|
||
Kein Meilenstein wird als erfüllt markiert, bevor seine Akzeptanzkriterien
|
||
verifiziert sind.
|