import type { UnifiedResultProjection } from '../domain/unified-result'; import { POLICY_CLASSIFICATION_LABELS } from '../domain/policy-status-labels'; import { discloseResultDbProjection } from '../domain/result-db-disclosure'; const SAFE_ENVIRONMENT_PROPERTIES = new Set([ 'domain.controller_count', 'domain.functional_level', 'domain.recycle_bin_enabled', 'forest.functional_level', 'logging.wef.configuration_count', 'operating_system.count', 'operating_system.name' ]); export function privacySafeResultJsonFileName(generatedAt: string): string { const timestamp = generatedAt.replace(/\.\d{3}Z$/, 'Z').replaceAll(':', '-'); return `assessor-result-db-privacy-safe-${timestamp}.json`; } export function resultsToPrivacySafeExportJson(items: UnifiedResultProjection[]): string { // A raw GPO observation is context for its rolled-up comparison, not a finding of its own. const findings = items.filter(item => item.sourceKind !== 'environment-observation' && item.sourceKind !== 'policy-observation'); const policyObservations = items.filter(item => item.sourceKind === 'policy-observation'); const policyFindings = findings.filter(item => item.sourceKind === 'policy-evaluation'); const environment = items.filter(item => item.sourceKind === 'environment-observation'); const actionable = findings.filter(item => item.isDeviation || item.isIoe || ['fail', 'warning', 'execution_error'].includes(item.outcomeNormalized)); const notRun = findings.filter(item => item.outcomeNormalized === 'not_run'); return JSON.stringify({ schemaVersion: 1, summary: { totalResults: items.length, findingResults: findings.length, environmentResults: environment.length, policyObservationResults: policyObservations.length, actionableFindings: actionable.length, notRunChecks: notRun.length, findingOutcomes: countBy(findings, item => item.outcomeNormalized), findingSeverities: countBy(findings, item => item.severityNormalized), findingTools: countBy(findings, item => item.sourceTool ?? 'unknown'), policyStatuses: countBy(policyFindings, policyStatus), environmentStates: countBy(environment, item => item.environmentState ?? 'unknown') }, findings: actionable.map(item => { const description = copiedText(item.description); const recommendation = copiedText(item.recommendation); return { id: item.sourceRuleId ?? null, title: privacySafeText(item.title), ...(description ? { description } : {}), ...(recommendation ? { recommendation } : {}), severity: item.severityNormalized, outcome: item.outcomeNormalized, resultType: item.resultType, isIoe: item.isIoe, deviation: item.isDeviation, tool: item.sourceTool ?? 'unknown', ...privacySafePolicy(item) }; }), notRunChecks: notRun.map(item => ({ id: item.sourceRuleId ?? null, title: privacySafeText(item.title), severity: item.severityNormalized, resultType: item.resultType, tool: item.sourceTool ?? 'unknown' })), environmentFacts: groupedEnvironmentFacts(environment) }, null, 2); } function policyStatus(item: UnifiedResultProjection): string { return item.policyClassification ? POLICY_CLASSIFICATION_LABELS[item.policyClassification] : 'unknown'; } /** GPO comparison facts under the Result DB disclosure policy: the customer value stays withheld. */ function privacySafePolicy(item: UnifiedResultProjection): { policy?: Record } { if (item.sourceKind !== 'policy-evaluation') return {}; const disclosed = discloseResultDbProjection(item); const policy = { status: policyStatus(disclosed), baseline: disclosed.policyBaselineLabel, expectedValue: disclosed.policyBaselineValue, comparison: disclosed.policyComparisonRelation, customerState: disclosed.policyCustomerState, scope: disclosed.policyScope, delivery: disclosed.policyDeliveryMechanism, diagnostics: disclosed.policyDiagnosticCodes?.length ? disclosed.policyDiagnosticCodes : undefined }; return { policy: Object.fromEntries(Object.entries(policy).filter(([, value]) => value !== undefined)) }; } function groupedEnvironmentFacts(items: UnifiedResultProjection[]) { const grouped = new Map(); items.forEach(item => { const property = copiedEnvironmentProperty(item); if (!property) return; const fact = { title: privacySafeText(item.title), property, value: item.environmentNormalizedValue ?? null, state: item.environmentState ?? 'unknown', tool: item.sourceTool ?? 'unknown' }; const key = JSON.stringify(fact); const existing = grouped.get(key); if (existing) existing.count += 1; else grouped.set(key, { ...fact, count: 1 }); }); return [...grouped.values()]; } function countBy(items: UnifiedResultProjection[], valueOf: (item: UnifiedResultProjection) => string): Record { const counts: Record = {}; items.forEach(item => { const value = valueOf(item); counts[value] = (counts[value] ?? 0) + 1; }); return counts; } function copiedText(value: string | undefined): string | undefined { const trimmed = value?.trim(); return !trimmed || trimmed.toLocaleLowerCase('en-US') === 'none' ? undefined : privacySafeText(trimmed); } function privacySafeText(value: string): string { return value.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, '[redacted-email]'); } function copiedEnvironmentProperty(item: UnifiedResultProjection): string | undefined { if (item.sourceKind !== 'environment-observation' || !item.environmentProperty) return undefined; if (SAFE_ENVIRONMENT_PROPERTIES.has(item.environmentProperty) || item.environmentProperty.endsWith('.row_count')) { return item.environmentProperty; } const aggregateAccountValue = item.environmentValueType === 'number' && ['user-information.account-analysis', 'computer-information.account-analysis'].includes(item.environmentSubjectType ?? ''); return aggregateAccountValue ? item.environmentProperty : undefined; }