import type { AssessorRepository } from '../../persistence/assessor-repository'; import type { ReportDiagnostic, ReportProjectionInput, ReportProvenanceEntry, ReportScoreSource } from '../../domain/report-projection'; import { loadCanonicalProjectWorkspace } from '../project-import/load-canonical-project-workspace'; import { queryAllUnifiedResults } from '../../exports/unified-result-jsonl'; import { hydrateWorkspaceReviews } from '../review-workspace/database-review'; import { dashboardScoreFromStates, type DashboardResultState } from '../dashboard/dashboard-result-state'; import { securitySourceScores, weightedSecurityScore } from '../../domain/security-scoring'; import { securityToolId } from '../../scoring/canonical-security-score'; import { clientReportInput } from '../dashboard/client-report-projection'; export interface ReportAssemblyScope { projectId: string; assessmentId?: string; assessmentIds?: string[]; scanRunIds?: string[]; generatedAt: string; } /** Repository-only, export-time assembly. No renderer derives a verdict or reads storage. */ export async function assembleReportProjectionInput( repository: AssessorRepository, scope: ReportAssemblyScope ): Promise { const project = await repository.getProject(scope.projectId); if (!project) throw new Error('Project not found.'); const assessmentIds = scope.assessmentIds ?? (scope.assessmentId ? [scope.assessmentId] : undefined); const workspace = await loadCanonicalProjectWorkspace({ listProjectAssessments: projectId => repository.listAssessments(projectId), loadAssessmentModel: async assessmentId => { const model = await repository.loadAssessmentModel(assessmentId); if (!model) throw new Error(`Assessment ${assessmentId} not found.`); return model; }, queryUnifiedResults: query => repository.queryUnifiedResults(query), listPolicyEvaluations: async projectId => ({ records: await repository.listPolicyEvaluations(projectId) }), listMicrosoftSecurityBaselineAnalyses: async baselineId => ({ records: await repository.listMicrosoftSecurityBaselineAnalyses(baselineId) }) }, scope.projectId, scope.scanRunIds, assessmentIds); if (!workspace.dataset) throw new Error('No reportable assessment data in the selected scope.'); const [results, snapshot, storedReviews, processing, analyses, aggregations, evaluations] = await Promise.all([ queryAllUnifiedResults(query => repository.queryUnifiedResults(query), { audience: 'internal-analysis', projectId: scope.projectId, assessmentIds: workspace.assessmentIds, scanRunIds: scope.scanRunIds, sourceKind: ['scanner-finding', 'policy-evaluation'] }), repository.getEnvironmentSnapshot({ projectId: scope.projectId, assessmentId: scope.assessmentId, scanRunIds: scope.scanRunIds }), repository.listReviews(scope.projectId), repository.listProjectProcessingRuns(scope.projectId), repository.listGpoPolicyAnalyses(scope.projectId), repository.listPolicySnapshotAggregations(scope.projectId), repository.listPolicyEvaluations(scope.projectId) ]); const dataset = workspace.dataset; const selectedObservationIds = new Set(results.filter(result => result.sourceKind === 'policy-observation') .map(result => result.sourceResultId)); const scopedAnalyses = analyses.map(record => ({ ...record, resolutions: record.resolutions.filter(item => selectedObservationIds.has(item.observationId)) })).filter(record => record.resolutions.length > 0); const selectedEvaluationRunIds = new Set(results.flatMap(result => result.evaluationRunId ? [result.evaluationRunId] : [])); const scopedEvaluations = evaluations.filter(record => selectedEvaluationRunIds.has(record.run.id) || (selectedObservationIds.size > 0 && record.results.length > 0 && record.results.some(result => result.observationIds.some(id => selectedObservationIds.has(id))) && record.results.every(result => result.observationIds.every(id => selectedObservationIds.has(id))))); const selectedAggregationRunIds = new Set(scopedEvaluations.map(record => record.run.aggregationRunId)); const scopedAggregations = aggregations.filter(record => selectedAggregationRunIds.has(record.run.id)); const selectedArtifactIds = new Set(results.flatMap(result => result.importArtifactId ? [result.importArtifactId] : [])); const scopedProcessing = processing.filter(run => selectedArtifactIds.has(run.artifactId)); const reviews = hydrateWorkspaceReviews(dataset, storedReviews, {}).reviews; const preliminary = clientReportInput({ project, assessmentIds: workspace.assessmentIds, generatedAt: scope.generatedAt, results, dataset, environment: snapshot, reviews }); const ratingByTool = new Map(securitySourceScores(dataset.securityScoring?.scans ?? []).map(item => [item.toolId, item])); const resultsByTool = new Map(); results.forEach(result => { const toolId = securityToolId(result.sourceTool ?? 'unknown'); const group = resultsByTool.get(toolId) ?? []; group.push(result); resultsByTool.set(toolId, group); }); for (const toolId of ratingByTool.keys()) if (!resultsByTool.has(toolId)) resultsByTool.set(toolId, []); const scoreSources: ReportScoreSource[] = [...resultsByTool].map(([toolId, items]) => { const rating = ratingByTool.get(toolId); const states: DashboardResultState[] = toolId === 'group-policy' ? (preliminary.policyEntries ?? []).map(entry => entry.verdict === 'Pass' ? 'passed' : entry.verdict === 'Fail' ? 'deviation' : 'unknown') : items.map(item => item.outcomeNormalized === 'pass' ? 'passed' : item.isDeviation ? 'deviation' : item.outcomeNormalized === 'not_run' ? 'not-run' : item.outcomeNormalized === 'not_applicable' ? 'not-applicable' : 'unknown'); const counted = dashboardScoreFromStates(states); return { toolId, score: rating?.score ?? (rating ? undefined : counted.score), method: rating?.methods.length === 1 ? rating.methods[0] : rating ? 'unavailable' : counted.assessed ? 'pass-rate' : 'unavailable', weight: rating?.weight ?? dataset.securityScoring?.policy?.weights[toolId] ?? dataset.securityScoring?.policy?.defaultWeight ?? 1, nativeValue: rating?.scans.length === 1 ? rating.scans[0].nativeValue : undefined, nativeProperty: rating?.scans.length === 1 ? rating.scans[0].nativeProperty : undefined, passed: counted.passed, deviations: counted.deviations, assessed: counted.assessed, observedAt: rating?.scans.map(scan => scan.observedAt).filter((date): date is string => Boolean(date)).sort().at(-1), issues: rating?.issues ?? [] }; }); const input = { ...preliminary, scoreSources, overallScore: weightedSecurityScore(scoreSources) }; const diagnosticMap = new Map(); const addDiagnostic = (code: string, origin: ReportDiagnostic['origin'], reason?: string) => { const key = `${origin}\u0000${code}\u0000${reason ?? ''}`; const previous = diagnosticMap.get(key); diagnosticMap.set(key, { code, origin, count: (previous?.count ?? 0) + 1, reason }); }; workspace.models.forEach(model => model.diagnostics.forEach(item => addDiagnostic(item.code, 'import', item.message))); scopedProcessing.forEach(run => run.diagnosticCodes.forEach(code => addDiagnostic(code, 'import'))); const seenResolverObservations = new Set(); scopedAnalyses.forEach(record => record.resolutions.forEach(item => { if (item.resolution.status !== 'unresolved' || seenResolverObservations.has(item.observationId)) return; seenResolverObservations.add(item.observationId); addDiagnostic(item.resolution.reasonCode, 'analysis', item.resolution.reasonCode); })); scopedAggregations.forEach(record => record.run.diagnosticCodes.forEach(code => addDiagnostic(code, 'aggregation'))); scopedEvaluations.forEach(record => record.run.diagnosticCodes.forEach(code => addDiagnostic(code, 'evaluation'))); const provenance: ReportProvenanceEntry[] = [ ...input.provenance ?? [], ...workspace.models.flatMap(model => model.importFiles.map(file => ({ kind: `ImportFile: ${file.fileName}`, id: file.id, version: [file.processedWith.profileVersion, file.processedWith.mappingVersion, file.processedWith.normalizationVersion].filter(Boolean).join(' / '), observedAt: file.importedAt }))), ...scopedProcessing.map(run => ({ kind: `ProcessingRun: ${run.status}`, id: run.id, version: run.processors.map(item => `${item.id}@${item.version}`).join(', '), observedAt: run.completedAt })), ...scopedAnalyses.map(record => ({ kind: `PolicyAnalysisRun: ${record.run.status}`, id: record.run.id, version: `${record.run.analyzer.id}@${record.run.analyzer.version}; ${record.run.identityResolver.id}@${record.run.identityResolver.version}`, observedAt: record.run.completedAt })), ...scopedAggregations.map(record => ({ kind: 'AggregationRun', id: record.run.id, observedAt: record.run.completedAt })), ...scopedEvaluations.map(record => ({ kind: `EvaluationRun: ${record.run.status}`, id: record.run.id, version: record.run.evaluator.version, observedAt: record.run.completedAt })) ]; return { ...input, diagnostics: [...diagnosticMap.values()], provenance }; }