150 lines
5.6 KiB
TypeScript
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';
|
|
}
|
|
}
|