Assessor_2.0/src/domain/result-contract-mapping.ts

150 lines
5.6 KiB
TypeScript

/**
* 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';
}
}