diff --git a/tests/runner-e2e/SECURITY.md b/tests/runner-e2e/SECURITY.md index 3c015e9507f..c0f26962427 100644 --- a/tests/runner-e2e/SECURITY.md +++ b/tests/runner-e2e/SECURITY.md @@ -581,3 +581,10 @@ qualification prerequisite. This alternative does not qualify that path, create an instance administrator, bypass route authorization, or change retained failed outcomes. Passwords and cookies remain in the existing private session registry; the setup receipt contains only public identity, role, grant keys and statuses. + +A screenshot route marker is provisional. Any original secret finding, persisted +state secret finding, privacy-driven reporter redaction/omission, or packaged +evidence secret finding withholds all screenshots for that attempt. The launcher +retains a closed withholding reason and removes public markers while preserving +original private PNG bytes and hashes. Later cleanup labels and empty sanitized +export scans cannot clear this decision; the publisher and gallery enforce it. diff --git a/tests/runner-e2e/abnormal-finalization.ts b/tests/runner-e2e/abnormal-finalization.ts index af09442cf95..4a637d778a1 100644 --- a/tests/runner-e2e/abnormal-finalization.ts +++ b/tests/runner-e2e/abnormal-finalization.ts @@ -1,9 +1,10 @@ +import { withholdPrivateScreenshots } from "./screenshot-privacy.js"; import type { RunnerE2EResult } from "./types.js"; /** Evidence failure must retain the original startup/work verdict. A generated * fallback remains a failed harness result, never proof that a browser ran. */ export function abnormalFinalizationResult(original: RunnerE2EResult, error: unknown): RunnerE2EResult { const detail = error instanceof Error ? error.message : String(error); - return { ...original, status: "failed", failureClass: original.status === "failed" ? original.failureClass : "permanent_infrastructure", + return { ...withholdPrivateScreenshots(original), status: "failed", failureClass: original.status === "failed" ? original.failureClass : "permanent_infrastructure", error: `${original.error ? `${original.error}; ` : ""}Abnormal attempt finalization: ${detail}` }; } diff --git a/tests/runner-e2e/dashboard.ts b/tests/runner-e2e/dashboard.ts index 96a5eefad07..d4e9319ef16 100644 --- a/tests/runner-e2e/dashboard.ts +++ b/tests/runner-e2e/dashboard.ts @@ -1,3 +1,4 @@ +import { screenshotWithholdingNotice } from "./screenshot-privacy.js"; import { campaignAttemptTimeLabel, campaignTimingScope, renderWorkerPhaseTiming, summarizeCampaignTiming, summarizeWorkerPhaseTiming } from "./campaign-timing.js"; import { html, durationLabel, tokenLabel, llmCostLabel, usdLabel } from "./report-format.js"; import { renderReportShell, reportNavigationStyles, reportShellScript } from "./report-navigation.js"; @@ -83,6 +84,7 @@ function compactJson(value: unknown) { function resolveScreenshots( entry: RunnerDashboardEntry | undefined, ): ResolvedScreenshot[] { + if (entry && screenshotWithholdingNotice(entry.result)) return []; const declaredScreenshots = entry?.result.screenshots?.length ? entry.result.screenshots : entry @@ -244,7 +246,8 @@ function renderCase( ${STARTUP_REUSE_LAYERS.map(layer => `${html(reuse.layers[layer].state)} (${html(reuse.layers[layer].reason)})`).join("")} ${html(reuse.providerLaunchCount === null ? "unknown" : reuse.providerLaunchCount)} `).join(""); - const gallery = screenshots.length + const withholdingNotice = entry ? screenshotWithholdingNotice(entry.result) : null; + const gallery = withholdingNotice ? `

${html(withholdingNotice)}

` : screenshots.length ? `