import type { GpoPolicyValue, GpoRegistryPolicyData } from '../domain/gpo-policy'; import type { PolicyComparableValue, PolicyComparatorDefinition, PolicyComparatorOutcome, PolicyComparatorRelation } from '../domain/policy-evaluation'; import { canonicalJson } from '../shared/canonical-json'; function sortedUnique(values: string[]): string[] { return [...new Set(values)].sort((left, right) => left.localeCompare(right)); } function registryDataValue(data: GpoRegistryPolicyData | undefined): PolicyComparableValue | undefined { if (!data) return undefined; if (data.kind === 'none') return { kind: 'none' }; if (data.kind === 'string') return { kind: 'text', value: data.value }; if (data.kind === 'multi-string') return { kind: 'string-set', values: sortedUnique(data.values) }; if (data.kind === 'binary') return { kind: 'binary', hexadecimal: data.hexadecimal.toLocaleLowerCase('en-US') }; if (data.kind === 'uint32') return { kind: 'integer', value: String(data.value) }; if (data.kind === 'uint64' && /^\d+$/.test(data.value)) return { kind: 'integer', value: BigInt(data.value).toString() }; return undefined; } export function policyComparableValue(value: GpoPolicyValue | undefined): PolicyComparableValue | undefined { if (!value) return undefined; if (value.kind === 'registry-policy') return registryDataValue(value.data); // Security-template registry values use their INF type code plus raw value. // Retain the type in the comparable representation so equal text encoded as // different registry types is not treated as the same recommendation. if (value.kind === 'registry') return { kind: 'text', value: `${value.typeCode},${value.rawData}` }; if (value.kind === 'scalar') return { kind: 'text', value: value.raw }; if (value.kind === 'principal-set') return { kind: 'string-set', values: sortedUnique(value.principals) }; if (value.kind === 'audit-flags') return { kind: 'audit-flags', success: value.success, failure: value.failure }; return undefined; } function integer(value: PolicyComparableValue): bigint | undefined { if (value.kind !== 'integer' || !/^-?\d+$/.test(value.value)) return undefined; return BigInt(value.value); } function relationByStrength(actual: bigint, baseline: bigint, higherIsStronger: boolean): PolicyComparatorRelation { if (actual === baseline) return 'match'; return (actual > baseline) === higherIsStronger ? 'stronger' : 'weaker'; } function stringSet(value: PolicyComparableValue): Set | undefined { return value.kind === 'string-set' ? new Set(value.values) : undefined; } function includesAll(left: Set, right: Set): boolean { return [...right].every(value => left.has(value)); } function compareSets(actual: Set, baseline: Set, mode: 'superset' | 'subset' | 'exact'): PolicyComparatorRelation { const actualContains = includesAll(actual, baseline); const baselineContains = includesAll(baseline, actual); if (actualContains && baselineContains) return 'match'; if (mode === 'exact') return 'different'; if (mode === 'superset') return actualContains ? 'stronger' : baselineContains ? 'weaker' : 'different'; return baselineContains ? 'stronger' : actualContains ? 'weaker' : 'different'; } function compareAudit( actual: Extract, baseline: Extract, mode: 'contains-baseline' | 'exact' ): PolicyComparatorRelation { if (actual.success === baseline.success && actual.failure === baseline.failure) return 'match'; if (mode === 'exact') return 'different'; const actualContains = (!baseline.success || actual.success) && (!baseline.failure || actual.failure); const baselineContains = (!actual.success || baseline.success) && (!actual.failure || baseline.failure); return actualContains ? 'stronger' : baselineContains ? 'weaker' : 'different'; } export function comparePolicyValues( actualSource: GpoPolicyValue | undefined, baselineSource: GpoPolicyValue | undefined, comparator: PolicyComparatorDefinition ): PolicyComparatorOutcome { const actual = policyComparableValue(actualSource); const baseline = policyComparableValue(baselineSource); let relation: PolicyComparatorRelation = 'incomparable'; const diagnosticCodes: string[] = []; if (!actual || !baseline) diagnosticCodes.push('policy-comparator-value-unsupported'); else if (comparator.kind === 'equality') { relation = canonicalJson(actual) === canonicalJson(baseline) ? 'match' : 'different'; } else if (comparator.kind === 'minimum' || comparator.kind === 'maximum') { const actualInteger = integer(actual); const baselineInteger = integer(baseline); if (actualInteger === undefined || baselineInteger === undefined) diagnosticCodes.push('policy-comparator-integer-required'); else relation = relationByStrength(actualInteger, baselineInteger, comparator.kind === 'minimum'); } else if (comparator.kind === 'enum') { const actualText = actual.kind === 'text' ? actual.value : undefined; const baselineText = baseline.kind === 'text' ? baseline.value : undefined; const actualIndex = actualText === undefined ? -1 : comparator.orderedValues.indexOf(actualText); const baselineIndex = baselineText === undefined ? -1 : comparator.orderedValues.indexOf(baselineText); if (actualIndex < 0 || baselineIndex < 0 || new Set(comparator.orderedValues).size !== comparator.orderedValues.length) { diagnosticCodes.push('policy-comparator-enum-value-unsupported'); } else relation = relationByStrength(BigInt(actualIndex), BigInt(baselineIndex), comparator.direction === 'higher-is-stronger'); } else if (comparator.kind === 'bitmask') { const actualInteger = integer(actual); const baselineInteger = integer(baseline); if (actualInteger === undefined || baselineInteger === undefined || actualInteger < 0n || baselineInteger < 0n) { diagnosticCodes.push('policy-comparator-bitmask-required'); } else if (actualInteger === baselineInteger) relation = 'match'; else if (comparator.mode === 'exact') relation = 'different'; else if ((actualInteger & baselineInteger) === baselineInteger) relation = 'stronger'; else if ((actualInteger & baselineInteger) === actualInteger) relation = 'weaker'; else relation = 'different'; } else if (comparator.kind === 'set') { const actualSet = stringSet(actual); const baselineSet = stringSet(baseline); if (!actualSet || !baselineSet) diagnosticCodes.push('policy-comparator-set-required'); else relation = compareSets(actualSet, baselineSet, comparator.mode); } else { if (actual.kind !== 'audit-flags' || baseline.kind !== 'audit-flags') diagnosticCodes.push('policy-comparator-audit-flags-required'); else relation = compareAudit(actual, baseline, comparator.mode); } return { version: 1, comparator, relation, actual, baseline, diagnosticCodes }; }