84 lines
3.6 KiB
TypeScript
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);
|
|
}
|
|
|