import type { GpoSecretIndicatorStatus } from './gpo-ingestion'; export interface GpoBackupMetadataDocument { version: 1; parser: { id: 'gpo-backup-metadata-xml'; version: '1.0.0' | '1.1.0' | '1.2.0' | '1.3.0' | '1.4.0' }; /** Only requested by the Microsoft template analyzer; never inferred from paths. */ templateNames?: string[]; sourcePath: string; sourceSha256: string; status: 'complete' | 'partial' | 'invalid'; gpoId?: string; backupId?: string; domainKey?: string; backupCreatedAt?: string; /** * Link facts of a GPReport.xml (parser 1.4.0). Only counts are retained, never the * linked OU or domain names. Absent for other metadata files and earlier parsers. */ links?: GpoBackupLinkEvidence; secretIndicatorStatus: GpoSecretIndicatorStatus; diagnosticCodes: string[]; } /** Whether the backup's own report lists an enabled link to a domain or OU at backup time. */ export interface GpoBackupLinkEvidence { status: 'linked' | 'not-linked'; linkCount: number; enabledLinkCount: number; } export interface GpoBackupMetadataAggregate { version: 1; status: 'complete' | 'partial' | 'invalid'; gpoId?: string; backupId?: string; domainKey?: string; backupCreatedAt?: string; sourcePaths: string[]; /** Present only when exactly one report of the backup states its links. */ links?: GpoBackupLinkEvidence; diagnosticCodes: string[]; } function unique(values: Array): string[] { return [...new Set(values.filter((value): value is string => Boolean(value)))].sort(); } export function aggregateGpoBackupMetadata(documents: GpoBackupMetadataDocument[]): GpoBackupMetadataAggregate { const diagnostics = new Set(documents.flatMap(document => document.diagnosticCodes)); let invalid = documents.some(document => document.status === 'invalid'); const field = (name: 'gpoId' | 'backupId' | 'domainKey' | 'backupCreatedAt'): string | undefined => { const values = unique(documents.map(document => document[name])); if (values.length > 1) { diagnostics.add(`gpo-metadata-${name}-conflict`); invalid = true; } return values.length === 1 ? values[0] : undefined; }; const gpoId = field('gpoId'); const backupId = field('backupId'); const domainKey = field('domainKey'); const backupCreatedAt = field('backupCreatedAt'); const complete = Boolean(gpoId && backupId && domainKey && backupCreatedAt); // Several reports with link facts cannot be ordered; no link status is claimed then. const linkDocuments = documents.filter(document => document.links); if (linkDocuments.length > 1) diagnostics.add('gpo-metadata-links-ambiguous'); if (!gpoId) diagnostics.add('gpo-metadata-gpo-id-missing'); if (!backupId) diagnostics.add('gpo-metadata-backup-id-missing'); if (!domainKey) diagnostics.add('gpo-metadata-domain-missing'); if (!backupCreatedAt) diagnostics.add('gpo-metadata-backup-time-missing'); return { version: 1, status: invalid ? 'invalid' : complete ? 'complete' : 'partial', gpoId, backupId, domainKey, backupCreatedAt, sourcePaths: [...new Set(documents.map(document => document.sourcePath))].sort(), ...(linkDocuments.length === 1 ? { links: linkDocuments[0].links } : {}), diagnosticCodes: [...diagnostics].sort() }; }