/** * Deterministische, versionierte Ableitungsregeln für die additive Realisierung * von RESULT_CONTRACT 1.0.0 (siehe docs/RESULT_CONTRACT.md und * docs/CANONICAL_MODEL_1.0.0.md). * * Dies ist der einzige Ort, an dem `resultType`, `isIoe`, `exposureState` und * die Vertragsprojektion `resultState` entstehen. Keine dieser Größen wird an * anderer Stelle aus einem rohen Tool-String oder Anzeigetext abgeleitet. * `is_ioe` ist per RESULT_CONTRACT niemals per Default true für * `compliance_check` oder `policy_deviation`. * * GPO-/Baseline-Ergebnisse (`policy_deviation`) entstehen aus der versionierten * Policy-Evaluation. Die Finding Factory darf sie um freigegebene Risiko- und * Severity-Aussagen anreichern; `deriveResultType` leitet sie nie aus einer * bloßen Tool-ID ab. */ import type { CanonicalOutcome, CanonicalTechnicalExecutionState } from './canonical-model'; export const RESULT_CONTRACT_MAPPING_VERSION = '1.0.0'; export type ResultContractResultType = | 'exposure_finding' | 'vulnerability_finding' | 'compliance_check' | 'policy_deviation' | 'informational' | 'unknown'; export type ResultContractExposureState = | 'detected' | 'not_detected' | 'not_evaluated' | 'execution_error' | 'unknown'; export type ResultContractResultState = | 'fail' | 'pass' | 'warning' | 'error' | 'not_applicable' | 'not_evaluated' | 'informational' | 'unknown'; export interface ResultContractIoeDerivation { isIoe: boolean; ioeRuleId: string; ioeRuleVersion: string; ioeDerivationMethod: string; } /** * Werkzeuge, deren native Aussage RESULT_CONTRACT als `exposure_finding` * einordnet (Quellen-Mapping-Tabelle, docs/RESULT_CONTRACT.md). */ const EXPOSURE_TOOL_IDS = new Set(['pingcastle', 'purple-knight', 'maester']); /** Scanner, deren native Aussage eine konkrete Schwachstelle bezeichnet. */ const VULNERABILITY_TOOL_IDS = new Set(['defender-for-iot-vulnerabilities']); /** * Werkzeuge, deren native Aussage RESULT_CONTRACT als `compliance_check` * einordnet. */ const COMPLIANCE_TOOL_IDS = new Set(['cis-cat']); /** * result_type ist ausschließlich von der werkzeuggebundenen Tool-ID abhängig, * nie vom Anzeigetext oder von einem einzelnen Vorkommen. Unbekannte oder * generische Werkzeuge bleiben `unknown` — das entspricht dem generischen * Fallback in ASSESSOR und verhindert, dass ein noch nicht katalogisiertes * Werkzeug fälschlich als Exposure- oder Compliance-Quelle erscheint. */ export function deriveResultType(toolId: string): ResultContractResultType { const normalized = toolId.trim().toLocaleLowerCase('en-US'); if (EXPOSURE_TOOL_IDS.has(normalized)) return 'exposure_finding'; if (VULNERABILITY_TOOL_IDS.has(normalized)) return 'vulnerability_finding'; if (COMPLIANCE_TOOL_IDS.has(normalized)) return 'compliance_check'; return 'unknown'; } /** * exposure_state existiert laut RESULT_CONTRACT nur für `exposure_finding`. * Die Ableitung verwendet ausschließlich die bereits normalisierte * ASSESSOR-Outcome-Achse (`outcomeNormalized`), nie den rohen Quellstring. */ export function deriveExposureState( resultType: ResultContractResultType, outcomeNormalized: CanonicalOutcome ): ResultContractExposureState | undefined { if (resultType !== 'exposure_finding') return undefined; switch (outcomeNormalized) { case 'fail': return 'detected'; case 'pass': return 'not_detected'; case 'not_evaluated': case 'informational': case 'not_run': case 'not_applicable': return 'not_evaluated'; case 'execution_error': return 'execution_error'; default: return 'unknown'; } } const IOE_DEFAULT_RULE_ID = 'result-contract-ioe-default'; const IOE_DEFAULT_DERIVATION_METHOD = 'exposure-state-default'; /** * is_ioe ist ausschließlich das Ergebnis dieser Funktion. Der Default aus * RESULT_CONTRACT: exposure_finding + exposure_state=detected -> true, sonst * immer false. Eine Elevationsregel, die einen Compliance-Check oder eine * Policy-Abweichung auf true hebt, ist laut Vertrag ausdrücklich nur über eine * eigene, versionierte Elevationsregel zulässig und in ASSESSOR bislang nicht * spezifiziert (siehe RESULT_CONTRACT.md, „Offene Punkte"). */ export function deriveIsIoe( resultType: ResultContractResultType, exposureState: ResultContractExposureState | undefined ): ResultContractIoeDerivation { const isIoe = resultType === 'exposure_finding' && exposureState === 'detected'; return { isIoe, ioeRuleId: IOE_DEFAULT_RULE_ID, ioeRuleVersion: RESULT_CONTRACT_MAPPING_VERSION, ioeDerivationMethod: IOE_DEFAULT_DERIVATION_METHOD }; } /** * result_state ist in ASSESSOR keine persistierte Spalte (siehe * RESULT_CONTRACT.md, „Abweichungen der ASSESSOR-Realisierung"). Diese * Funktion projiziert den Vertragswert deterministisch aus den beiden * bestehenden, getrennt persistierten Achsen `outcomeNormalized` und * `technicalExecutionState`. Ein technischer Ausführungsfehler hat Vorrang, * damit ein Tool-Absturz nie als fachliches `fail` erscheint. */ export function projectResultState( outcomeNormalized: CanonicalOutcome, technicalExecutionState: CanonicalTechnicalExecutionState ): ResultContractResultState { if (technicalExecutionState === 'failed') return 'error'; switch (outcomeNormalized) { case 'fail': return 'fail'; case 'pass': return 'pass'; case 'warning': return 'warning'; case 'execution_error': return 'error'; case 'not_applicable': return 'not_applicable'; case 'not_run': return 'not_evaluated'; case 'not_evaluated': return 'not_evaluated'; case 'informational': return 'informational'; default: return 'unknown'; } }