import { createHash } from 'node:crypto'; import { GPO_NATIVE_POLICY_IDENTITY_RESOLVER, gpoTechnicalPolicyIdentityValidationErrors, type AdmxPolicyCatalog, type GpoPolicyIdentityResolution } from '../../src/domain/gpo-policy-identity'; import type { GpoPolicyObservation } from '../../src/domain/gpo-policy'; import { resolveRegistryPolicyIdentity } from './admx-policy-catalog'; export function resolvePolicyTarget(observation: GpoPolicyObservation, catalog?: AdmxPolicyCatalog): GpoPolicyIdentityResolution { if (observation.deliveryMechanism === 'registry-policy') return resolveRegistryPolicyIdentity(observation, catalog); if (!['security-template', 'advanced-audit-policy'].includes(observation.deliveryMechanism)) { return { version: 1, status: 'unresolved', reasonCode: `identity-resolver-unsupported-${observation.deliveryMechanism}` }; } const kind = observation.deliveryMechanism as 'security-template' | 'advanced-audit-policy'; const section = observation.identity.section.normalize('NFC'); const key = observation.identity.key.normalize('NFC'); // The digest stays case-folded so one technical setting keeps one identity across // differently written sources. The retained target keeps the written form, exactly as // the registry resolver already does, so the value stays recognisable and searchable. const digest = createHash('sha256').update(['native-policy-target-v1', kind, observation.scope, section.toLowerCase(), key.toLowerCase()].join('\0')).digest('hex'); const identity = { version: 1 as const, id: `gpo-policy-identity-${digest.slice(0, 32)}`, resolver: GPO_NATIVE_POLICY_IDENTITY_RESOLVER, scope: observation.scope, target: { kind, section, key }, admx: { status: 'not-found' as const } }; if (gpoTechnicalPolicyIdentityValidationErrors(identity).length) { return { version: 1, status: 'unresolved', reasonCode: 'policy-technical-target-invalid' }; } return { version: 1, status: 'resolved', identity }; }