import type { CanonicalFieldRole, CanonicalFindingStatus, CanonicalSeverity } from './canonical-model'; export type ProfileSignalScope = 'file-name' | 'raw-text' | 'table-name' | 'header' | 'cell-value'; export interface WeightedPattern { pattern: RegExp; contribution: number; message: string; } export interface ProfileSourceSignal extends WeightedPattern { scope: ProfileSignalScope; } export interface ProfileFieldRule { role: CanonicalFieldRole; headers: WeightedPattern[]; /** Conditions apply only within the current source table/candidate shape. */ whenHeadersPresent?: RegExp[]; whenHeadersAbsent?: RegExp[]; } export interface ProfileValueMapping { normalizedValue: T; patterns: RegExp[]; } export interface ProfileStatusFilter { value: string; label: string; patterns: RegExp[]; } export interface ProfileStatusDisplayRule { label: string; patterns: RegExp[]; } export type ProcessingChangeClass = 'presentation-only' | 'compatible' | 'semantic-breaking'; /** * Versionierter Verarbeitungsvertrag eines Werkzeugprofils. * * Die Profilversion beschreibt das gesamte Paket. Mapping, Normalisierung und * Darstellung entwickeln sich dagegen unabhaengig. Der compatibilityKey ist * die fail-closed-Grenze fuer Aggregation und Vergleich kanonischer Ergebnisse. */ export interface ToolProcessingContract { mappingVersion: string; normalizationVersion: string; presentationVersion: string; compatibilityKey: string; changeClass: ProcessingChangeClass; reimportRequired: boolean; affectedDimensions: string[]; } /** Every scanner must explicitly assess native overall-score support before fallback. */ export interface ToolSecurityScoringDefinition { version: string; native: { kind: 'mapped'; /** Exact mapped report properties, never fuzzy matches against finding fields. */ properties: [string, ...string[]]; scope: 'overall'; scale: '0-100'; direction: 'higher-is-better' | 'lower-is-better'; /** Other native scopes that make this single contribution ambiguous. */ incompatibleProperties?: string[]; } | { kind: 'unavailable'; /** Why the supported report formats cannot currently supply a usable native score. */ reason: string; }; targets: string[]; benchmark?: string[]; resultPopulation: 'complete' | 'findings-only' | 'unknown'; } /** * Every scanner must state how its findings may be ordered against each other. * Severity is not universal: CIS-CAT delivers none at all and PingCastle carries * only Points. A basis is declared with its exact mapped properties, or declared * unavailable with a reason. Never infer one from an arbitrary field named * severity, level, score or rank, and never order across sources with different * bases — see docs/REPORT_STRATEGY.md. */ export interface ToolPriorityBasisDefinition { version: string; basis: { kind: 'severity'; /** Exact mapped properties carrying the native criticality. */ properties: [string, ...string[]]; } | { kind: 'numeric'; /** Exact mapped properties behind the numeric rank value. */ properties: [string, ...string[]]; scale: string; direction: 'higher-is-worse' | 'lower-is-worse'; } | { kind: 'unavailable'; /** Why the supported report formats cannot supply a defensible ordering. */ reason: string; }; /** * Fields that resemble a basis but are not one. Recorded so a later change * cannot quietly promote them, which would manufacture a criticality the * source never stated. */ excludedProperties?: Array<{ property: string; reason: string }>; } /** * Versioned contract for remediation comparisons. This is deliberately * separate from score population: a complete score denominator does not prove * that an omitted rule or object was re-tested successfully. */ interface AvailableToolTimeslotComparisonBase { version: string; kind: 'available'; scope: { kind: string; properties: [string, ...string[]]; }; observationTimeProperties: [string, ...string[]]; resultEvidence: 'explicit-results' | 'findings-only' | 'unknown'; /** `unsupported` keeps missing target occurrences unresolved. */ negativeEvidence: 'unsupported' | 'complete-explicit-negative'; diagnosticPolicy: 'fail-closed-by-impact'; } export type ToolTimeslotComparisonDefinition = (AvailableToolTimeslotComparisonBase & { granularity: 'scope' | 'aggregate'; }) | (AvailableToolTimeslotComparisonBase & { granularity: 'asset'; object: { kind: string; namespace: string; source: 'affected-asset' }; }) | { version: string; kind: 'unavailable'; reason: string; }; export interface ToolProfileDefinition { schemaVersion: 1; id: string; name: string; /** Further scan-run names the import assigns to this profile, for example scope-specific names. */ runNames?: string[]; version: string; processing: ToolProcessingContract; securityScoring: ToolSecurityScoringDefinition; priorityBasis: ToolPriorityBasisDefinition; timeslotComparison: ToolTimeslotComparisonDefinition; fallback?: boolean; sourceSignals: ProfileSourceSignal[]; tableRules: { preferredNames: WeightedPattern[]; discouragedNames: WeightedPattern[]; }; fieldRules: ProfileFieldRule[]; normalization: { severity: Array>; status: Array>; }; presentation: { titleHeaders: RegExp[]; severityHeaders: RegExp[]; sortHeaders: RegExp[]; sortDirection: 'asc' | 'desc'; defaultStatusFilter: string; statusFilters: ProfileStatusFilter[]; /** Geordnete Header-Prioritaet fuer den sichtbaren Ergebnisstatus. */ statusHeaders?: RegExp[]; /** Optionale, rein visuelle Beschriftung roher Werkzeugstatus. */ statusDisplayRules?: ProfileStatusDisplayRule[]; /** * Header, deren Feldwert ein beschreibender Ergebnistext ist statt eines * kurzen Status-Badges. Manche Werkzeuge (z. B. Maester) nennen diese * Spalte schlicht "Result", waehrend andere (z. B. Purple Knight) unter * demselben Namen nur ein kurzes Statuswort fuehren und den Text separat * in "Result Message" ablegen. Ohne diese Angabe bleibt der Reader beim * Purple-Knight-Verhalten und blendet ein Feld namens "Result" aus. */ resultMessageHeaders?: RegExp[]; /** * Header, deren Feldwert als eigener benannter Abschnitt in das * Results-Fenster gehoert - der Feldname wird zum Titel des Abschnitts. * * Gedacht fuer Inhalte, die der Bericht selbst nur aufgeklappt zeigt und * die deshalb zu den Belegdaten zaehlen, nicht zur Lesespalte: CIS-CAT * versteckt hinter "Show Rule Result XML" und "More" das Rule Result XML * und die Rahmenwerk-Zuordnungen. Anders als `resultMessageHeaders` (ein * einzelner Ergebnistext anstelle einer Ergebnisdatei) duerfen es mehrere * Abschnitte sein; sie erscheinen in Feldreihenfolge. Der Reader blendet * diese Felder aus, damit dieselbe Aussage nicht doppelt steht. */ resultSectionHeaders?: RegExp[]; /** * Verknuepfte Belegtabellen benoetigen einen fachlichen Abschnittstitel, * wenn ihr Tabellenname Bedeutung traegt und nicht nur die Quelle wiederholt. */ showResultSheetTitles?: boolean; /** Deklarative Umbenennung fachlicher Belegtabellentitel. */ resultSheetTitleRules?: Array<{ pattern: RegExp; replacement: string }>; /** * Ergebnisabschnitte, deren Markdown-Listenpunkte als Titel der jeweils * folgenden Belegtabelle desselben Markdown-Abschnitts dienen. */ resultSectionListSheetTitles?: RegExp[]; /** Zeileninhalte verknuepfter Belegtabellen, die visuell hervorgehoben werden. */ resultSheetHighlightedRowPatterns?: RegExp[]; /** Zeileninhalte, welche die erwartete Sollbedingung einer Regel beschreiben. */ resultSheetCriterionRowPatterns?: RegExp[]; /** Darstellung einer vom Quellbericht als ausgewertet markierten Belegzeile. */ resultSheetEvaluatedRowTone?: 'violation'; /** * Explicit per-row status markers written by the source itself (for example a * check mark in a status cell). A row is colored only when its cells prove exactly * one of both states; nothing is inferred from other values. */ resultSheetRowStatusCells?: { match: RegExp[]; violation: RegExp[] }; /** * First-read layout of the Finding Reader (user decision 2026-09-25): which retained * source fields state the problem, its impact and the next step. Only declared * fields are used; a missing field removes its line instead of inventing text. */ firstRead?: { /** Concrete result statement of the scanner for this environment. */ resultHeaders?: RegExp[]; /** Explicit count of affected results. */ affectedHeaders?: RegExp[]; /** Count linked result-table rows when no explicit count exists. */ affectedFromResults?: boolean; /** Count rows the source itself marks as passed or failed (`resultSheetRowStatusCells`). */ statusRowCounts?: boolean; /** Show the source's criterion and evaluated rows as expected and found. */ criterionEvidence?: boolean; impactHeaders?: RegExp[]; nextStepHeaders?: RegExp[]; /** Retained https documentation link offered as the next step when no remediation text exists. */ nextStepLinkHeaders?: RegExp[]; /** * Native remediation subsection inside another retained field, used when no dedicated * remediation field exists. The section runs from the native heading to the end of the field; * the text before the heading stays in place as background. */ nextStepSection?: { headers: RegExp[]; heading: RegExp }; /** * Native reference heading the scanner appends to its impact text. The impact row ends before it; * the retained remainder is shown with the references. */ impactReferenceMarker?: RegExp; /** Explanatory text shown below the next step. */ backgroundHeaders?: RegExp[]; /** Documentation and references shown after the background. */ referenceHeaders?: RegExp[]; }; }; comparisonKeys: CanonicalFieldRole[][]; }