@@ -20,7 +20,7 @@ import {
2020 prepareSandboxSessionAccess ,
2121 reportUnsettledSandboxProcess ,
2222 retainSandboxExecution ,
23- sandboxSessionInputsSafe ,
23+ sandboxSessionInputProvenance ,
2424} from '@/lib/execution/remote-sandbox/execution-observer'
2525import { withSandboxFilePublication } from '@/lib/execution/remote-sandbox/file-publication'
2626import {
@@ -55,7 +55,10 @@ import {
5555 SESSION_SANDBOX_IDLE_MS ,
5656} from '@/lib/execution/remote-sandbox/session'
5757import { sessionCommandPath } from '@/lib/execution/remote-sandbox/session-cli'
58- import { recordSessionFileInput } from '@/lib/execution/remote-sandbox/session-file-provenance'
58+ import {
59+ readSessionSecretProvenance ,
60+ recordSessionFileInput ,
61+ } from '@/lib/execution/remote-sandbox/session-file-provenance'
5962import { withSandboxSessionLock } from '@/lib/execution/remote-sandbox/session-lock'
6063import type {
6164 CreateSandboxOptions ,
@@ -842,6 +845,18 @@ async function provisionWithinBudget(
842845 throwIfAborted ( signal )
843846}
844847
848+ /** Confidentiality checks also run on provider failures, before their diagnostics can escape. */
849+ async function acceptSessionOutputHistory (
850+ session : SandboxSessionRequest | undefined ,
851+ machine : { providerId : SandboxProviderId ; sandboxId : string }
852+ ) : Promise < void > {
853+ if ( ! session ) return
854+ const provenance = await readSessionSecretProvenance ( session . key , machine )
855+ if ( session . acceptOutputProvenance ) await session . acceptOutputProvenance ( provenance )
856+ else if ( provenance . status !== 'exact' || provenance . entries . length > 0 )
857+ throw new Error ( 'Workbench output withheld because its secret provenance is unavailable' )
858+ }
859+
845860async function executeInSandboxWithinBudget (
846861 // The budget wrapper always injects the signal; the required-signal type states that
847862 // invariant instead of a cast hiding it.
@@ -887,13 +902,13 @@ async function executeInSandboxWithinBudget(
887902 // the finally below. Dependencies land before the inputs so user code and its
888903 // mounts always see a complete environment.
889904 //
890- if ( req . session )
905+ if ( lease . session && req . session )
891906 await recordSessionFileInput (
892907 req . session . key ,
893908 { providerId : created . providerId , sandboxId } ,
894- sandboxSessionInputsSafe ( ) &&
895- ! req . session . unprovenancedInputs &&
896- ! Object . keys ( selected ?. envs ?? { } ) . length
909+ req . session . unprovenancedInputs || Object . keys ( selected ?. envs ?? { } ) . length
910+ ? { status : 'unknown' }
911+ : ( req . session . inputProvenance ?. ( ) ?? sandboxSessionInputProvenance ( ) )
897912 )
898913 await provisionWithinBudget ( sandbox , selected , signal )
899914 await writeSandboxInputs ( sandbox , req . sandboxFiles , {
@@ -1025,8 +1040,15 @@ async function executeInSandboxWithinBudget(
10251040 if ( cost && billableOutputError ) {
10261041 attachTrustedSandboxOutputCost ( billableOutputError , cost )
10271042 }
1028- await privateInputFiles ?. cleanup ( )
1029- await lease . release ( )
1043+ try {
1044+ await privateInputFiles ?. cleanup ( )
1045+ await lease . release ( )
1046+ } finally {
1047+ await acceptSessionOutputHistory ( lease . session ? req . session : undefined , {
1048+ providerId : created . providerId ,
1049+ sandboxId,
1050+ } )
1051+ }
10301052 }
10311053}
10321054
@@ -1076,13 +1098,13 @@ async function executeShellInSandboxWithinBudget(
10761098 // Inside the try so a failed install or mount still releases the sandbox via
10771099 // the finally below. The install shares the caller's budget rather than adding
10781100 // to it — see the note in `executeInSandbox`.
1079- if ( req . session )
1101+ if ( lease . session && req . session )
10801102 await recordSessionFileInput (
10811103 req . session . key ,
10821104 { providerId : created . providerId , sandboxId } ,
1083- sandboxSessionInputsSafe ( ) &&
1084- ! req . session . unprovenancedInputs &&
1085- ! Object . keys ( selected ?. envs ?? { } ) . length
1105+ req . session . unprovenancedInputs || Object . keys ( selected ?. envs ?? { } ) . length
1106+ ? { status : 'unknown' }
1107+ : ( req . session . inputProvenance ?. ( ) ?? sandboxSessionInputProvenance ( ) )
10861108 )
10871109 await provisionWithinBudget ( sandbox , selected , signal )
10881110 await writeSandboxInputs ( sandbox , req . sandboxFiles , {
@@ -1192,8 +1214,15 @@ async function executeShellInSandboxWithinBudget(
11921214 if ( cost && billableOutputError ) {
11931215 attachTrustedSandboxOutputCost ( billableOutputError , cost )
11941216 }
1195- await privateInputFiles ?. cleanup ( )
1196- await lease . release ( )
1217+ try {
1218+ await privateInputFiles ?. cleanup ( )
1219+ await lease . release ( )
1220+ } finally {
1221+ await acceptSessionOutputHistory ( lease . session ? req . session : undefined , {
1222+ providerId : created . providerId ,
1223+ sandboxId,
1224+ } )
1225+ }
11971226 }
11981227}
11991228
0 commit comments