// @requirements REQ-PRODUCT-RIGHTS-CENTER // @requirements REQ-PRODUCT-SCOPE-GUARDS // @requirements REQ-PRODUCT-EVIDENCE-LIFECYCLE // @requirements REQ-IT-REPROMPT REQ-PRODUCT-REGIONAL-PROMPTS import {build as parseBrowserModule} from 'esbuild'; // @requirements REQ-PRODUCT-PROCESSING-CHOICES // @requirements REQ-PRODUCT-CF-HOST import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFile, readdir } from 'node:fs/promises'; import { serveHost, hostAuthorization } from '../host.mjs'; import { EvidenceWorkspace } from '../worker.mjs'; import { createSyntheticStorage } from '../../packages/notabenda-cf/examples/synthetic.mjs'; import data from '../dist/server-data.json' with {type:'json'}; const key='a'.repeat(64); const auth={Authorization:'Basic '+btoa('admin:'+key)}; const request=(path,init={})=>new Request('https://example.test'+path,init); const state=storage=>({storage,blockConcurrencyWhile:callback=>callback()}); // @requirements REQ-EU-GUARANTEE-NOTICE REQ-PRODUCT-GUARANTEE-NOTICE test('guarantee examples preserve selected official bytes, show text and use no scripts or evidence store',async()=>{ const root=new URL('../dist/public/',import.meta.url),env={ASSETS:{fetch:async()=>new Response('Static guarantee notice')},WORKSPACE:{get(){throw Error('A static guarantee display does not open records.');}}}; const {sha256}=await import('notabenda/content'); for(const prefix of ['','dark/']){const path=prefix+'guarantees/',html=await readFile(new URL(path+'index.html',root),'utf8'),n=JSON.parse(await readFile(new URL(path+'notice.json',root),'utf8')),svg=await readFile(new URL(n.asset.url.slice(1),root),'utf8');assert.equal(sha256(svg),n.asset.sha256);assert.match(html,/nb-demo-guarantees/);assert.match(html,/guarantees\.css/);assert.match(html,/LEGAL/);assert.ok(html.includes(n.informationUrl));assert.doesNotMatch(html,/{ const root=new URL('../dist/public/',import.meta.url),env={ASSETS:{fetch:async()=>new Response('Static notice')},WORKSPACE:{get(){throw Error('A notice does not open an evidence store.');}}}; for(const prefix of ['','dark/']){ const path=prefix+'indirect-collection/',html=await readFile(new URL(path+'index.html',root),'utf8'),n=JSON.parse(await readFile(new URL(path+'notice.json',root),'utf8')),text=await readFile(new URL(path+'notice.txt',root),'utf8'); assert.match(html,/example-pages\.css/);assert.match(html,/nb-demo-indirect-collection/);assert.match(html,/data-indirect-notice-id=/);assert.doesNotMatch(html,/{ const basic={Authorization:'Basic '+btoa('admin:admin')},dev={NOTABENDA_LOCAL_DEV_AUTH:'1'}; for(const host of ['127.0.0.1','localhost','[::1]'])assert.equal(await hostAuthorization(new Request(`http://${host}:4179/backoffice`,{headers:basic}),dev),null); assert.equal((await hostAuthorization(new Request('http://127.0.0.1:4179/backoffice'),dev)).status,401); for(const host of ['example.test','localhost.example.test','127.0.0.1.example.test']){ const req=new Request(`https://${host}/backoffice`,{headers:basic});assert.equal((await hostAuthorization(req,dev)).status,503);assert.equal((await hostAuthorization(req,{...dev,ADMIN_KEY:key})).status,401); } assert.equal((await hostAuthorization(new Request('https://example.test/backoffice',{headers:auth}),{...dev,ADMIN_KEY:key})),null); for(const flag of [undefined,'0','true',true])assert.equal((await hostAuthorization(new Request('http://127.0.0.1:4179/backoffice',{headers:basic}),{ADMIN_KEY:key,NOTABENDA_LOCAL_DEV_AUTH:flag})).status,401); }); // @requirements REQ-PRODUCT-REGIONAL-PROMPTS REQ-IT-REPROMPT test('regional prompt laboratory uses explicit in-memory policy and self-hosted runtime without scripting legal routes',async()=>{ const root=new URL('../dist/public/',import.meta.url),env={ADMIN_KEY:key,ASSETS:{fetch:async()=>new Response('asset')}}; for(const prefix of ['','dark/']){ const page=await readFile(new URL(prefix+'repeat-prompts/index.html',root),'utf8');assert.match(page,/example-pages\.css/);assert.match(page,/nb-demo-regions/);assert.match(page,/in this tab/); const config=JSON.parse(await readFile(new URL(prefix+'repeat-prompts/site.json',root),'utf8'));assert.equal(config.consent.promptPolicy.profile,'it-garante-2021');assert.equal(config.consent.refusalLifetimeDays,1); const response=await serveHost(request('/'+prefix+'repeat-prompts/'),env);assert.match(response.headers.get('Content-Security-Policy'),/script-src 'self'; connect-src 'self'/); const policy=await serveHost(request('/'+prefix+'repeat-prompts/privacy/'),env);assert.doesNotMatch(policy.headers.get('Content-Security-Policy'),/script-src/); } const script=await readFile(new URL('assets/repeat-prompts.mjs',root),'utf8');assert.match(script,/new Map/);assert.doesNotMatch(script,/localStorage|sessionStorage|document\.cookie/); }); // @requirements REQ-PRODUCT-SCOPE-GUARDS test('scope laboratory publishes declared restrictions with an inert runtime and script-free legal pages',async()=>{ const root=new URL('../dist/public/',import.meta.url),env={ASSETS:{fetch:async()=>new Response('asset')}}; for(const prefix of ['','dark/']){ const page=await readFile(new URL(prefix+'scope-guards/index.html',root),'utf8'); assert.match(page,/example-pages\.css/);assert.match(page,/nb-demo-guards/);assert.match(page,/id="guard-scenario"/);assert.doesNotMatch(page,/<(?:input|textarea)\b/u); const config=JSON.parse(await readFile(new URL(prefix+'scope-guards/site.json',root),'utf8')); assert.equal(config.scopeGuards.site.children,'absent');assert.equal(config.scopeGuards.purposes[0].facts.sensitiveData,'present');assert.deepEqual(config.storage,[]); const response=await serveHost(request('/'+prefix+'scope-guards/'),env);assert.match(response.headers.get('Content-Security-Policy'),/script-src 'self'; connect-src 'self'/); for(const kind of ['privacy','cookies','terms']){ const legal=await serveHost(request('/'+prefix+'scope-guards/'+kind+'/'),env);assert.doesNotMatch(legal.headers.get('Content-Security-Policy'),/script-src/); } } const script=await readFile(new URL('assets/scope-guards.mjs',root),'utf8');assert.match(script,/new Map/);assert.doesNotMatch(script,/localStorage|sessionStorage|document\.cookie/); for(const file of ['scope-guards.js','scope-guard-policy.js'])await parseBrowserModule({entryPoints:[new URL('assets/runtime/'+file,root).pathname],bundle:true,platform:'browser',write:false}); }); // @requirements REQ-PRODUCT-RETAINED-RELEASES test('clean site output retains exact old release URLs and connects both versions to authenticated history',async()=>{ const root=new URL('../dist/public/',import.meta.url),releases=data.releases.filter(r=>(r.artifact.artifact??r.artifact).manifest.siteId==='juniper-releases');assert.equal(releases.length,2); const [before,after]=releases.map(r=>r.artifact);const index=await readFile(new URL('releases/index.html',root),'utf8');assert.doesNotMatch(index,/