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) { %>
-
+