Exact reviewed source / 18 of 64
prototype/evidence/ui-dom-checks.cjs
SHA-256 of original bytes
a7b1a48982f89ad06d7d1589ddeed92ee2d80afd731427d2f13670c036def4bb
// Source/DOM simulation only. This is not a browser, visual, keyboard, or XSS execution test.
const fs = require('fs');
const vm = require('vm');
const assert = require('assert/strict');
const path = require('node:path');
const { pathToFileURL } = require('node:url');
const ROOT = path.resolve(__dirname, '..');
const OUT = path.join(ROOT, 'evidence');
(async () => {
const { normalizeIssues, applyDecisions } = await import(pathToFileURL(path.join(ROOT, 'src/core.mjs')).href);
const checks=[], ids=new Map(), requests=[], downloads=[], blobMap=new Map();
class Element {
constructor(tag='div'){this.tagName=tag.toUpperCase();this.children=[];this.attributes={};this.events={};this.className='';this._text='';this.hidden=false;this.disabled=false;this.value='';this.dataset={};this.checked=false;this.files=[];this.classList={toggle:(name,on)=>{let a=this.className.split(' ').filter(Boolean);const enabled=on??!a.includes(name);a=a.filter(n=>n!==name);if(enabled)a.push(name);this.className=a.join(' ');},remove:(name)=>{this.className=this.className.split(' ').filter(n=>n!==name).join(' ');}};}
set textContent(v){this._text=String(v);this.children=[];}
get textContent(){return this._text+this.children.map(c=>c.textContent).join('');}
set innerHTML(_){throw Error('Unexpected HTML sink in shipped code');}
append(...children){for(const c of children){c.parentNode=this;this.children.push(c);}}
replaceChildren(...children){this._text='';this.children=[];this.append(...children);}
get childElementCount(){return this.children.length;}
addEventListener(type,fn){(this.events[type]??=[]).push(fn);}
setAttribute(k,v){this.attributes[k]=String(v);}
focus(){}
remove(){if(this.parentNode)this.parentNode.children=this.parentNode.children.filter(x=>x!==this);}
async dispatch(type){for(const fn of this.events[type]??[])await fn({target:this});}
click(){if(this.tagName==='A')downloads.push({url:this.href,name:this.download});else if(!this.disabled)return this.dispatch('click');}
}
const body=new Element('body');
const html=fs.readFileSync(ROOT+'/public/index.html','utf8');
for(const match of html.matchAll(/id="([^"]+)"/g)){const e=new Element();e.id=match[1];ids.set(e.id,e);body.append(e);}
const filters=['all','review','duplicates'].map(f=>{const e=new Element('button');e.className='filter';e.dataset.filter=f;body.append(e);return e;});
function elements(){const out=[];const visit=n=>{out.push(n);n.children.forEach(visit)};visit(body);return out;}
const document={body,getElementById:id=>ids.get(id),createElement:tag=>new Element(tag),querySelectorAll:selector=>elements().filter(e=>selector.split(',').some(sel=>e.className.split(' ').includes(sel.trim().slice(1))))};
let current=[], before=[], runNo=0;
const sample=[{id:1,title:'Synthetic crash',body:'Test body',labels:['needs-triage']},{id:2,title:'A feature request',body:'Synthetic feature',labels:[]}];
const fetch=async (url,options)=>{
const input=options.body?JSON.parse(options.body):undefined;
requests.push({url,input});
let data;
if(url==='/api/evaluation')data={available:false};
else if(url==='/api/sample')data={issues:sample};
else if(url==='/api/triage'){
current=normalizeIssues(input);runNo++;
const items=current.map((item,i)=>({...item,suggestedLabel:i?'feature':'bug',similarity:.6,margin:.1,needsReview:i===1,evidence:{title:'Synthetic reference',label:i?'feature':'bug',similarity:.7},duplicateOf:i?1:null,duplicateSimilarity:i?.9:0}));
data={runId:'mock-'+runNo,items,summary:{total:items.length,ready:1,review:items.length-1,duplicateCandidates:items.length-1},model:{name:'mock-model-for-tests-only',offline:true},timings:{totalMs:1},workflowSteps:['Test stub']};
}else if(url==='/api/apply'){before.push(JSON.parse(JSON.stringify(current)));current=applyDecisions(current,input.decisions);data={revisionId:'rev-'+before.length,issues:current,appliedCount:input.decisions.length};}
else if(url==='/api/undo'){current=before.pop();data={issues:current,restoredCount:1};}
else throw Error('Unexpected URL: '+url);
return {ok:true,status:200,json:async()=>JSON.parse(JSON.stringify(data))};
};
const context={document,fetch,Blob,URL:{createObjectURL:blob=>{const id='blob:mock/'+blobMap.size;blobMap.set(id,blob);return id;},revokeObjectURL:()=>{}},setTimeout:()=>{},console};
vm.createContext(context);
const source=fs.readFileSync(ROOT+'/public/app.js','utf8');
vm.runInContext(source.replace(/\}\)\(\);\s*$/, 'globalThis.__test={state,validateIssues,render,runTriage,loadSample,applySelected,undoLast,exportCurrent,exportCSV};})();'),context);
const app=context.__test;
await app.loadSample();
assert.equal(app.state.currentIssues.length,2);
await app.runTriage();
assert.equal(app.state.selected.size,0);
assert(ids.get('apply-button').disabled);
assert.equal(document.querySelectorAll('.issue-card').length,2);
checks.push({check:'Load sample and triage produce API-backed cards with no automatic selection',pass:true});
let checkbox=document.querySelectorAll('.issue-checkbox')[0];checkbox.checked=true;await checkbox.dispatch('change');
let select=document.querySelectorAll('.label-select')[0];select.value='documentation';await select.dispatch('change');
assert.equal(app.state.selected.size,1);assert(!ids.get('apply-button').disabled);
await app.applySelected();
assert.equal(requests.at(-1).input.decisions.length,1);assert.equal(requests.at(-1).input.decisions[0].label,'documentation');
assert(current[0].labels.includes('documentation'));assert.deepEqual(current[1].labels,[]);
checks.push({check:'Explicit checkbox and label override submit one decision; other issue stays unchanged',pass:true});
await app.undoLast();assert.equal(JSON.stringify(current),JSON.stringify(sample));
checks.push({check:'Undo response restores the local version and clears selection',pass:true});
await filters[2].dispatch('click');assert.equal(document.querySelectorAll('.issue-card').length,1);
await filters[1].dispatch('click');assert.equal(document.querySelectorAll('.issue-card').length,1);
checks.push({check:'Review and duplicate filters render matching returned flags',pass:true});
app.exportCurrent();let exported=JSON.parse(await blobMap.get(downloads.at(-1).url).text());assert.equal(JSON.stringify(exported.issues),JSON.stringify(sample));
checks.push({check:'JSON download serializes the restored current local copy',pass:true});
ids.get('json-input').value='{broken';await ids.get('json-input').dispatch('input');await app.runTriage();
assert(ids.get('notice').textContent.includes('JSON isn’t valid'));assert(ids.get('apply-button').disabled);
checks.push({check:'Invalid JSON is rejected and input edits disable stale selections',pass:true});
const unsafe=[{number:10,title:'<img src=x onerror="evil()">',body:'</textarea><script>evil()</script>',labels:[{name:'<svg onload="evil()">'}],privateExtra:'must-not-export'},...['=2+2','+SUM(1,2)','-1+2','@SUM(1,2)','\t=2+2','\r=2+2'].map((title,i)=>({id:11+i,title,body:null,labels:[]}))];
ids.get('file-input').files=[{name:'synthetic.json',size:100,text:async()=>JSON.stringify(unsafe)}];
await ids.get('file-input').dispatch('change');await app.runTriage();
assert(!JSON.stringify(app.state.currentIssues).includes('privateExtra'));assert(!JSON.stringify(app.state.currentIssues).includes('must-not-export'));
assert(document.querySelectorAll('.issue-card')[0].textContent.includes('<img src=x'));
assert.equal(elements().filter(e=>['IMG','SCRIPT','SVG'].includes(e.tagName)).length,0);
checks.push({check:'DOM simulation uses text nodes for markup payloads; schema strips extra fields (not a browser XSS execution test)',pass:true});
app.exportCSV();const csv=await blobMap.get(downloads.at(-1).url).text();
for(const prefix of ['=2+2','+SUM(1,2)','-1+2','@SUM(1,2)'])assert(csv.includes('"\''+prefix+'"'));
assert(csv.includes('onerror=""evil()""'));assert(!csv.includes('privateExtra'));
checks.push({check:'CSV formula prefixes and embedded quotes are escaped; extra fields absent',pass:true});
const idsInHTML=[...html.matchAll(/id="([^"]+)"/g)].map(x=>x[1]);assert.equal(idsInHTML.length,new Set(idsInHTML).size);
const referencedIDs=[...source.matchAll(/\$\('([^']+)'\)/g)].map(x=>x[1]);for(const id of referencedIDs)assert(ids.has(id),'missing '+id);
assert(!/\.innerHTML\s*=|insertAdjacentHTML|\beval\s*\(|new Function\b/.test(source));
checks.push({check:'Unique HTML IDs, all JS element references present, and no dynamic HTML/eval sink',pass:true});
const report={kind:'Source and simulated DOM checks, not browser verification',checks,passed:checks.length,browserStatus:'Blocked: Chromium Unix socket creation denied by sandbox. Visual layout, real keyboard behavior, browser XSS inertness and actual downloads remain unverified.'};
fs.writeFileSync(OUT+'/ui-dom-report.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report,null,2));
})().catch(e=>{console.error(e);process.exitCode=1});