diff --git a/ci/dynamo-removal/report-proof.cjs b/ci/dynamo-removal/report-proof.cjs index 94c680d20b..bf21fe2e2c 100644 --- a/ci/dynamo-removal/report-proof.cjs +++ b/ci/dynamo-removal/report-proof.cjs @@ -12,13 +12,18 @@ const rid=process.env.DYNAMO_PROOF_REPORT_ID; assert(/^rlocal[a-z0-9]+$/.test(rid),'generated local report ID required'); const output=process.env.DYNAMO_PROOF_OUTPUT; assert(output,'DYNAMO_PROOF_OUTPUT required');fs.mkdirSync(output,{recursive:true}); +// The report must render with every non-local host blocked. Deliberate third +// parties are listed by name; they are blocked too, only the assertion allows them. +const deliberateHosts=new Set(['scripts.simpleanalyticscdn.com','queue.simpleanalyticscdn.com']); +const localUrl=/^https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?\//; (async()=>{ const browser=await chromium.launch({headless:true});const receipts=[]; try { for(const route of ['report','topicStats','topicReport']){ const page=await browser.newPage({viewport:{width:1440,height:1100}}); - const errors=[];const responses=[];let narrativeEvidence; + const errors=[];const responses=[];const blocked=[];let narrativeEvidence; page.on('pageerror',error=>errors.push(error.message)); + await page.route(url=>!localUrl.test(url.href),route=>{blocked.push(route.request().url());return route.abort('blockedbyclient')}); page.on('response',async response=>{ if(!response.url().includes('/api/'))return; let body;try{body=await response.text()}catch{body='unavailable'} @@ -61,8 +66,10 @@ assert(output,'DYNAMO_PROOF_OUTPUT required');fs.mkdirSync(output,{recursive:tru const body=await page.locator('body').innerText(); await page.screenshot({path:path.join(output,route+'.png'),fullPage:true}); await page.screenshot({path:path.join(output,route+'-viewport.png')}); - const record={route,body,errors,responses,narrativeEvidence};receipts.push(record); + const record={route,body,errors,blocked,responses,narrativeEvidence};receipts.push(record); fs.writeFileSync(path.join(output,'browser.json'),JSON.stringify(receipts,null,2)); + const thirdParty=blocked.filter(url=>!deliberateHosts.has(new URL(url).hostname)); + assert.deepEqual(thirdParty,[],route+': requests to third-party hosts'); assert.equal(errors.length,0,route+': '+errors.join('; ')); assert(responses.length>0,route+': API requests observed'); assert(responses.every(r=>r.status<400),route+': '+responses.filter(r=>r.status>=400).map(r=>r.url)); diff --git a/client-admin/package-lock.json b/client-admin/package-lock.json index f627cf7cf0..da77276dc7 100644 --- a/client-admin/package-lock.json +++ b/client-admin/package-lock.json @@ -8,6 +8,7 @@ "name": "polis-client-admin", "version": "0.1.0", "dependencies": { + "@fontsource/space-mono": "5.3.0", "@reduxjs/toolkit": "^2.11.0", "@theme-ui/mdx": "^0.17.2", "oidc-client-ts": "^3.4.1", @@ -2496,6 +2497,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fontsource/space-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/space-mono/-/space-mono-5.3.0.tgz", + "integrity": "sha512-FrpBMOVWn3PRdRHlrZWC55X3kZG/2BTH7SM5ZyS/bky3iKya2BturI5lk+t0RoDQL2JyHzURsVq/EisCsxpI5A==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", diff --git a/client-admin/package.json b/client-admin/package.json index 9b6ecb3bb4..f5446c44da 100644 --- a/client-admin/package.json +++ b/client-admin/package.json @@ -54,6 +54,7 @@ "webpack-dev-server": "~5.2.2" }, "dependencies": { + "@fontsource/space-mono": "5.3.0", "@reduxjs/toolkit": "^2.11.0", "@theme-ui/mdx": "^0.17.2", "oidc-client-ts": "^3.4.1", diff --git a/client-admin/public/index.html b/client-admin/public/index.html index ad8b69bd83..5e2b33b090 100644 --- a/client-admin/public/index.html +++ b/client-admin/public/index.html @@ -8,9 +8,10 @@ - + + <% htmlWebpackPlugin.options.fontStylesheets.forEach(function (href) { %> + + <% }) %> <% if (htmlWebpackPlugin.options.isProduction) { %> - +