Assessor_2.0/tests/features/results-finding-context-exp...

66 lines
5.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @vitest-environment jsdom
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, expect, it, vi } from 'vitest';
import { read, utils } from 'xlsx';
import { FloatingResultsWindow } from '../../src/features/linked-results/FloatingResultsWindow';
import { resultsFindingContext } from '../../src/features/linked-results/results-excel-content';
import { buildResultsWindowExcel } from '../../src/exports/results-window-excel';
import { makeSheet } from '../../src/importers/sheet-factory';
import { downloadBrowserBlob } from '../../src/platform/browser/browser-download-adapter';
vi.mock('../../src/platform/browser/browser-download-adapter', () => ({ downloadBrowserBlob: vi.fn() }));
vi.mock('../../src/app/AppProvider', () => ({ useOptionalApp: () => undefined }));
afterEach(() => { vi.clearAllMocks(); vi.unstubAllGlobals(); delete document.documentElement.dataset.theme; });
const description = 'Checks certificate templates allowing a subjectAltName in the CSR.';
const result = 'Found 17 published templates on which unprivileged users can write properties.';
const impact = 'Controlling templates allows issuance of an arbitrary certificate. References Original reference';
const finding = makeSheet('Synthetic indicator', [['Name','Description','Result Message','Number of Results','Likelihood of Compromise','Result'],
['Certificate templates',description,result,'17',impact,'Failed']], '', 'synthetic.csv').analysis.records[0];
const expected = [['Description','Description',description],['Problem','Result',result],['Problem','Affected','17'],['Problem','Impact',impact]];
async function workbook(blob: Blob) {
const bytes = await new Promise<ArrayBuffer>((resolve,reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as ArrayBuffer); reader.onerror = reject; reader.readAsArrayBuffer(blob); });
return read(bytes,{type:'array'});
}
it.each(['light','dark'])('includes the complete original finding context in actual all and selected downloads (%s)', async theme => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT',true);vi.stubGlobal('ResizeObserver',class { observe() {} disconnect() {} });
document.documentElement.dataset.theme=theme;
const sheets = [makeSheet('Objects',[['Object'],['DUMMY-01'],['DUMMY-02']],'','synthetic.csv'),makeSheet('Second table',[['Other'],['DUMMY-03']],'','synthetic.csv')];
const before=JSON.stringify({finding,sheets});const host=document.createElement('div');document.body.append(host);const root=createRoot(host);
try {
await act(async () => root.render(<FloatingResultsWindow id="context" title="Results – Certificate templates" finding={finding} toolId="purple-knight" outcome="fail" sheets={sheets} color="" zIndex={1} offset={0} search="" onActivate={()=>{}} onClose={()=>{}}/>));
await act(async () => host.querySelector<HTMLButtonElement>('[aria-label="Export entire Results to Excel"]')!.click());
const all=await workbook(vi.mocked(downloadBrowserBlob).mock.calls.at(-1)![0]);
expect(all.SheetNames.slice(0,2)).toEqual(['Results','Results 02']);
expect(utils.sheet_to_json(all.Sheets['Finding context'],{header:1}).slice(2)).toEqual(expected);
expect(all.Sheets['Finding context'].A1.v).toBe('Certificate templates');
await act(async () => host.querySelector<HTMLInputElement>('input[type="checkbox"]')!.click());
await act(async () => host.querySelector<HTMLButtonElement>('[aria-label="Export selected tables to Excel"]')!.click());
const subset=await workbook(vi.mocked(downloadBrowserBlob).mock.calls.at(-1)![0]);
expect(subset.SheetNames).toHaveLength(3);expect(subset.SheetNames[1]).toBe('Finding context');
expect(utils.sheet_to_json(subset.Sheets['Finding context'],{header:1}).slice(2)).toEqual(expected);
expect(subset.SheetNames).not.toContain(all.SheetNames[1]);
expect(JSON.stringify({finding,sheets})).toBe(before);
} finally {await act(async()=>root.unmount());host.remove();}
});
it('preserves long descriptions completely and handles missing context without inventing numbers',()=>{
const long='Long description 🧪 '.repeat(2500);const copy={...finding,fields:finding.fields.map(field=>field.label==='Description'?{...field,value:long}:field)};
const context=resultsFindingContext(copy,'purple-knight',[],'fail');
const wb=read(buildResultsWindowExcel({title:'Text only',text:'Result',tables:[],sourceSections:[],findingContext:context}),{type:'array'});
expect(wb.SheetNames[0]).toBe('Results');
const parts=utils.sheet_to_json<string[]>(wb.Sheets['Long text'],{header:1}).slice(2);
expect(parts.filter(row=>row[0]==='Finding context'&&row[1]==='3'&&row[2]==='3').map(row=>row[4]).join('')).toBe(long);
expect(resultsFindingContext(undefined)).toEqual([]);
const generic={...finding,fields:[{label:'Description',value:'Original',index:0},{label:'Result',value:'Failed',index:1}]};
expect(resultsFindingContext(generic,'unknown')).toEqual([{section:'Description',label:'Description',value:'Original'}]);
});
it('retains the existing GPO problem comparison and backup scope without re-evaluation',()=>{
const policy={...finding,fields:[{label:'Description',value:'Original policy explanation',index:0},{label:'Status',value:'Fail',index:1}],
policyBaselineComparisons:[{baseline:'Synthetic baseline',status:'Fail',summary:'Stored comparison',context:'Backup only',scopeNotice:'Not effective host state',explanation:'Source',remediation:'Original guidance',
recommendations:[{template:'Synthetic',settingId:'s1',comparison:'different',expected:'Disabled',additional:[],missing:[]}]}]};
const before=JSON.stringify(policy);const context=resultsFindingContext(policy,'group-policy',[],'fail');
expect(context).toContainEqual({section:'Description',label:'Description',value:'Original policy explanation'});
expect(context).toContainEqual({section:'Problem',label:'Expected',value:'Disabled'});
expect(context.some(row=>row.label==='Affected')).toBe(false);
expect(JSON.stringify(policy)).toBe(before);
});