import { canonicalJson } from '../../src/shared/canonical-json'; import type { AggregatedPolicyState, EnvironmentProfile, StoredPolicySnapshotAggregation } from '../../src/domain/gpo-snapshot-aggregation'; import type { PolicyBaselineState, PolicyComparatorDefinition } from '../../src/domain/policy-evaluation'; import { policyEvaluationRulesForContext, type PolicyDependencyRule, type PolicyEvaluationRule, type PolicyEvaluationRuleCatalog } from '../../src/domain/policy-evaluation-rules'; export interface PolicyRuleResolution { baselineState: PolicyBaselineState; comparator: PolicyComparatorDefinition; ruleIds: string[]; dependencyRuleIds: string[]; riskRuleIds: string[]; notApplicable: boolean; diagnosticCodes: string[]; } function sortedUnique(values: string[]): string[] { return [...new Set(values)].sort((left, right) => left.localeCompare(right)); } function dependencyTarget( aggregation: StoredPolicySnapshotAggregation, dependency: PolicyDependencyRule ): AggregatedPolicyState | undefined { return aggregation.states.find(state => state.identity.id === dependency.identityId && state.deliveryMechanism === dependency.deliveryMechanism); } function targetIsConfigured(target: AggregatedPolicyState): boolean { return target.status === 'configured' && target.completeness === 'complete' && target.targeting === 'none'; } function dependencyResolution( aggregation: StoredPolicySnapshotAggregation, dependencies: PolicyDependencyRule[] ): { notApplicable: boolean; diagnosticCodes: string[] } { const diagnostics = new Set(); let notApplicable = false; for (const dependency of dependencies) { const target = dependencyTarget(aggregation, dependency); if (!target) { if (!aggregation.snapshot.permitsSetAbsenceConclusions) { diagnostics.add('policy-dependency-target-absence-unknown'); } else if (dependency.kind === 'prerequisite') { notApplicable = true; } continue; } if (target.status === 'conflicted' || target.status === 'indeterminate' || target.completeness !== 'complete' || target.targeting !== 'none') { diagnostics.add('policy-dependency-target-indeterminate'); continue; } const configured = targetIsConfigured(target); if (dependency.kind === 'prerequisite' && !configured) notApplicable = true; if (dependency.kind === 'override' && configured) notApplicable = true; if (dependency.kind === 'mutual-exclusion' && configured) { diagnostics.add('policy-dependency-mutual-exclusion-active'); } } return { notApplicable, diagnosticCodes: [...diagnostics].sort() }; } export function resolvePolicyEvaluationRule( catalog: PolicyEvaluationRuleCatalog, profile: EnvironmentProfile, aggregation: StoredPolicySnapshotAggregation, state: AggregatedPolicyState ): PolicyRuleResolution { const diagnostics = new Set(); const selected: PolicyEvaluationRule[] = []; for (const contextId of state.applicableContextIds) { const context = profile.contexts.find(candidate => candidate.id === contextId); if (!context) { diagnostics.add('policy-evaluation-context-missing'); continue; } const candidates = policyEvaluationRulesForContext( catalog, state.identity.id, state.deliveryMechanism, context, state.identity.scope ); if (candidates.length !== 1) { diagnostics.add(candidates.length ? 'policy-evaluation-rule-ambiguous' : 'policy-evaluation-rule-catalog-gap'); continue; } selected.push(candidates[0]); } const rules = [...new Map(selected.map(rule => [rule.id, rule])).values()] .sort((left, right) => left.id.localeCompare(right.id)); const dispositions = new Set(rules.map(rule => rule.baselineDisposition)); if (dispositions.size > 1) diagnostics.add('policy-evaluation-rule-context-conflict'); let baselineState: PolicyBaselineState = dispositions.size === 1 ? rules[0].baselineDisposition : 'catalog-gap'; const recommendationRules = rules.filter(rule => rule.baselineDisposition === 'explicit-recommendation'); const comparatorSignatures = new Set(recommendationRules.map(rule => canonicalJson(rule.comparator))); if (comparatorSignatures.size > 1) diagnostics.add('policy-evaluation-comparator-consensus-conflict'); const riskSignatures = new Set(recommendationRules.map(rule => canonicalJson(rule.riskRule))); if (riskSignatures.size > 1) diagnostics.add('policy-evaluation-risk-consensus-conflict'); if (baselineState === 'no-recommendation') diagnostics.add('policy-evaluation-no-recommendation'); const dependencies = [...new Map(rules.flatMap(rule => rule.dependencies).map(rule => [rule.id, rule])).values()] .sort((left, right) => left.id.localeCompare(right.id)); const conflictingDependencyIds = new Set(); for (const rule of rules) { for (const dependency of rule.dependencies) { const sameId = dependencies.find(candidate => candidate.id === dependency.id); if (sameId && canonicalJson(sameId) !== canonicalJson(dependency)) conflictingDependencyIds.add(dependency.id); } } if (conflictingDependencyIds.size) diagnostics.add('policy-evaluation-dependency-definition-conflict'); const dependency = dependencyResolution(aggregation, dependencies); dependency.diagnosticCodes.forEach(code => diagnostics.add(code)); let notApplicable = baselineState === 'explicitly-not-applicable' || dependency.notApplicable; if (dependency.notApplicable) baselineState = 'explicitly-not-applicable'; if (diagnostics.size) notApplicable = false; const comparator = recommendationRules[0]?.comparator ?? { kind: 'equality' as const }; return { baselineState, comparator, ruleIds: rules.map(rule => rule.id), dependencyRuleIds: dependencies.map(rule => rule.id), riskRuleIds: sortedUnique(recommendationRules.flatMap(rule => rule.riskRule ? [rule.riskRule.id] : [])), notApplicable, diagnosticCodes: [...diagnostics].sort() }; }