119 lines
4.5 KiB
TypeScript
119 lines
4.5 KiB
TypeScript
import type { UnifiedResultProjection } from '../domain/unified-result';
|
|
|
|
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 {
|
|
const findings = items.filter(item => item.sourceKind !== 'environment-observation');
|
|
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,
|
|
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'),
|
|
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'
|
|
};
|
|
}),
|
|
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 groupedEnvironmentFacts(items: UnifiedResultProjection[]) {
|
|
const grouped = new Map<string, {
|
|
title: string;
|
|
property: string;
|
|
value: unknown;
|
|
state: string;
|
|
tool: string;
|
|
count: number;
|
|
}>();
|
|
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<string, number> {
|
|
const counts: Record<string, number> = {};
|
|
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;
|
|
}
|