47 lines
4.6 KiB
JavaScript
47 lines
4.6 KiB
JavaScript
// Synthetic browser check of the actual Results window; no datastore mutation.
|
||
import assert from 'node:assert/strict';
|
||
import { readFile } from 'node:fs/promises';
|
||
import { createServer } from 'node:http';
|
||
import { pathToFileURL } from 'node:url';
|
||
import { build } from 'esbuild';
|
||
const { chromium } = await import(pathToFileURL(process.env.PLAYWRIGHT_MODULE).href);
|
||
const main = await readFile('src/main.tsx', 'utf8');
|
||
const styles = [...main.matchAll(/^import '([^']+\.css)';/gm)].map(([, path]) =>
|
||
`import ${JSON.stringify(path.startsWith('.') ? './src/' + path.slice(2) : path)};`).join('\n');
|
||
const bundle = await build({ stdin: { resolveDir: process.cwd(), loader: 'tsx', contents: `${styles}
|
||
import React from 'react'; import {createRoot} from 'react-dom/client';
|
||
import {FloatingResultsWindow} from './src/features/linked-results/FloatingResultsWindow';
|
||
import {makeSheet} from './src/importers/sheet-factory';
|
||
const finding=makeSheet('Synthetic',[['Title','Status','Source Rule ID','Verwandte Daten'],
|
||
['Synthetic SMBv1 finding','Matched','S-SMB-v1','synthetic::rule-details-S-SMB-v1']], '', 'synthetic.html').analysis.records[0];finding.importFileId='synthetic';
|
||
const own=makeSheet('PingCastle · Details',[['Domain controller'],['DUMMY-DC']], '', 'synthetic.html',{id:'pingcastle-section-synthetic::rule-details-S-SMB-v1'});
|
||
const inventory=makeSheet('PingCastle · Inventory',[['Domain controller','SMB v1'],['DUMMY-OTHER','NO']], '', 'synthetic.html');
|
||
const root=createRoot(document.getElementById('root'));
|
||
window.renderSynthetic=tool=>root.render(<FloatingResultsWindow id="synthetic" title="Results – Synthetic evidence" finding={finding}
|
||
toolId={tool} outcome="fail" sheets={tool==='pingcastle'?[own,inventory]:[]}
|
||
sections={tool==='maester'?[{title:'Test result',value:'| Object | Status |\\n| --- | --- |\\n| DUMMY-BAD | ❌ Failed |\\n| DUMMY-GOOD | ✅ Passed |\\n| DUMMY-UNKNOWN | Investigate |'}]:[]}
|
||
color="" zIndex={1} offset={0} search="" onActivate={()=>{}} onClose={()=>{}}/>);
|
||
window.renderSynthetic('pingcastle');` }, jsx:'automatic',bundle:true,write:false,outdir:'synthetic',format:'iife',
|
||
loader:{'.woff2':'dataurl','.woff':'dataurl','.png':'dataurl','.svg':'dataurl'} });
|
||
const server=createServer((req,res)=>{const file=bundle.outputFiles.find(file=>req.url==='/'+file.path.split(/[\\/]/).at(-1));
|
||
res.setHeader('Content-Type',file?(req.url.endsWith('.css')?'text/css':'text/javascript'):'text/html');
|
||
res.end(file?.contents??'<!doctype html><html><head><meta charset="utf-8"><link rel="stylesheet" href="/stdin.css"></head><body><div id="root"></div><script src="/stdin.js"></script></body></html>');});
|
||
await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));let browser;
|
||
try { browser=await chromium.launch({headless:true,channel:'chrome'});const page=await browser.newPage({viewport:{width:1280,height:900}});
|
||
page.on('pageerror', error=>console.error(error.message));
|
||
await page.goto('http://127.0.0.1:'+server.address().port+'/');
|
||
for(const theme of ['dark','light'])for(const tool of ['pingcastle','maester']) {
|
||
await page.evaluate(({theme,tool})=>{document.documentElement.dataset.theme=theme;window.renderSynthetic(tool);},{theme,tool});
|
||
try { await page.waitForFunction(tool=>document.querySelector('.rich-text-table-row--violation')?.textContent?.includes(tool==='pingcastle'?'DUMMY-DC':'DUMMY-BAD'),tool,{timeout:5000}); }
|
||
catch(error) { console.error(tool, await page.locator('tbody tr').evaluateAll(rows=>rows.map(row=>({text:row.textContent,cls:row.className})))); throw error; }
|
||
const red=await page.locator('.rich-text-table-row--violation td').first().evaluate(cell=>({actual:getComputedStyle(cell).backgroundColor,
|
||
token:getComputedStyle(document.documentElement).getPropertyValue('--violation-highlight-bg').trim(),text:cell.textContent}));
|
||
const tokenColor=await page.evaluate(token=>{const element=document.createElement('span');element.style.backgroundColor=token;document.body.append(element);const result=getComputedStyle(element).backgroundColor;element.remove();return result;},red.token);
|
||
assert.equal(red.actual,tokenColor);
|
||
const neutral=await page.locator('tbody tr').filter({hasText:tool==='pingcastle'?'DUMMY-OTHER':'DUMMY-UNKNOWN'}).first().evaluate(row=>row.classList.contains('rich-text-table-row--violation'));
|
||
assert.equal(neutral,false);
|
||
if(tool==='maester')assert.equal(await page.locator('.rich-text-table-row--match').count(),1);
|
||
}
|
||
console.log('Actual Results window: red affected evidence, neutral inventory/unknown rows and explicit pass rows verified in both themes.');
|
||
} finally {await browser?.close();await new Promise(resolve=>server.close(resolve));}
|