fix(demo): print the attack-case fields that were actually observed
Quality gates / quality (push) Canceled after 0s

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.
This commit is contained in:
timmy committed 2026-10-30 12:22:56 +00:00
1 parent 0bf8230280
commit 1bbcc10a35
2 files changed
+17 -5

No files matched your search

+12 -5
View File
@@ -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'); if (securityReport.cases.length < 8) throw new Error('the RC attack suite must run every named abuse class');
await tap('[data-view="home"]', 450); await tap('[data-view="home"]', 450);
const caseDetail = (item) => { const caseDetail = (item) => {
const observed = item.observed || {}; const observed = item.observed === null || typeof item.observed !== 'object' ? {} : item.observed;
if ('providerCalls' in observed) return `http ${observed.status} · worker ${observed.providerCalls}`; // Print each observed field the case actually recorded. A case with no `status` used to render
if ('refused' in observed) return `refused ${observed.refused}`; // `http undefined` on camera, and one with no `providerCalls` rendered its status twice — both read
if ('imageBytesInLog' in observed) return `image bytes ${observed.imageBytesInLog}`; // as a broken demo instead of the measurement it is.
return item.status; 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 = [ const securityRows = [
['command', 'node scripts/security_attack_suite.mjs'], ['command', 'node scripts/security_attack_suite.mjs'],
+5
View File
@@ -535,4 +535,9 @@ test('release demo runs the RC attack suite on camera and prints the verdict it
assert.match(demo, /securityCardProof/); assert.match(demo, /securityCardProof/);
// The finding must name where the fix lives instead of implying the boundary is already safe. // 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/); 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\}`/);
}); });