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

220 lines
13 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 { baselineVariantSummary } from '../../evaluation/policy-baseline-variants';
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 { policySourceEvidence } from '../../profiles/group-policy/source-evidence';
import { policyBackupImpactSources } from './policy-backup-impact-sources';
import { rollupPolicyWorkRows } from './policy-work-row-rollup';
import { stableRecordId } from '../../shared/identifiers';
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);
}
const preferenceCseLabels: Record<string, string> = {
registry: 'registry', 'local-users-groups': 'local users and groups',
services: 'services', 'drive-maps': 'drive maps'
};
/**
* A Group Policy Preference has no comparable technical policy setting. The retained
* action and item-level targeting decide whether and how it applies, so both stay visible
* instead of an implied baseline comparison.
*/
function notComparableSummary(sources: UnifiedResultProjection[]): string {
const value = <T,>(pick: (source: UnifiedResultProjection) => T | undefined) =>
[...new Set(sources.flatMap(source => pick(source) !== undefined ? [String(pick(source))] : []))].sort();
const cse = value(source => source.policyPreferenceCse).map(item => preferenceCseLabels[item] ?? item);
const action = value(source => source.policyPreferenceAction);
const targeting = value(source => source.policyPreferenceTargeting);
return [
cse.length ? `Group Policy Preference (${cse.join(', ')})` : 'Group Policy Preference',
'no comparable technical policy setting, so it is not compared with a Microsoft baseline',
action.length ? `action: ${action.join(', ')}` : '',
targeting.includes('present') ? 'item-level targeting: present, effective application unknown'
: targeting.length ? 'item-level targeting: none' : ''
].filter(Boolean).join('. ') + '.';
}
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', 'Historical result'
];
const policyEntries = rollupPolicyWorkRows(entries.map(entry => {
const comparison = entry.comparison;
const previousVersion = comparison && previousVersions.get(comparison.id);
const readiness = previousVersion
? `Re-evaluation required: historical result from engine ${previousVersion}; current engine is ${POLICY_EVALUATION_ENGINE.version}. Re-evaluate in Data Hub before using this result as a current assessment.`
: '';
const conflict = buildPolicyConflictPresentation(entry);
const duplicate = buildPolicyDuplicatePresentation(entry);
const configured = comparison?.comparator.actual
?? (comparison?.customerState === 'missing' ? 'Not configured'
: 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 {
entry,
historical: Boolean(previousVersion),
importedAt: entry.importedAt,
failingBackupSources: policyBackupImpactSources(entry),
sourceEvidence: policySourceEvidence(entry.sources,
previousVersion ? undefined : comparison?.baselineVariantAssessment),
row: [
entry.title, 'GPOs', previousVersion ? 'Review required' : entry.status, entry.scope, display(configured),
display(comparison?.comparator.baseline ?? comparison?.baselineVariantAssessment?.variants.map(variant => ({
settingId: variant.settingId, value: variant.comparator.baseline, relation: variant.comparator.relation
}))), comparison?.baselineLabel ?? '',
previousVersion ? 'Historical comparison — re-evaluation required'
: comparison?.baselineVariantAssessment ? baselineVariantSummary(comparison.baselineVariantAssessment)
: 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 === 'Not comparable' ? notComparableSummary(entry.sources)
: entry.status === 'No baseline rule'
? 'No applicable Microsoft baseline rule for this technical setting; retained for inspection without a verdict'
: entry.conflict || entry.status === 'Conflicts' ? 'Different values or types for the same setting'
: entry.duplicate || 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
: preferenceCseLabels[entry.sources[0]?.policyPreferenceCse ?? ''] ?? entry.sources[0]?.policyPreferenceCse ?? '',
comparison ? guidance.get(comparison.id)?.description || (guidance.get(comparison.id)?.descriptionLoadFailed
? 'Baseline description could not be loaded. The saved comparison is retained.'
: 'Description unavailable in the referenced baseline metadata.') : '',
comparison ? guidance.get(comparison.id)?.remediation ?? '' : '',
comparison ? guidance.get(comparison.id)?.source ?? '' : '',
conflict?.summary ?? (comparison?.baselineVariantAssessment && entry.conflict ? 'Conflicting customer states; see original GPO source instructions.' : ''), conflict ? serializePolicyConflictPresentation(conflict) : '',
duplicate?.summary ?? (comparison?.baselineVariantAssessment && entry.duplicate ? 'Same setting contributed by multiple GPO backups.' : ''), duplicate ? serializePolicyConflictPresentation(duplicate) : '',
entry.evaluatedAt ?? '', entry.importedAt ? `GPOs · ${entry.importedAt}` : 'GPOs', readiness,
previousVersion ? `${entry.status} · Engine ${previousVersion}` : ''
]
};
}), headers);
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' }
);
sheet.analysis.records.forEach((record, index) => {
record.policyFailingBackupSources = policyEntries[index].failingBackupSources;
record.policySourceEvidence = policyEntries[index].sourceEvidence;
record.policyBaselineLabels = policyEntries[index].baselineLabels;
if (policyEntries[index].reviewMembers) {
record.id = `gpo-work-setting:${encodeURIComponent(policyEntries[index].entry.settingKey!)}`;
record.policyReviewMembers = policyEntries[index].reviewMembers!.map(member => ({ baseline: member.baseline,
id: stableRecordId({ sourceFile: projectName, sheetName: 'GPOs', headers, row: member.row }) }));
}
});
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];
const policyIndex = index - first.analysis.records.length;
const policyRecord = sheet.analysis.records[policyIndex];
if (!original && policyRecord?.policyReviewMembers) {
for (const member of policyEntries[policyIndex].reviewMembers ?? []) {
const values = new Map(headers.map((header, i) => [header, member.row[i] ?? '']));
values.set('Source: File', projectName);
values.set('Source: Sheet', 'GPOs');
policyRecord.policyReviewMembers.push({ baseline: member.baseline, id: stableRecordId({
sourceFile: first.sourceFile ?? projectName, sheetName: first.name, headers: aggregateHeaders,
row: aggregateHeaders.map(header => values.get(header) ?? '')
}) });
}
}
return original ? { ...record, findingOccurrenceId: original.findingOccurrenceId,
findingDefinitionId: original.findingDefinitionId, scanRunId: original.scanRunId, importFileId: original.importFileId,
policyFailingBackupSources: original.policyFailingBackupSources, policySourceEvidence: original.policySourceEvidence,
policyBaselineLabels: original.policyBaselineLabels, policyReviewMembers: original.policyReviewMembers }
: { ...record, ...(policyRecord?.policyReviewMembers ? { id: policyRecord.id, policyReviewMembers: policyRecord.policyReviewMembers } : {}),
policyFailingBackupSources: policyEntries[index - first.analysis.records.length]?.failingBackupSources,
policySourceEvidence: policyEntries[index - first.analysis.records.length]?.sourceEvidence,
policyBaselineLabels: policyEntries[index - first.analysis.records.length]?.baselineLabels };
});
return combined;
})() : first;
return {
...dataset,
sheets: aggregate ? [aggregate, sheet, ...rest] : [sheet],
scanTool: dataset.scanTool === 'GPOs' ? dataset.scanTool : 'Multiple Tools'
};
}