From 1bbcc10a356f62d3ab2118081874cbb8ab5e7ea6 Mon Sep 17 00:00:00 2001 From: Timmy Date: Fri, 30 Oct 2026 12:22:56 +0000 Subject: [PATCH] fix(demo): print the attack-case fields that were actually observed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RC security card rendered `http undefined · worker 12` for the flood case, which records no status, and repeated its bare status for the malformed case, which records no provider call. Both read as a broken card rather than the measurement it is. Build each row from the observed fields that exist and leave the rest out. --- scripts/record_release_demo.mjs | 17 ++++++++++++----- tests/release-demo.test.js | 5 +++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/scripts/record_release_demo.mjs b/scripts/record_release_demo.mjs index 63b46ae..10ee935 100644 --- a/scripts/record_release_demo.mjs +++ b/scripts/record_release_demo.mjs @@ -1195,11 +1195,18 @@ const securityReport = securityRun.report; if (securityReport.cases.length < 8) throw new Error('the RC attack suite must run every named abuse class'); await tap('[data-view="home"]', 450); const caseDetail = (item) => { - const observed = item.observed || {}; - if ('providerCalls' in observed) return `http ${observed.status} · worker ${observed.providerCalls}`; - if ('refused' in observed) return `refused ${observed.refused}`; - if ('imageBytesInLog' in observed) return `image bytes ${observed.imageBytesInLog}`; - return item.status; + const observed = item.observed === null || typeof item.observed !== 'object' ? {} : item.observed; + // Print each observed field the case actually recorded. A case with no `status` used to render + // `http undefined` on camera, and one with no `providerCalls` rendered its status twice — both read + // as a broken demo instead of the measurement it is. + const parts = []; + if ('status' in observed) parts.push(`http ${observed.status}`); + if ('refused' in observed) parts.push(`refused ${observed.refused}`); + if ('providerCalls' in observed) parts.push(`worker ${observed.providerCalls}`); + if ('providerTextLeaked' in observed) parts.push(`text leaked ${observed.providerTextLeaked}`); + if ('repeatedAnswerIdentical' in observed) parts.push(`identical ${observed.repeatedAnswerIdentical}`); + if ('imageBytesInLog' in observed) parts.push(`image bytes ${observed.imageBytesInLog}`); + return parts.length ? parts.join(' · ') : item.status; }; const securityRows = [ ['command', 'node scripts/security_attack_suite.mjs'], diff --git a/tests/release-demo.test.js b/tests/release-demo.test.js index d104856..16e8033 100644 --- a/tests/release-demo.test.js +++ b/tests/release-demo.test.js @@ -535,4 +535,9 @@ test('release demo runs the RC attack suite on camera and prints the verdict it assert.match(demo, /securityCardProof/); // The finding must name where the fix lives instead of implying the boundary is already safe. assert.match(demo, /Refusing bytes by their content is the fix in review, not a claim this video makes/); + // Each case row prints the fields it actually observed. A guarantee that the card never renders + // `http undefined` (a case with no status) or the bare status again (a case with no provider calls). + assert.match(demo, /'refused' in observed/); + assert.match(demo, /'providerTextLeaked' in observed/); + assert.doesNotMatch(demo, /http \$\{observed\.status\} · worker \$\{observed\.providerCalls\}`/); });