Slow Triage
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});
← prototype/evidence/pre-evaluation-freeze.txtprototype/evidence/ui-dom-report.json →