Assessor_2.0/src/domain/canonical-model.ts

357 lines
9.9 KiB
TypeScript

import type { ResultContractExposureState, ResultContractResultType } from './result-contract-mapping';
import type { RiskAcceptance } from './risk-acceptance';
import type { EnvironmentObservation, ScannerReportSection } from './environment-context';
import type { SecurityScoringPolicy } from './security-scoring';
export type JsonPrimitive = string | number | boolean | null;
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };
/**
* result_type aus RESULT_CONTRACT 1.0.0 (docs/RESULT_CONTRACT.md). Additive
* Facette, ausschließlich über src/domain/result-contract-mapping.ts abgeleitet.
*/
export type CanonicalResultType = ResultContractResultType;
/**
* exposure_state aus RESULT_CONTRACT 1.0.0, nur bei
* CanonicalResultType = 'exposure_finding' belegt.
*/
export type CanonicalExposureState = ResultContractExposureState;
export type CanonicalSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info' | 'unknown';
export type CanonicalFindingStatus =
| 'open'
| 'failed'
| 'passed'
| 'resolved'
| 'error'
| 'unknown'
| 'manual'
| 'not-applicable'
| 'not-run'
| 'investigate';
export type CanonicalOutcome =
| 'fail' | 'pass' | 'warning' | 'informational' | 'execution_error'
| 'not_run' | 'not_applicable' | 'not_evaluated' | 'unknown';
export type CanonicalTechnicalExecutionState = 'completed' | 'failed' | 'not-run' | 'unknown';
export type MappingConfidenceLevel = 'high' | 'medium' | 'low' | 'unknown';
export type MappingDecisionSource = 'automatic' | 'user-confirmed' | 'source-native';
export interface MappingConfidence {
level: MappingConfidenceLevel;
score: number;
reasons: string[];
}
export interface MappingProvenance {
decisionSource: MappingDecisionSource;
confidence: MappingConfidence;
mappingVersion: string;
}
export interface MappedValue<T extends JsonValue> {
originalValue: JsonValue;
normalizedValue: T | null;
provenance: MappingProvenance;
}
export interface ToolIdentity {
id: string;
name: string;
version?: string;
}
export interface ProcessingVersion {
parserId: string;
parserVersion: string;
profileId: string;
profileVersion: string;
mappingVersion: string;
/** Additive Felder; bei Altdaten aus dem versionierten Manifest aufloesbar. */
normalizationVersion?: string;
presentationVersion?: string;
compatibilityKey?: string;
changeClass?: 'presentation-only' | 'compatible' | 'semantic-breaking';
reimportRequired?: boolean;
}
export interface AssessmentProject {
id: string;
name: string;
description?: string;
createdAt: string;
updatedAt: string;
attributes: Record<string, JsonValue>;
}
export interface Assessment {
id: string;
projectId: string;
name: string;
description?: string;
scope?: string;
/** Fachlicher Stichtag dieses vollständigen Mandanten-Bewertungsstands. */
effectiveAt?: string;
/** Persisted with the assessment JSON; absent historical values resolve to immutable v1. */
scoringPolicy?: SecurityScoringPolicy;
createdAt: string;
updatedAt: string;
}
export interface ScanRun {
id: string;
assessmentId: string;
sequence: number;
label: string;
tool: ToolIdentity;
startedAt?: string;
completedAt?: string;
importedAt: string;
/** Reimport history: replaced runs remain immutable and auditable, but are no longer active projections. */
supersedesScanRunId?: string;
supersededByScanRunId?: string;
supersededAt?: string;
}
export type SourceArtifactContent =
| { kind: 'embedded'; bytesBase64: string }
| { kind: 'external'; storageKey: string }
| { kind: 'not-retained'; reason: string };
export interface ImportFileRecord {
id: string;
scanRunId: string;
fileName: string;
mediaType?: string;
format: string;
sizeBytes: number;
lastModifiedAt?: string;
sha256?: string;
importedAt: string;
detectedTool: ToolIdentity;
detection: MappingConfidence;
processedWith: ProcessingVersion;
sourceArtifact: SourceArtifactContent;
/** Persistent, content-free outcome of preserving client work during reimport. */
reimportReviewTransfer?: {
replacesScanRunId: string;
transferred: number;
retainedByStableIdentity: number;
unresolved: number;
};
}
export interface SourceTableRecord {
id: string;
importFileId: string;
name: string;
index: number;
hidden?: boolean;
rowCount: number;
columnCount: number;
attributes: Record<string, JsonValue>;
rows?: SourceTableRow[];
}
export interface SourceTableRow {
index: number;
fields: OriginalField[];
/** Der Bericht markiert diese Zeile selbst (z. B. CIS-CAT: `<tr class="evaluated">`). */
highlighted?: boolean;
}
export interface FrameworkReference {
framework: string;
controlId: string;
version?: string;
title?: string;
url?: string;
}
export type FindingIdentityStrategy = 'tool-rule-id' | 'framework-control' | 'unresolved';
export interface FindingIdentity {
version: 1;
strategy: FindingIdentityStrategy;
durable: boolean;
evidence: string[];
}
export interface FindingDefinition {
id: string;
canonicalKey: string;
identity?: FindingIdentity;
title: string;
description?: string;
category?: string;
recommendation?: string;
frameworkReferences: FrameworkReference[];
createdAt: string;
updatedAt: string;
}
export interface OriginalField {
key: string;
label: string;
value: JsonValue;
rawValue?: string;
path?: string;
columnIndex?: number;
}
export interface SourcePosition {
rowIndex?: number;
columnIndex?: number;
cellReference?: string;
objectPath?: string;
elementSelector?: string;
}
export interface SourceFindingIdentity {
tool: ToolIdentity;
originalRuleId?: string;
canonicalRuleId?: string;
originalTitle?: string;
}
export type CanonicalFieldRole =
| 'rule-id'
| 'title'
| 'description'
| 'category'
| 'severity'
| 'status'
| 'score'
| 'asset'
| 'evidence'
| 'recommendation'
| 'framework-reference'
| 'additional';
export interface FieldMapping {
targetRole: CanonicalFieldRole;
sourceFieldKeys: string[];
normalizedValue: JsonValue;
provenance: MappingProvenance;
}
export interface FindingOccurrence {
id: string;
definitionId: string;
scanRunId: string;
importFileId: string;
sourceTableId?: string;
observedAt?: string;
sourceIdentity: SourceFindingIdentity;
sourcePosition: SourcePosition;
originalFields: OriginalField[];
fieldMappings: FieldMapping[];
severity: MappedValue<CanonicalSeverity>;
status: MappedValue<CanonicalFindingStatus>;
outcome?: MappedValue<CanonicalOutcome>;
technicalExecutionState?: MappedValue<CanonicalTechnicalExecutionState>;
score?: MappedValue<number>;
affectedAsset?: MappedValue<string>;
/**
* RESULT_CONTRACT-Facetten. Wie bei outcome/technicalExecutionState gilt:
* ein hier gesetzter Wert wird beim Persistieren übernommen; fehlt er, wird
* er aus tool_id sowie outcome/technicalExecutionState über
* src/domain/result-contract-mapping.ts abgeleitet. Kein Producer setzt
* diese Felder derzeit explizit.
*/
resultType?: CanonicalResultType;
isIoe?: boolean;
ioeRuleId?: string;
ioeRuleVersion?: string;
ioeDerivationMethod?: string;
exposureState?: CanonicalExposureState;
/**
* Version des Feld-Mappings (dataset.mapping.mappingProfile.version), das
* dieses Vorkommen erzeugt hat. Wird beim Persistieren übernommen; fehlt
* sie, wird sie aus processedWith.mappingVersion der zugehörigen
* ImportFileRecord übernommen. Ermöglicht, einen historischen Fund auf sein
* damaliges Mapping zurückzuführen.
*/
mappingRuleVersion?: string;
}
export type EvidenceKind = 'text' | 'table' | 'object' | 'reference' | 'attachment';
export interface EvidenceRecord {
id: string;
occurrenceId: string;
kind: EvidenceKind;
title?: string;
value: JsonValue;
sourceFieldKeys: string[];
sourcePosition?: SourcePosition;
}
export type ReviewWorkflowStatus = 'unreviewed' | 'open' | 'in-progress' | 'accepted' | 'deferred' | 'resolved' | 'not-relevant';
export type ReviewPriority = 'critical' | 'high' | 'medium' | 'low' | 'none';
export type ReviewTarget =
| { kind: 'definition'; definitionId: string }
| { kind: 'occurrence'; occurrenceId: string };
export interface FindingReview {
id: string;
target: ReviewTarget;
workflowStatus: ReviewWorkflowStatus;
/** Exact FindingReader label when it is more specific than the canonical workflow state. */
workflowLabel?: string;
priority: ReviewPriority;
includeInActionPlan: boolean;
owner?: string;
dueAt?: string;
comment?: string;
prerequisites?: string;
requiredAccess?: string;
nextStep?: string;
acceptanceEvidence?: string;
riskAcceptance?: RiskAcceptance;
markers: string[];
createdAt: string;
updatedAt: string;
revision: number;
/** Audit reference when client-owned review data was carried to a reimported target. */
transferredFromReviewId?: string;
transferredAt?: string;
}
export type DiagnosticSeverity = 'info' | 'warning' | 'error';
export interface ImportDiagnostic {
id: string;
importFileId: string;
sourceTableId?: string;
severity: DiagnosticSeverity;
code: string;
message: string;
sourcePosition?: SourcePosition;
details?: JsonValue;
}
export interface CanonicalAssessmentModel {
/** Latest stored reference derivation per scan; canonical source facts remain unchanged. */
referenceRuns?: import('./reference-recognition').ReferenceRecognitionRun[];
schemaVersion: 1;
project: AssessmentProject;
assessment: Assessment;
scanRuns: ScanRun[];
importFiles: ImportFileRecord[];
sourceTables: SourceTableRecord[];
/** Additive scanner context; absent on canonical model records created before Phase 13C. */
reportSections?: ScannerReportSection[];
/** Typed scan-level observations; absent on canonical model records created before Phase 13C. */
environmentObservations?: EnvironmentObservation[];
findingDefinitions: FindingDefinition[];
findingOccurrences: FindingOccurrence[];
evidence: EvidenceRecord[];
reviews: FindingReview[];
diagnostics: ImportDiagnostic[];
}