78 lines
5.5 KiB
TypeScript
78 lines
5.5 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { describe, expect, it } from 'vitest';
|
|
import * as XLSX from 'xlsx';
|
|
import { importFile } from '../../src/importers/importer';
|
|
import { importProfiles } from '../../src/profiles/profile-catalog';
|
|
import { previewEntryFromDataset, confirmEntryMappings } from '../../src/features/import-workflow/import-workflow';
|
|
import { createCanonicalImportBatch } from '../../src/importers/canonical-import';
|
|
import { maesterClientInformation } from '../../src/importers/maester-client-information';
|
|
import { cloudInformationFacts } from '../../src/domain/cloud-information';
|
|
import { assertCanonicalAssessmentModel } from '../../src/domain/canonical-validation';
|
|
import { maesterDefinition } from '../../src/profiles/maester/maester-definition';
|
|
import type { JsonValue } from '../../src/domain/canonical-model';
|
|
|
|
const proof = 'Your tenant is configured as **disabled**.\n\nThe recommended value is **\'enabled\'** for **policies/authenticationMethodsPolicy**\n\n| Method | State |\n| --- | --- |\n| FIDO2 | disabled |';
|
|
const rows = [['ID', 'Title', 'Severity', 'Status', 'Result'],
|
|
['EIDSCA.AF01', 'Synthetic authentication method', 'High', 'Failed', proof],
|
|
['MT.1032', 'Synthetic roles', 'Medium', 'Passed', 'Native role: synthetic-admin; assignment: eligible.'],
|
|
['MT.9999', 'Unknown synthetic test', 'Low', 'Skipped', 'No configuration claim'],
|
|
['EIDSCA.AP08', 'Synthetic consent', 'High', 'Error', 'Access denied by source']];
|
|
|
|
async function modelFor(file: File) {
|
|
const dataset = await importFile(file, { profiles: importProfiles });
|
|
const entry = previewEntryFromDataset(dataset, { sha256: 'b'.repeat(64), fileName: file.name, sizeBytes: file.size, lastModified: 0 });
|
|
const model = createCanonicalImportBatch('synthetic-client-information', [confirmEntryMappings(entry, importProfiles)], '2026-10-04T10:00:00Z').model;
|
|
assertCanonicalAssessmentModel(model);
|
|
return { dataset, model };
|
|
}
|
|
|
|
describe('Maester Client Information through the generic import pipeline', () => {
|
|
it.each(['HTML', 'CSV', 'Excel'])('retains exact native evidence and finding outcomes from %s without inventing effective settings', async format => {
|
|
let file: File;
|
|
if (format === 'HTML') {
|
|
const payload = { TenantId: 'synthetic-tenant', TenantName: 'Synthetic tenant', ExecutedAt: '2026-10-01T10:00:00Z', Result: 'Failed',
|
|
Tests: rows.slice(1).map(row => ({ Id: row[0], Title: row[1], Severity: row[2], Result: row[3], ResultDetail: { TestResult: row[4] } })) };
|
|
file = new File([`<html><title>Maester Security Tests</title><script type="application/json">${JSON.stringify(payload)}</script></html>`], 'Maester.html');
|
|
} else if (format === 'CSV') {
|
|
file = new File([rows.map(row => row.map(value => `"${value.replaceAll('"', '""')}"`).join(',')).join('\n')], 'Maester.csv');
|
|
} else {
|
|
const workbook = XLSX.utils.book_new();
|
|
XLSX.utils.book_append_sheet(workbook, XLSX.utils.aoa_to_sheet(rows), 'Tests');
|
|
file = new File([XLSX.write(workbook, { type: 'array', bookType: 'xlsx' })], 'Maester.xlsx');
|
|
}
|
|
const { dataset, model } = await modelFor(file);
|
|
expect(dataset.profileId).toBe('maester');
|
|
expect(model.findingOccurrences.map(item => item.status.originalValue)).toEqual(['Failed', 'Passed', 'Skipped', 'Error']);
|
|
expect(model.findingOccurrences.map(item => item.status.normalizedValue)).toEqual(['failed', 'passed', 'not-run', 'error']);
|
|
const cloud = model.environmentObservations!.filter(item => item.property.startsWith('cloud.'));
|
|
expect(cloud).toHaveLength(3);
|
|
expect(cloud[0]).toMatchObject({ property: 'cloud.authentication.eidsca.af01.reported_evidence', originalValue: proof, state: 'observed',
|
|
attributes: { inventoryComplete: false, informationKind: 'reported-test-evidence', sourceRuleId: 'EIDSCA.AF01' } });
|
|
expect(cloud[0].normalizedValue).toBeUndefined();
|
|
expect(cloudInformationFacts({ observations: cloud })).toMatchObject([{ value: 'disabled', label: 'FIDO2', observationId: cloud[0].id }]);
|
|
expect(cloud[2].state).toBe('not-assessed');
|
|
expect(cloud.every(item => item.scanRunId && item.importFileId)).toBe(true);
|
|
if (format === 'HTML') {
|
|
expect(cloud[0]).toMatchObject({ subject: { identity: 'synthetic-tenant' }, observedAt: '2026-10-01T10:00:00.000Z',
|
|
source: { objectPath: '$.Tests[0].ResultDetail.TestResult' } });
|
|
} else {
|
|
expect(cloud[0].subject.identity).toBeUndefined();
|
|
expect(cloud[0].source).toMatchObject({ rowIndex: 1, columnIndex: 4 });
|
|
}
|
|
expect(model.reportSections!.some(section => section.tables.some(table => table.rows.some(row => row[0] === 'FIDO2' && row[1] === 'disabled')))).toBe(true);
|
|
expect(maesterDefinition.securityScoring?.native.kind).toBe('unavailable');
|
|
});
|
|
|
|
it('requires exact supported IDs and explicit non-status source evidence, retaining access errors as unassessed', () => {
|
|
const root: Record<string, JsonValue> = { Tests: [
|
|
{ Id: 'EIDSCA.AF01', Title: 'Known without evidence', Result: 'Failed' },
|
|
{ Id: 'EIDSCA.AF02', Result: 'Passed', ResultDetail: { TestResult: 'Passed' } },
|
|
{ Id: 'unknown', Title: 'FIDO2 is enabled', Result: 'Failed', ResultDetail: { TestResult: proof } },
|
|
{ Id: 'MT.1005', Result: 'Skipped', ResultDetail: { TestResult: 'Test was not executed.' } }
|
|
] };
|
|
const information = maesterClientInformation('synthetic.html', root, '$', 0);
|
|
expect(information.observations).toHaveLength(1);
|
|
expect(information.observations[0]).toMatchObject({ state: 'not-assessed', originalValue: 'Test was not executed.' });
|
|
});
|
|
});
|