Assessor_2.0/src/features/defender-for-iot/defender-dataset-model.ts

84 lines
3.6 KiB
TypeScript

import type { JsonValue } from '../../domain/canonical-model';
import {
buildDefenderVulnerabilityModel,
defenderTableRole,
type DefenderSourceRow,
type DefenderSourceTable,
type DefenderVulnerabilityModel
} from '../../domain/defender-for-iot';
import type { DatasetSheet, ImportedDataset } from '../../domain/dataset';
function recordFields(sheet: DatasetSheet, recordIndex: number): Record<string, JsonValue> {
const record = sheet.analysis.records[recordIndex];
return Object.fromEntries(record.fields.map(field => [field.label, field.value]));
}
function parsedObservations(fields: Record<string, JsonValue>): Array<Record<string, JsonValue>> | undefined {
const source = Object.entries(fields).find(([label]) => /^Source Observations$/i.test(label.trim()))?.[1];
if (typeof source !== 'string' || !source.trim()) return undefined;
try {
const parsed: unknown = JSON.parse(source);
return Array.isArray(parsed) && parsed.every(item => item && typeof item === 'object' && !Array.isArray(item))
? parsed as Array<Record<string, JsonValue>>
: undefined;
} catch {
return undefined;
}
}
function observationRows(sheet: DatasetSheet, tableIndex: number): DefenderSourceRow[] | undefined {
const expanded = sheet.analysis.records.flatMap((record, recordIndex) => {
const observations = parsedObservations(recordFields(sheet, recordIndex));
if (!observations) return [];
return observations.map((observation, observationIndex): DefenderSourceRow => {
const source = observation.source && typeof observation.source === 'object' && !Array.isArray(observation.source)
? observation.source as Record<string, JsonValue>
: {};
const additional = observation.additionalSourceFields && typeof observation.additionalSourceFields === 'object' && !Array.isArray(observation.additionalSourceFields)
? observation.additionalSourceFields as Record<string, JsonValue>
: {};
return {
tableIndex: typeof source.tableIndex === 'number' ? source.tableIndex : tableIndex,
rowIndex: typeof source.rowIndex === 'number' ? source.rowIndex : record.index + observationIndex,
fields: {
Name: observation.name ?? '', IP: observation.ip ?? '', Site: observation.site ?? '', Sensor: observation.sensor ?? '',
CVE: observation.cve ?? '', Severity: observation.severity ?? '', NULL: additional.NULL ?? null,
Beschreibung: observation.description ?? '', ...additional
}
};
});
});
return expanded.length ? expanded : undefined;
}
function sourceTable(sheet: DatasetSheet, index: number): DefenderSourceTable | undefined {
const tableIndex = sheet.sourceTableIndex ?? sheet.workbookIndex ?? index;
const expanded = observationRows(sheet, tableIndex);
if (expanded) return {
tableIndex,
name: sheet.sourceSheet ?? sheet.name,
headers: ['Name', 'IP', 'Site', 'Sensor', 'CVE', 'Severity', 'NULL', 'Beschreibung'],
rows: expanded
};
if (!defenderTableRole(sheet.analysis.headers)) return undefined;
return {
tableIndex,
name: sheet.sourceSheet ?? sheet.name,
headers: sheet.analysis.headers,
rows: sheet.analysis.records.map((record, recordIndex) => ({
tableIndex,
rowIndex: record.index,
fields: recordFields(sheet, recordIndex)
}))
};
}
export function defenderModelForDataset(dataset: ImportedDataset): DefenderVulnerabilityModel {
const tables = dataset.sheets
.filter(sheet => !sheet.isProjectSheet)
.map(sourceTable)
.filter((table): table is DefenderSourceTable => Boolean(table));
return buildDefenderVulnerabilityModel(tables);
}