767 lines
45 KiB
Markdown
767 lines
45 KiB
Markdown
# Assessment output strategy
|
||
|
||
## Export layout decision 2026-10-02
|
||
|
||
User approval 2026-10-02: this implemented report is the binding template for future Management Reports. Its permanent contract lives inside Report Engine at [MANAGEMENT_REPORT_TEMPLATE.md](../src/reporting/MANAGEMENT_REPORT_TEMPLATE.md). Preserve the approved renderer structure and data boundaries unless explicitly revised by the user; historical HTML exports are not overwritten.
|
||
|
||
The user-supplied Huntsman report page is the layout/style reference for the Management Report only: white report sheets, ASSESSOR branding, orange headings, dark table headers, two-column category explanations/evidence, factual bar charts and repeated footers. This explicit export-only decision supersedes previous app-style layout guidance for the Management Report; application views remain unchanged. The report embeds the existing ASSESSOR logo and local Inter with no remote dependency. The opening overview and complete Context heatmap lead into one logical report sheet per populated category, followed by scope and method. Logical sheet numbering does not promise one physical PDF page per sheet. Missing time-series/score/endpoint data is never fabricated to imitate the reference. Original technical evidence and recommendations remain in the separate Technical Report.
|
||
|
||
## Management Report with published Context results (2026-10-02)
|
||
|
||
Explicit user decision supersedes the earlier Core-only Report A content below: the Management Report requires Context results for the same client, assessments, scan runs and committed repository revision. It is generated offline with fixed German text, no LLM and no Internet access. A missing Context producer or mismatched scope blocks the management run; a changing revision retries the read and ultimately fails rather than combining snapshots. Client Info refuses management generation while Context Engine is off and suppresses late output after it is switched off. Technical generation remains available independently and does not compute Context when invoked alone; its archive contains no management HTML rather than a Core-only fallback. Historical frozen projections retain the historical renderer and are not rewritten.
|
||
|
||
Context Engine publishes a versioned result object through `context-report-publication.ts`; Report Engine consumes only its declared `ReportContextPublication` contract. The Core backend composes the two at its existing pinned extension point using the canonical read-only port. Neither engine imports the other. The publication preserves catalog/vocabulary/engine/severity versions, deduplicated finding count, per-cluster scanner/GPO counts, assignment confidence, source provenance and technical Result IDs. It uses the existing Context membership and heatmap semantics, without classifying or scoring again.
|
||
|
||
The management file contains: Management Summary; all Context categories in the severity-sorted heatmap with the unique overall Finding count; populated problem fields with general catalog descriptions, evidence counts, confidence and scanner sources; fixed review guidance pointing to original recommendations in Report B; documented risk acceptance and selected work-state counts; comparison results only when supplied by the existing shared projection; environment facts, source coverage and collection times; technical Security Domain counts in the appendix; method and source-version notes. General catalog descriptions are explicitly not evidence of a common attack path. Base Severity is category severity, never a new finding verdict, CVSS, priority, exploitation probability or business loss. Unknown/unexecuted checks are not passed; neutral heatmap cells are not secure; GPO backup configuration is not effective host state. Overlapping cluster counts are never added into a total. Work tracking and risk acceptance do not prove remediation; rescans remain required.
|
||
|
||
Management disclosure stays separate: no Result IDs, source Rule IDs, evidence paths, registry keys, technical finding fields or original scanner remediation payloads are hidden in Report A. Report B receives the frozen Context counts and the Result IDs proving them alongside its original detailed evidence and recommendations. Archive projection JSON retains the published results for reproducible replay. Static HTML includes the already bundled local Inter font as embedded data and has no script or external resources. The report export is not part of the client load path.
|
||
|
||
Current versions: Report Engine 0.3.0, shared projection 1.3.0, Management renderer 3.0.0; product capability release 2.13.0. No database schema, migration or Core result semantics change. A separate historical Delta Report remains its own artifact; the new management cut does not invent comparison evidence when no delta was supplied.
|
||
|
||
Decision: 2026-09-18, explicitly approved by the user. The 2026-09-23 user request
|
||
starts the A/B HTML deliverable through Client Info (Phase 7 UI integration,
|
||
cross-cutting report projection). Artifact C and D remain separate future work.
|
||
|
||
ASSESSOR produces **three** artifacts from one assessment:
|
||
|
||
| Artifact | Audience | Nature |
|
||
| --- | --- | --- |
|
||
| A — Management Overview (HTML) | customer decision makers | frozen snapshot |
|
||
| B — Technical Report (HTML) | engineers reproducing a finding | frozen snapshot, complete |
|
||
| C — Arbeitsgrundlage (XLSX + HTML) | the people doing the work | living document, curated |
|
||
| D — Delta Report (HTML, management + technical cut) | both, after a rescan | frozen snapshot of the change |
|
||
|
||
The dividing principle: **a report states the finding and its proof; the
|
||
Arbeitsgrundlage states the measure and who owns it.**
|
||
|
||
Remediation progress is proven by rescans, never by self-reported status. See
|
||
*Progress is proven by rescan* below.
|
||
|
||
## Approved delivery decisions
|
||
|
||
| Decision | Choice |
|
||
| --- | --- |
|
||
| Delivery | Separate files per artifact |
|
||
| Design | ASSESSOR application look |
|
||
| Technical scope (B) | Deviations, diagnostics and score-neutral classes |
|
||
| Interaction | Static, print- and PDF-ready, no JavaScript |
|
||
| Language | German, all artifacts |
|
||
| Arbeitsgrundlage format | XLSX to work in, HTML as the frozen status view |
|
||
| Arbeitsgrundlage scope | Curated selection, plus the complete list on a second sheet |
|
||
| Progress evidence | Rescan only; planning fields stay but are marked unverified |
|
||
| Delta delivery | Own artifact, management and technical cut |
|
||
|
||
Separate files keep the confidentiality boundary real: the Management file can be
|
||
forwarded without carrying registry keys, SIDs, evidence paths or parser
|
||
versions in its source. A single file with a hidden section would still contain
|
||
them.
|
||
|
||
## Report language
|
||
|
||
Both reports are written in German: headings, prose, labels, table headers,
|
||
legends and every explanatory sentence.
|
||
|
||
This does not change the Client Info dashboard, which remains English-only by its
|
||
own decision. The reports are separate customer-facing artifacts; the two rules
|
||
stand side by side and neither overrides the other.
|
||
|
||
German is the carrier language. **Established English technical terminology stays
|
||
English** and is not translated, not hyphenated into a German compound and not
|
||
replaced by an invented German equivalent. This is the general rule, not a list
|
||
of exceptions. It covers at least:
|
||
|
||
- the agreed GPO verdicts `Deviation`, `Passed`, `Not configured`,
|
||
`No baseline rule`, `Not comparable`, `Conflicts`, `Duplicates`
|
||
- the security domain names from `src/features/dashboard/security-domains.ts`
|
||
- assessment vocabulary such as Finding, Baseline, Score, Scope, Severity,
|
||
Pass Rate, Review, Owner, Due Date, Remediation, Evidence
|
||
- platform vocabulary such as Group Policy, Registry, Domain Controller,
|
||
Privilege, Attack Path, Hardening
|
||
|
||
Reason: these are the terms the readers already use, and most of them appear
|
||
verbatim in the application's Work filters and in the Client Info dashboard. A
|
||
reader must be able to match a line in the report to the view it came from, and a
|
||
translated second vocabulary would weaken the documented agreements.
|
||
|
||
Where a term is not self-explanatory to a management reader, it is introduced once
|
||
with a short German explanation and then used in its canonical English form.
|
||
German prose carries the sentence; the term inside it stays English.
|
||
|
||
Original source values, registry keys, value names, principals, evidence paths and
|
||
native identifiers are never translated or localized. Numbers and dates follow
|
||
German formatting.
|
||
|
||
The shared projection stays language-neutral and carries data, not display
|
||
strings. All German wording lives in the renderers.
|
||
|
||
## Shared report projection
|
||
|
||
Implemented in `src/reporting/report-projection.ts` (moved from `src/domain` on 2026-09-30)
|
||
(`REPORT_PROJECTION_VERSION = '1.1.0'`), covered by
|
||
`tests/domain/report-projection.test.ts`.
|
||
|
||
All artifacts read **one** projection built in the platform-neutral domain layer.
|
||
The projection is the single source of truth; the renderers only choose depth,
|
||
never content. Two consequences are binding:
|
||
|
||
- A number that appears in both reports is the same number. The Management
|
||
Overview never rounds, re-derives or re-filters a value that the Technical
|
||
Report states differently.
|
||
- Renderers contain no source-specific parsing, no scoring arithmetic and no
|
||
database access. They receive the finished projection.
|
||
|
||
The projection is built on export only. It is not part of the workspace load
|
||
path and must not derive presentation fields for records no report section
|
||
consumes.
|
||
|
||
## Report A — Management Overview
|
||
|
||
**Superseding user decision 2026-09-29 (Phase 7):** The Management Report is
|
||
generated offline and without an LLM from the current, selected client's
|
||
existing report projection. It is a factual overview, not a scanner finding
|
||
list, mixed-method score or speculative risk assessment. The earlier Report A
|
||
content contract below (overall-score donut, domain spine, priority findings,
|
||
remediation status and source overview) is retained as historical context but
|
||
does not govern the current Management export.
|
||
|
||
Current Report A shows project name, report date, the captured domain and DC
|
||
count when present, counts of assessed and deviating results, a heatmap of
|
||
deviations divided by assessed results for each catalog Security Domain, and a
|
||
second stacked chart of passed and deviating results with the Pass Rate and
|
||
underlying counts. A domain with no assessed results has no percentage or heat
|
||
level. The colors and percentages describe result frequency only: they do not
|
||
express severity, business impact, compromise, protection or progress.
|
||
|
||
The GPO domain chart counts assigned result rows. When the separate policy
|
||
evaluation exists, its failing comparisons and affected settings are stated
|
||
separately and never equated to result rows. The export names its statement
|
||
limits: source observations may come from different times; affected business
|
||
services, exploitation and current remediation state are not derivable from
|
||
these facts. It does not extrapolate backup age or add invented risk scenarios,
|
||
owners, measures or deadlines. Technical results remain in Report B. The
|
||
document is standalone HTML with inline styles and no external resources.
|
||
|
||
**User decision 2026-09-30 (Report Engine v0.2):** Directly below the three key
|
||
figures, Report A shows a `Kernaussagen` block with three deterministic statements.
|
||
Each is a fixed German sentence into which only projection values are inserted:
|
||
|
||
- **Gesamtbild:** assessed and deviating results with their share.
|
||
- **Schwerpunkt:** the domain or domains with the most deviations and with the
|
||
highest deviation share. Ties name every tied domain, and the sentence carries
|
||
the note that share and frequency say nothing about severity or business risk.
|
||
- **Aussagegrenze:** not run, not applicable, failed and otherwise unassessed
|
||
checks, which are neither passed nor deviating and enter no rate.
|
||
|
||
A statement without a factual basis is omitted. The statements are
|
||
language-neutral data in the projection (`statements`, built by
|
||
`src/reporting/report-narrative.ts`). The wording lives in
|
||
`src/reporting/report-statement-text.ts`, which both renderers share. The
|
||
Technical Report proves every statement with the same sentence and the result IDs
|
||
behind each number (`Belege der Kernaussagen`). Report A contains no IDs.
|
||
|
||
### Historical Report A design (superseded 2026-09-29)
|
||
|
||
Implemented in `src/reporting/management-overview-report.ts`
|
||
(`MANAGEMENT_OVERVIEW_RENDERER_VERSION = '1.0.0'`), covered by
|
||
`tests/exports/management-overview-report.test.ts`.
|
||
|
||
Answers: how do we stand, what is most urgent, what is this based on, what do we
|
||
not know. Its reading order follows the agreed Client Info grammar.
|
||
|
||
**Named deviation from the `assessor-ui` skill:** a delivered document uses no
|
||
application shell — no sidebar, topbar, command band or context strip — and none
|
||
of the four page archetypes, because it is read and printed outside the
|
||
application. Typography roles, donut geometry, tile and segment grammar, status
|
||
colour rules and the panel inset apply unchanged. Light is the document default
|
||
because a report is printed on white; dark renders from the same semantic roles
|
||
through `prefers-color-scheme`.
|
||
|
||
The bundled Inter is referenced by family name with a system fallback. No font,
|
||
style, script or image is loaded when the file is opened.
|
||
|
||
### 1. Assessment identity
|
||
Customer, environment (domain, forest, domain controller count from the
|
||
environment inventory), assessment period, report date, ASSESSOR product version.
|
||
|
||
### 2. Overall posture
|
||
The single score as a donut using the shared donut geometry, with its method
|
||
label. Binding wording from `SCORING_MODEL.md`:
|
||
|
||
- The GPO contribution is *conformance of configured, comparable in-scope
|
||
settings*. Binding German wording: „Konformität der konfigurierten,
|
||
vergleichbaren Einstellungen im Scope". It is never labelled baseline
|
||
compliance, baseline coverage or proof that recommended settings are
|
||
implemented — in German neither „Baseline-Compliance", „Baseline-Abdeckung"
|
||
nor any phrasing implying the baseline is fully rolled out.
|
||
- PingCastle enters as `100 − native risk`; the raw risk stays secondary context.
|
||
- A mixed-method index is named as such. Security Domain values are pass rates
|
||
and are not a decomposition of the index.
|
||
|
||
### 3. Security Domains
|
||
The eight domains from `src/features/dashboard/security-domains.ts` as a
|
||
connected pass-rate spine beside the overall score. Not a table.
|
||
|
||
### 4. Priority Findings
|
||
Grouped by security domain. **There is no global cross-tool ranking.** Inside a
|
||
domain, findings are ordered by the declared priority basis of their own source,
|
||
and that basis is named at the point of display.
|
||
|
||
Per entry: title, domain, the source's own rank value with its basis, one
|
||
sentence of impact, plus owner and status when a review exists.
|
||
|
||
GPO deviations form their own equal-weight block with their count, the affected
|
||
policy settings and the scope limit. They are never assigned an invented
|
||
criticality.
|
||
|
||
No registry keys, no SIDs, no file paths, no raw values.
|
||
|
||
See *Priority without a common severity* below for why.
|
||
|
||
### 5. Remediation status
|
||
Open, in progress, accepted, overdue, and findings without an owner — derived
|
||
from the stored reviews including accepted-risk data. If no reviews are
|
||
maintained, the section is omitted rather than rendered empty.
|
||
|
||
### 6. Coverage and gaps
|
||
What was assessed, what was not, unassessed controls, and every scope limit that
|
||
bounds a statement. A GPO import covering a subset of the domain is reported as
|
||
scope-limited and never presented as a domain-wide or effective-device claim.
|
||
This section is what makes the report defensible.
|
||
|
||
### 7. Source overview
|
||
Per tool: run or file date, score contribution, and the method that produced it.
|
||
|
||
### Excluded from Report A
|
||
Per-record tables, raw values, evidence paths, diagnostics, parser, analyzer and
|
||
manifest versions.
|
||
|
||
## Report B — Technical Report
|
||
|
||
### Current form (user decisions 2026-10-08, renderer 4.2.0, model 2.1.0, projection 1.5.0)
|
||
|
||
The preview corrections of 2026-10-07/08 supersede the section list further below. The
|
||
Technical Report is one static DIN A4 document (`@page` A4, 210 mm sheet, no forced page
|
||
breaks), white, without tiles or grey surfaces, in five chapters:
|
||
|
||
1. **Information**: the Client Info overview facts of the same snapshot, about half a page:
|
||
the six Data facts, Domain Information without the counts Data already shows, and the
|
||
six environment tiles (Resilience, Inventory, Password policy, Certificates & PKI,
|
||
Operating systems, Cloud Information) with their compact four-fact overviews. Built by
|
||
the same React-free functions Client Info renders
|
||
(`src/features/dashboard/client-info-facts.ts`); the GPO count follows the GPO backup
|
||
list (instances of the latest processed run per backup source).
|
||
2. **Lagebild**: one factual sentence; Overall Score and the deviations as the only two
|
||
donuts (ring sizes 2.5 : 2), the six Security Domains with their Client Info deviation
|
||
counts and amount bars in the Overall Score tone; problem-field key figures.
|
||
3. **Beziehungen (Pathfinder)**: one transition sentence (domains, problem fields, largest
|
||
domain and field) and the Security Domain to problem field diagram. No MITRE section.
|
||
4. **Problemfelder**: every published problem field in exact Context heatmap order, as an
|
||
ASSESSOR list row (severity stripe, disclosure arrow, name, German name, Base Severity,
|
||
bar, count, share; an open field brackets its stripe into its top and bottom separator).
|
||
An open field shows situation and technical lever side by side, its Security Domains and
|
||
every Work record behind it as a slim line (Work severity hexagon and title). A finding
|
||
line opens the head of its Finding Reader card: status, the reader's badge fields,
|
||
MITRE badges, the Context problem field, Source Rule ID, the first-read Problem rows
|
||
(Result reduced to its statement without appended lists or URLs), a collapsed
|
||
Description (flattened Markdown restored to headings, steps, lists and safe https
|
||
links) and the recognised ATT&CK/D3FEND references. `Ohne Problemkategorie` and the sum
|
||
close the list; the partition reconciles to the Client Info deviation total.
|
||
5. **Datenbasis und Datenstand**: collection period, imported reports, record counts, all
|
||
checks by Client Info result state, and the engine and catalog versions.
|
||
|
||
Wording states the actual state only; method explanations are not part of the report.
|
||
Scan tools are never named. Printing opens the problem fields but keeps finding cards
|
||
closed. Both reports read the same published Context: the Management Report requires it,
|
||
the Technical Report renders without problem fields when it is unavailable.
|
||
|
||
### Earlier rebuild (2026-10-07, superseded where the form above differs)
|
||
|
||
Explicit user decision 2026-10-07 supersedes the content inventory below (sections 1 to 6,
|
||
including the complete per-finding detail and the complete result index): the Technical
|
||
Report is rebuilt as a *report*, not a list of all findings. It goes technically deeper than
|
||
the Management Report, uses only categorisations and clusterings that ASSESSOR already has
|
||
(Context problem fields, Security Domains, native source areas and severities, GPO verdicts,
|
||
Pathfinder relations), avoids redundant information and uses graphics in the ASSESSOR
|
||
application design language. It is generated offline without Internet access and without
|
||
a language model. Renderer 4.0.0 (`src/reporting/technical-report.ts`) with the model
|
||
`technical-report-model.ts` 1.0.0 and the chart primitives `report-charts.ts`:
|
||
|
||
Follow-up user correction 2026-10-07: ASSESSOR is the only source. Scan tools are neither named
|
||
nor compared; the Pathfinder view connects Security Domains with problem fields; a problem field
|
||
shows only its situation, technical lever and Security Domains. Renderer 4.1.0, model 2.0.0:
|
||
|
||
1. Lagebild: Overall Score, deviations, passed and assessed as full donuts; key figures.
|
||
2. Problemfelder: every Client Info deviation exactly once in its primary Context
|
||
cluster (or `Ohne Problemkategorie`) with Base Severity; per problem field the
|
||
situation, the technical lever and the Security Domains it falls into.
|
||
3. Beziehungen (Pathfinder): Security Domain to problem field diagram; ATT&CK tactics and
|
||
techniques separated into explicitly assigned and only mentioned; assigned D3FEND
|
||
countermeasures.
|
||
4. Security Domains: pass/deviation per domain.
|
||
5. GPO-Baseline-Vergleich: all seven GPO verdicts with score-neutral/flag labels,
|
||
deviations per policy section, contributing and unlinked backups.
|
||
6. Abdeckung und Aussagegrenzen: all records by result state and reading notes.
|
||
7. Umgebung (to move into the report header, see the implementation plan).
|
||
8. Datenstand und Versionen.
|
||
|
||
All figures are aggregated from the same Work records, result states and Security Domain
|
||
classifier as Client Info (AGENTS.md, Report numbers). Result IDs and every original value
|
||
remain in the archived projection JSON and in ASSESSOR; the HTML does not list them. The
|
||
Management Report now points to ASSESSOR for individual evidence and original
|
||
recommendations. Without a Context result of the same state the problem-field section states
|
||
its absence and every other section remains complete.
|
||
|
||
The following historical inventory is retained for traceability only.
|
||
|
||
### Historical inventory (superseded 2026-10-07)
|
||
|
||
The initial static renderer is implemented in `src/reporting/technical-report.ts`.
|
||
It retains the current internal result projection, original Work fields when a
|
||
canonical occurrence matches, and the retained per-backup GPO instructions.
|
||
Missing source facts are labelled as unavailable; the renderer never invents
|
||
actual/expected values or an endpoint-effective state. A dedicated completeness
|
||
audit of diagnostics, reimport metadata and all source formats remains open and
|
||
must not be mistaken for a final acceptance of every section below.
|
||
|
||
Answers: what exactly is configured, what should it be, where does that stand,
|
||
how does ASSESSOR know, what do I do.
|
||
|
||
### 1. Scope and provenance
|
||
Imported artifacts, processing, analyzer and manifest versions, reimport status,
|
||
aggregation and evaluation run identifiers. Enough to reproduce the result.
|
||
|
||
### 2. Result inventory
|
||
Totals per status, severity, domain and tool, including the score-neutral
|
||
classes. Passed results appear here as a count.
|
||
|
||
### 3. Findings in detail
|
||
Grouped by security domain, ordered by severity inside each group. Per finding:
|
||
|
||
- technical identity and scope
|
||
- actual against expected value, with the field-specific deviation marking used
|
||
by the Finding Reader: `Operation`, `Data` and `Type code` for registry policy
|
||
differences, several fields only where each is independently proven
|
||
- principal sets compared per member: only configured principals absent from
|
||
every retained baseline variant are marked; a principal also present in the
|
||
expected baseline stays neutral
|
||
- GPO source attribution as vertically stacked sections per exact backup
|
||
identity, each with evidence reference, source-local sequence and every
|
||
original instruction immediately visible
|
||
- conflict and duplicate flags, kept separate from the policy verdict
|
||
- baseline variants where more than one exists
|
||
- the remediation step from the review
|
||
|
||
### 4. GPO policy register
|
||
All seven verdicts in full: `Deviation`, `Passed`, `Not configured`,
|
||
`No baseline rule`, `Not comparable`, `Conflicts`, `Duplicates`. The
|
||
score-neutral verdicts are labelled as score-neutral at the point of display, so
|
||
that a reader cannot mistake `Not configured` for a remediation obligation.
|
||
|
||
### 5. Diagnostics
|
||
Unresolved identities, parser gaps, scope limits, declared-against-counted
|
||
mismatches and incomplete backups. Each observation appears once with its
|
||
authoritative resolver reason and is not restated by snapshot aggregation.
|
||
|
||
### 6. Appendix
|
||
Complete result index.
|
||
|
||
## Artifact C — Arbeitsgrundlage
|
||
|
||
Decision: 2026-09-18, explicitly approved by the user.
|
||
|
||
Answers: what do we do, who owns it, by when, where does it stand.
|
||
|
||
The measured volume makes this its own artifact: the stored data holds **1,806
|
||
actual deviations** (CIS-CAT 969, Maester 350, PingCastle 277, Purple Knight 210)
|
||
plus the GPO deviations. Rendering each of those with full technical depth inside
|
||
Report B would produce a document nobody works with.
|
||
|
||
Size is not the decisive reason, though. A report is a frozen snapshot that
|
||
proves what was found on a date and is archived unchanged. An Arbeitsgrundlage is
|
||
a living document that the customer edits for weeks: owner, due date, status,
|
||
comment. Forcing both into one file makes both worse.
|
||
|
||
### Data model
|
||
|
||
The Arbeitsgrundlage is the stored review model in `src/domain/review.ts`:
|
||
`status`, `priority`, `owner`, `due`, `prerequisites`, `access`, `nextStep`,
|
||
`evidence`, `comment`, the markers `quickwin`, `blocked`, `external`, `question`
|
||
and `notRelevant`, plus `riskAcceptance`.
|
||
|
||
`include` is the curation switch. The consultant selects what reaches the
|
||
customer as a work item. Shipping 1,806 unfiltered items produces no action at
|
||
all.
|
||
|
||
### Structure
|
||
|
||
- **Sheet 1 — Maßnahmen.** Only the curated selection, with a stated selection
|
||
criterion. One row per work item.
|
||
- **Sheet 2 — Vollständige Abweichungen.** All deviations for reference, so the
|
||
customer can pull further items in without the working list overflowing.
|
||
|
||
The HTML variant renders the same content as a frozen status view for status
|
||
meetings and filing. It is generated from the same projection, never maintained
|
||
separately.
|
||
|
||
### No duplicated technical depth
|
||
|
||
A work item carries its stable finding identifier, the measure, prerequisites,
|
||
required access and the owner — not the evidence, not the source attribution, not
|
||
the baseline variants. Those stay in Report B and are reached through the
|
||
identifier.
|
||
|
||
One fact, one place. Repeating the technical detail would leave two diverging
|
||
versions after the first correction.
|
||
|
||
## Progress is proven by rescan
|
||
|
||
Decision: 2026-09-18, explicitly approved by the user: *"Den Fortschritt
|
||
dokumentiere ich durch Rescans — für mich zählt nur der technische Beweis."*
|
||
|
||
Remediation progress is established by comparing scan runs, never by a status
|
||
somebody ticked. This follows `TIMESLOT_COMPARISON_PLAN.md`, which already
|
||
replaced the conclusion *absent equals remediated*.
|
||
|
||
### Binding rules
|
||
|
||
- No artifact presents a self-reported status as a resolved finding. A work item
|
||
marked done in the Arbeitsgrundlage stays an open deviation in every report
|
||
until a rescan proves otherwise.
|
||
- A delta uses the lifecycle vocabulary of the timeslot contract — demonstrably
|
||
remediated, still open, unresolved, not comparable — never a binary
|
||
done/open. *Unresolved* is a legitimate and frequent outcome and is shown as
|
||
such.
|
||
- Absence of a finding is not proof of remediation. Remediation is claimed only
|
||
where the profile-bound negative-proof contract is satisfied for exactly that
|
||
rule and object.
|
||
- Result presence and deviation lifecycle stay separate. A rule can still be
|
||
reported while its concrete deviation is fixed.
|
||
- No effective-endpoint claim is derived from a backup-configuration comparison.
|
||
|
||
### Why the caveat is not theoretical
|
||
|
||
The stored data shows how easily absence would be misread: Maester carries **845
|
||
`not_run`** and 10 `execution_error`, Purple Knight 34 `not_applicable` and 10
|
||
`not_run`. A check that did not run would otherwise turn into a success message —
|
||
the most dangerous false statement a progress report can make, because it errs in
|
||
the comfortable direction.
|
||
|
||
### Artifact D — Delta Report
|
||
|
||
An own artifact per rescan, in the same two cuts as the reports. The initial
|
||
report stays frozen and is never rewritten; the change is a separate document
|
||
with its own reporting date.
|
||
|
||
**The comparison is against a chosen reference run, and that choice is made in
|
||
ASSESSOR — not at export time.** The export renders a comparison that already
|
||
exists; it contains no run-selection logic, no fallback to "the previous run" and
|
||
no implicit pairing of its own. Consequences:
|
||
|
||
- The chosen reference run is part of the stored comparison, together with its
|
||
contract version, engine versions and input fingerprint. The export reads it
|
||
and never re-derives it.
|
||
- The document names both compared runs explicitly — reference and current, each
|
||
with its identity and date — so it stays self-describing once it is out of the
|
||
application.
|
||
- Without a stored comparison there is no Delta Report. The export is
|
||
unavailable rather than guessing a pairing.
|
||
|
||
This keeps comparison a domain concern and presentation a separate one: the Delta
|
||
Report is a renderer, not a second comparison engine.
|
||
|
||
- **Management cut:** score movement, what is demonstrably remediated, what
|
||
persists, what is newly found, and how much of the comparison is unresolved or
|
||
not comparable. The unresolved share is stated, not hidden.
|
||
- **Technical cut:** per finding the lifecycle verdict with its evidence — which
|
||
run, which identity match, which contract qualified it — plus the
|
||
disqualifying diagnostics where a comparison was not admissible.
|
||
|
||
### Planning fields in the Arbeitsgrundlage
|
||
|
||
`status`, `due`, `owner` and the markers stay in the XLSX as the customer's own
|
||
steering aid. They are labelled as unverified self-report at the point of display.
|
||
ASSESSOR never promotes them into a report as a result.
|
||
|
||
The export therefore stays one-way by design, not by limitation: progress does not
|
||
need a return channel because it comes from the next scan.
|
||
|
||
## Priority without a common severity
|
||
|
||
Decision: 2026-09-18, explicitly approved by the user.
|
||
|
||
Severity is not a property every scanner delivers. Measured against the stored
|
||
data:
|
||
|
||
| Tool | Results | Severity coverage | Native ranking signal |
|
||
| --- | --- | --- | --- |
|
||
| CIS-CAT | 1,233 | none, all `unknown` | none |
|
||
| PingCastle | 277 | derived from Points | Points 0–50+ |
|
||
| Maester | 1,590 | 99.4 % | native severity |
|
||
| Purple Knight | 1,309 | 100 %, 187 `critical` | native severity |
|
||
| GPO / baseline | — | none | none |
|
||
|
||
A severity-ranked global list would therefore drop CIS-CAT and GPO out of the
|
||
Priority Findings entirely, let Purple Knight dominate merely because it is the
|
||
only source that emits `critical` natively, and keep PingCastle permanently below
|
||
`critical` because `pingCastleSeverityFromPoints` caps at `high` by construction.
|
||
That ordering would describe the report formats, not the customer's risk.
|
||
|
||
### Binding rules
|
||
|
||
- Priority Findings are grouped by security domain. The eight domains are
|
||
tool-neutral and already exist.
|
||
- Within a domain, findings are ordered by the declared priority basis of their
|
||
own source. The basis is visible to the reader.
|
||
- No ranking is ever produced across sources with incomparable bases. A single
|
||
ordered list that mixes them is not permitted.
|
||
- GPO deviations are equal-weight. They get their own block with count, affected
|
||
policy settings and scope limit, and never an inferred criticality.
|
||
- A source without any defensible ranking basis lists its findings unranked and
|
||
says so. Unranked is an acceptable answer; an invented rank is not.
|
||
|
||
### Profile obligation
|
||
|
||
Implemented as `ToolPriorityBasisDefinition` in
|
||
`src/domain/tool-profile-definition.ts`, required on every `ToolProfileDefinition`
|
||
and enforced by `tests/profiles/declarative-profiles.test.ts`.
|
||
|
||
Analogous to the existing `securityScoring` obligation, every tool profile
|
||
declares a **priority basis**: the exact mapped properties, scale, direction and
|
||
semantics of the value used to order its findings — or an explicit reason why no
|
||
usable basis exists. Never infer a priority from an arbitrary field named
|
||
severity, level, score or rank.
|
||
|
||
A declaration may also list `excludedProperties`: fields that resemble a basis
|
||
but are not one, each with its reason. This is what stops a later change from
|
||
quietly promoting them.
|
||
|
||
| Profile | Declared basis |
|
||
| --- | --- |
|
||
| Purple Knight | `severity` — native criticality, the only source reaching `critical` without derivation |
|
||
| Maester | `severity` — native per test |
|
||
| PingCastle | `numeric` on Points, `0-50+`, higher-is-worse |
|
||
| CIS-CAT | `unavailable` — benchmarks state no per-recommendation criticality |
|
||
| Generic | `unavailable` — unrecognized fields establish no scale or direction |
|
||
|
||
PingCastle is declared on Points rather than severity on purpose: its canonical
|
||
severity is *derived* from Points by `pingcastle-severity-from-points` and is
|
||
capped at `high` by construction. Points keeps the granularity the capped
|
||
severity loses.
|
||
|
||
`reportPriorityBasisFrom` turns an `unavailable` declaration into an explicit
|
||
`equal-weight` basis carrying the profile's own reason, so a report states why a
|
||
source is unranked instead of implying the declaration is missing.
|
||
|
||
A derived, normalized cross-tool ASSESSOR priority is **not** approved. It would
|
||
require its own versioned rule and an explicit decision of its own.
|
||
|
||
### Guarded mis-mapping risk
|
||
|
||
`src/profiles/cis-cat/cis-cat-definition.ts` declares
|
||
`severityHeaders: /^(?:severity|level)$/i`. CIS `Level 1` / `Level 2` is a
|
||
hardening profile tier, not a criticality. It falls through to `unknown`, which
|
||
is harmless, and it is now recorded as an excluded property with its reason and
|
||
covered by a test. It must not be "corrected" into a severity, because that would
|
||
manufacture criticalities the benchmark never stated.
|
||
|
||
## Report Engine boundary
|
||
|
||
Decision: 2026-09-30, explicitly approved by the user. Implementation plan: Phase 7,
|
||
*Report Engine als eigenstaendiger Nur-Lese-Baustein*.
|
||
|
||
The Report Engine is a self-contained, read-only building block inside ASSESSOR.
|
||
It produces the reports offline, without an LLM, and never changes ASSESSOR
|
||
logic or data.
|
||
|
||
- **Location.** The projection, correlation, input assembler,
|
||
`client-report-projection` and both renderers were moved unchanged to
|
||
`src/reporting/` on 2026-09-30. The shared security-domain catalog, scoring and review
|
||
hydration stay ASSESSOR components and are only imported, so that a report
|
||
number always equals the Client Info and Work number. An architecture test
|
||
enforces the boundary.
|
||
- **Read-only access.** The engine reads through a read-only repository port and
|
||
a dedicated read-only database pool. It detects a repository revision change
|
||
during assembly and repeats the read instead of mixing states.
|
||
- **Server rendering.** Since v0.1 the server renders both files once per run and
|
||
archives them. Client Info downloads exactly the archived file through
|
||
`POST /api/v1/projects/:id/report-runs`.
|
||
- **Frozen state.** Every report run is archived server-side in its own folder
|
||
and never modified afterwards. The folder holds `projection.json`, both HTML
|
||
files and a manifest with file hashes, engine versions, repository revision,
|
||
scope and time. The archive lives outside the repository and outside synced
|
||
folders (`ASSESSOR_REPORT_ARCHIVE_DIR`) and is deleted together with its
|
||
client. `projection.json` and the manifest are internal. They are never
|
||
delivered with Report A, which keeps the separate-files confidentiality
|
||
boundary.
|
||
- **Boundaries.**
|
||
- Only the shared security-domain catalog.
|
||
- Severity only per source, never summed across scanners.
|
||
- Review state only as unverified self-report, and never in Report A.
|
||
- Scenarios and cross-finding relationships only from the context engine
|
||
(Phase 13E), never from report-local rules.
|
||
|
||
## Guardrails for both reports
|
||
|
||
- Score-neutral classes never appear in Priority Findings and never enter the
|
||
score. They remain fully inspectable in Report B.
|
||
- A GPO verdict is taken only from the authoritative rolled-up policy verdict and
|
||
the explicit conflict and duplicate flags. Never from comparison prose,
|
||
historical results, baseline text, guidance or evidence.
|
||
- Unknown or unavailable facts render as an em dash, never as an inferred zero.
|
||
- Both files are self-contained: inline styles, no CDN, no external fonts,
|
||
scripts or images, no network access when opened. A report leaves the building
|
||
and must not load anything.
|
||
- The bundled Inter variable font is embedded or a local system stack is used;
|
||
no font is downloaded at view time.
|
||
- Shared typography roles, donut geometry, marking semantics and panel inset
|
||
apply as in the application. Status colour stays in accent lines, markers,
|
||
arcs and score nodes, never as a full status fill.
|
||
- Static output: no JavaScript, printable to PDF without layout loss.
|
||
|
||
## Relationship to the existing export
|
||
|
||
### Current-view export — explicit user extension, 2026-09-18
|
||
|
||
The Work toolbar's CSV/MD/HTML buttons now export the current filtered and sorted
|
||
view, including Results, with exactly two content columns: `Soll-Zustand` and
|
||
`Abweichungen`. This supersedes their old marked-only selection. The user can
|
||
still restrict the view with `My Workspace`; that restriction combines with the
|
||
other active filters. A view export is a frozen, scoped inspection snapshot,
|
||
not the curated Arbeitsgrundlage C, a replacement for reports A/B, or a delta.
|
||
|
||
`src/exports/work-view-export.ts` supplies one click-time presentation projection
|
||
for these three formats, consuming existing Work records and the shared Results
|
||
resolver without ranking, scoring, re-evaluation or database queries. The shared
|
||
`report-projection.ts` contract for artifacts A–D remains unchanged. Native
|
||
remediation, baseline variants, exact source attribution and verification gaps
|
||
stay visible. Review completion never becomes a technical resolution. Long CSV
|
||
cells are preserved in labelled continuation rows (same finding ID), keeping two
|
||
columns and avoiding Excel's cell-size truncation. HTML is self-contained,
|
||
script-free, with locally bundled Inter embedded and responsive wrapping.
|
||
|
||
The following paragraph records the earlier strategy baseline; its description
|
||
of the toolbar's marked-only behaviour has been superseded by this extension.
|
||
|
||
`src/exports/dashboard-export.ts` and `ActionlistControls` already export the
|
||
review model as CSV, Markdown and a minimal HTML page. That HTML knows nothing of
|
||
the score, the security domains, GPO verdicts or provenance and uses Segoe UI
|
||
with fixed colours.
|
||
|
||
The existing action-list export is the seed of artifact C, not of the reports.
|
||
The reports are new; the action-list HTML is replaced by the artifact C renderer,
|
||
and the CSV path stays useful for ticket-system import.
|
||
|
||
## Open decisions
|
||
|
||
- How many findings each domain block shows before it is truncated, and how a tie
|
||
inside one source's ranking basis is broken.
|
||
- Whether the Microsoft baseline packages carry any per-setting classification
|
||
that could later give GPO deviations a defensible basis. Until proven, GPO
|
||
stays equal-weight.
|
||
- The selection criterion wording for the curated sheet, and whether it is fixed
|
||
or written per engagement.
|
||
- Which profiles can satisfy a negative-proof contract at all. Profiles that
|
||
cannot will never produce a *demonstrably remediated* verdict, and the Delta
|
||
Report must say so per source rather than leave the reader guessing.
|
||
- File naming convention across all artifacts, including how a delta names its
|
||
two compared runs.
|
||
|
||
## Dependency
|
||
|
||
Artifact D depends on the timeslot comparison actually producing stored results.
|
||
`assessor_timeslot_evidence_comparisons` exists with its contract version, engine
|
||
versions and input fingerprint, but currently holds no rows. Until the comparison
|
||
is implemented per `TIMESLOT_COMPARISON_PLAN.md`, artifacts A, B and C are
|
||
deliverable and D is not.
|
||
|
||
## Change log
|
||
|
||
- 2026-09-18: Strategy established. Delivery, design, technical scope and
|
||
interaction decided by the user; content inventory per report fixed.
|
||
- 2026-09-18: Report language decided by the user — German for both reports. The
|
||
agreed GPO verdict vocabulary and the security domain names stay canonical
|
||
English inside the German text; source values are never localized. The Client
|
||
Info English-only rule is unaffected.
|
||
- 2026-09-18: User correction — established English technical terminology stays
|
||
English as a general rule, not as an enumerated exception. German carries the
|
||
prose; technical terms are never translated or turned into German compounds.
|
||
- 2026-09-18: Priority model decided by the user after the user raised that not
|
||
every tool provides severity. Verified against stored data: CIS-CAT and GPO
|
||
have no severity at all, PingCastle cannot reach `critical` by construction.
|
||
Priority Findings are grouped by security domain with a per-source, declared
|
||
and visible ranking basis; no cross-tool ranking; GPO equal-weight in its own
|
||
block. Profiles must declare a priority basis. A normalized cross-tool
|
||
priority is explicitly not approved.
|
||
- 2026-09-18: Third artifact added on the user's proposal. The per-finding
|
||
working basis becomes its own deliverable — XLSX to work in plus an HTML status
|
||
view, curated through `include` with the complete deviation list on a second
|
||
sheet. Decisive reason is the differing lifecycle, not only the measured volume
|
||
of 1,806 deviations. Work items reference findings by identifier and never
|
||
repeat the technical depth of Report B.
|
||
- 2026-09-18: Progress model fixed by the user — progress is documented by
|
||
rescans, only technical proof counts. A fourth artifact is added: the Delta
|
||
Report, in a management and a technical cut, using the timeslot lifecycle
|
||
vocabulary. Self-reported status never becomes a reported result; planning
|
||
fields remain in the Arbeitsgrundlage marked as unverified. The earlier open
|
||
question about re-importing a maintained worklist is closed: the export is
|
||
one-way by design.
|
||
- 2026-09-18: Reference run decided by the user — a delta compares against a
|
||
chosen reference run, and the choice is made in ASSESSOR. The export renders a
|
||
stored comparison and carries no run-selection logic, no previous-run fallback
|
||
and no implicit pairing. Both runs are named in the document.
|
||
- 2026-09-18: Shared projection implemented as `src/domain/report-projection.ts`.
|
||
The security-domain assignment is injected by the caller so the domain layer
|
||
never points at the feature layer. Profiles do not yet declare a priority
|
||
basis, so the projection accepts one per source and falls back to `undeclared`
|
||
with its own diagnostic rather than guessing. Declaring the basis inside
|
||
`ToolProfileDefinition` is the follow-up step.
|
||
- 2026-09-18: Profile obligation implemented. `ToolPriorityBasisDefinition` is
|
||
required on every registered profile and enforced by a contract test. Purple
|
||
Knight and Maester declare native severity, PingCastle declares Points because
|
||
its severity is derived and capped, CIS-CAT and Generic declare unavailable
|
||
with a reason. CIS `Level` is recorded as an excluded property so it cannot be
|
||
promoted into a criticality later.
|
||
- 2026-09-18: Management Overview renderer implemented as
|
||
`src/exports/management-overview-report.ts`. Static, self-contained German
|
||
HTML with no script, inline handler, external URL or `url()`. Deviation from
|
||
the `assessor-ui` shell and page archetypes named above; all token, typography,
|
||
donut and status rules kept. An out-of-range score is clamped and a missing one
|
||
draws no arc at all.
|
||
- 2026-09-30: Report Engine boundary decided by the user. The report modules move
|
||
unchanged to `src/reporting/` and access is read-only. Every run is archived
|
||
immutably server-side, outside the repository and synced folders. Domain,
|
||
severity, review-state and scenario boundaries are confirmed, with scenarios
|
||
only from the Phase 13E context engine. Documentation only; no
|
||
implementation yet.
|
||
- 2026-09-30: Report modules moved unchanged to `src/reporting/`, with their
|
||
tests in `tests/reporting/`. `tests/reporting/reporting-boundary.test.ts`
|
||
enforces the importer allow-list and the absence of writing repository calls.
|
||
Output and numbers are unchanged.
|
||
- 2026-09-30: Report Engine v0.1 (product 2.10.0). A read-only port, a PostgreSQL
|
||
reader with read-only sessions and a revision check before and after the read
|
||
(repeat, otherwise refuse) ensure a consistent read. Every run is archived
|
||
write-once on the server. The Client Info buttons now download the
|
||
server-rendered, archived file, and the browser no longer renders reports.
|
||
Report content is unchanged.
|
||
- 2026-09-30: Report Engine v0.2. The user chose the statements Gesamtbild,
|
||
Schwerpunkt and Aussagegrenze, placed below the key figures, with evidence in the
|
||
Technical Report and the archive. Projection 1.2.0 adds ID lists per domain and
|
||
per coverage group plus the statements. The narrative module is 1.0.0, the
|
||
Management renderer 2.1.0 and the Technical renderer 1.1.0, whose result index
|
||
now shows the result ID.
|
||
- 2026-10-07: Technical Report rebuilt on explicit user request as a report instead of
|
||
a finding list (renderer 4.0.0, model 1.0.0, projection 1.4.0 with a lean Work cut 2.0.0
|
||
carrying native area, rule ID and Pathfinder ATT&CK/D3FEND relations). The complete
|
||
result index and per-finding detail are dropped from the HTML; Result IDs stay in the
|
||
archive projection. Management renderer 3.3.1 adjusts its pointer to the Technical Report.
|
||
- 2026-10-07: User correction to the rebuilt Technical Report: single source ASSESSOR, no
|
||
scan-tool names or per-tool comparison (score composition, source tiles, source chips,
|
||
multi-source corroboration, lead checks, imported-report and diagnostic tables removed),
|
||
Security Domain to problem field diagram, lean problem-field cards. Renderer 4.1.0, model 2.0.0.
|
||
- 2026-10-08: Technical Report finalised from the user-reviewed Dummy preview and moved into
|
||
production (renderer 4.2.0, model 2.1.0, projection 1.5.0, Work cut 2.1.0 with the Finding
|
||
Reader card head per deviation): DIN A4, chapter order Information, Lagebild, Beziehungen,
|
||
Problemfelder, Datenbasis; heatmap order; MITRE section removed; problem-field rows with
|
||
finding lines and cards; Client Info facts in Information; the technical run now reads the
|
||
published Context. ASSESSOR 2.18.0.
|