Assessor_2.0/src/features/project-import/policy-observations-to-data...

143 lines
7.9 KiB
TypeScript

import type { ImportedDataset } from '../../domain/dataset';
import type { UnifiedResultProjection } from '../../domain/unified-result';
import { POLICY_EVALUATION_ENGINE, type StoredPolicyEvaluation } from '../../domain/policy-evaluation';
import { buildPolicyWorkResults } from '../../evaluation/policy-work-results';
import { rowsToCsv } from '../../exports/csv-export';
import { makeSheet } from '../../importers/sheet-factory';
import { buildPolicyGuidance, type PolicyGuidanceMap } from '../../profiles/group-policy/guidance';
import { formatPolicySources } from '../../profiles/group-policy/source-presentation';
import { buildPolicyConflictPresentation, buildPolicyDuplicatePresentation, serializePolicyConflictPresentation, POLICY_CONFLICT_DETAILS, POLICY_DUPLICATE_DETAILS } from '../../profiles/group-policy/conflict-presentation';
function display(value: unknown): string {
if (value === undefined) return '';
if (value === null) return 'null';
return typeof value === 'object' ? JSON.stringify(value) : String(value);
}
export function appendPolicyObservationsToDataset(
dataset: ImportedDataset | null,
projectName: string,
results: UnifiedResultProjection[],
evaluations: StoredPolicyEvaluation[] = [],
guidance: PolicyGuidanceMap = buildPolicyGuidance(evaluations)
): ImportedDataset | null {
const entries = buildPolicyWorkResults(results, evaluations);
if (!entries.length) return dataset;
const previousVersions = new Map(evaluations.filter(evaluation => evaluation.run.evaluator?.version
&& evaluation.run.evaluator.version !== POLICY_EVALUATION_ENGINE.version)
.flatMap(evaluation => evaluation.results.map(result => [result.id, evaluation.run.evaluator.version] as const)));
const headers = [
'Title', 'Category', 'Status', 'Scope', 'Configured Value', 'Baseline value', 'Baseline',
'Comparison', 'Registry path', 'Sources', 'Technical Identity', 'Imported', 'Diagnostics', 'Policy type', 'Policy section',
'Description', 'Remediation', 'Description source', 'Conflict', POLICY_CONFLICT_DETAILS, 'Duplicates', POLICY_DUPLICATE_DETAILS,
'Evaluated', 'Source: Scan', 'Evaluation readiness'
];
const policyEntries = entries.map(entry => {
const comparison = entry.comparison;
const previousVersion = comparison && previousVersions.get(comparison.id);
const readiness = previousVersion
? `Re-evaluation required: showing saved results from engine ${previousVersion}; current engine is ${POLICY_EVALUATION_ENGINE.version}.`
: '';
const conflict = buildPolicyConflictPresentation(entry);
const duplicate = buildPolicyDuplicatePresentation(entry);
const configured = comparison?.customerState === 'missing' ? 'Not configured'
: comparison?.comparator.actual ?? entry.sources.flatMap(source => source.policyValues ?? []);
const target = comparison?.identity.target;
const rawRegistry = entry.sources.flatMap(source => source.policyValues ?? [])
.find(value => value && typeof value === 'object' && !Array.isArray(value) && value.kind === 'registry-policy');
const registryPath = target && 'hive' in target && target.hive ? `${target.hive}\\${target.key}`
: rawRegistry && typeof rawRegistry === 'object' && !Array.isArray(rawRegistry)
? `${entry.scope === 'user' ? 'HKEY_CURRENT_USER' : 'HKEY_LOCAL_MACHINE'}\\${rawRegistry.key}` : '';
return {
importedAt: entry.importedAt,
row: [
entry.title, 'GPOs', entry.status, entry.scope, display(configured),
display(comparison?.comparator.baseline), comparison?.baselineLabel ?? '',
comparison?.baselineTemplateSelection?.status === 'no-match'
? 'No baseline template matches the specified comparison context'
: comparison?.diagnosticCodes.includes('policy-evaluation-template-context-required')
? 'Different template recommendations; customer target role not established'
: comparison?.diagnosticCodes.includes('policy-evaluation-baseline-consensus-conflict')
? 'Conflicting baseline recommendations'
: entry.status === 'Conflicts' ? 'Different values or types for the same setting'
: entry.status === 'Duplicates' ? 'Identical setting in multiple source entries'
: comparison?.customerState === 'missing' ? 'Baseline setting is not configured'
: entry.status === 'Pass' ? 'Matches baseline' : 'Differs from baseline',
registryPath,
formatPolicySources(entry.sources),
entry.identity, entry.importedAt,
[...new Set([...(comparison?.diagnosticCodes ?? []),
...entry.sources.flatMap(source => source.policyDiagnosticCodes ?? [])])].join(', '),
target?.kind ?? entry.sources[0]?.policyDeliveryMechanism ?? '', target && 'section' in target ? target.section : '',
comparison ? [readiness, guidance.get(comparison.id)?.description || 'Not available in imported baseline metadata.'].filter(Boolean).join('\n\n') : '',
comparison ? guidance.get(comparison.id)?.remediation ?? '' : '',
comparison ? guidance.get(comparison.id)?.source ?? '' : '',
conflict?.summary ?? '', conflict ? serializePolicyConflictPresentation(conflict) : '',
duplicate?.summary ?? '', duplicate ? serializePolicyConflictPresentation(duplicate) : '',
entry.evaluatedAt ?? '', entry.importedAt ? `GPOs · ${entry.importedAt}` : 'GPOs', readiness
]
};
});
const policyRows = policyEntries.map(entry => entry.row);
const rows = [headers, ...policyRows];
const sheet = makeSheet(
'GPOs',
rows,
rowsToCsv(rows),
projectName,
{ id: `gpo-policy-observations-${projectName}`, sourceFile: projectName, sourceSheet: 'GPOs', toolId: 'group-policy' }
);
if (!dataset) {
const lastModified = Math.max(
...policyEntries.map(entry => Date.parse(entry.importedAt)).filter(Number.isFinite),
0
);
return {
fileName: `${projectName} · GPOs`,
fileSize: 0,
lastModified,
format: 'GPO Backup',
rawText: '',
sheets: [sheet],
profileId: 'generic',
profileVersion: 'gpo-work-comparison-1.2.1',
scanTool: 'GPOs'
};
}
const [first, ...rest] = dataset.sheets;
const aggregate = first?.isProjectSheet ? (() => {
const sourceHeaders = first.rows[0] ?? [];
const aggregateHeaders = [...sourceHeaders];
for (const header of headers) if (!aggregateHeaders.includes(header)) aggregateHeaders.push(header);
const projectRows = first.rows.slice(1).map(row => {
const source = new Map(sourceHeaders.map((header, index) => [header, row[index] ?? '']));
return aggregateHeaders.map(header => source.get(header) ?? '');
});
const aggregatePolicyRows = policyRows.map(row => {
const source = new Map(headers.map((header, index) => [header, row[index] ?? '']));
source.set('Source: File', projectName);
source.set('Source: Sheet', 'GPOs');
return aggregateHeaders.map(header => source.get(header) ?? '');
});
const aggregateRows = [aggregateHeaders, ...projectRows, ...aggregatePolicyRows];
const combined = makeSheet(first.name, aggregateRows, rowsToCsv(aggregateRows), first.sourceFile ?? projectName, {
id: first.id,
sourceFile: first.sourceFile,
sourceSheet: first.sourceSheet,
isProjectSheet: true
});
// Keep exact scanner provenance when policy rows extend the aggregate.
combined.analysis.records = combined.analysis.records.map((record, index) => {
const original = first.analysis.records[index];
return original ? { ...record, findingOccurrenceId: original.findingOccurrenceId,
findingDefinitionId: original.findingDefinitionId, scanRunId: original.scanRunId, importFileId: original.importFileId } : record;
});
return combined;
})() : first;
return {
...dataset,
sheets: aggregate ? [aggregate, sheet, ...rest] : [sheet],
scanTool: dataset.scanTool === 'GPOs' ? dataset.scanTool : 'Multiple Tools'
};
}