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\}`/); });