Skip to content

Commit 754e37c

Browse files
icecrasher321claude
andcommitted
fix(execution): count the runtime payload as secret material in scope
An execution with no mounted files and no env secret still carries `params` and `contextVariables` into the sandbox — the runtime payload is serialized into a private-input file, so resolved block outputs and workflow variables land as plaintext regardless of `_sandboxFiles`. The scope predicate only looked at mounts and env secrets, so a binary derived from them was classified exact-empty. The route has no catalog for those values and cannot tell a secret-bearing one from an ordinary one, so they count as in scope. Only an execution with nothing at all in scope earns an exact-empty binary. This narrows where the relaxation applies rather than regressing anything: every binary export was unknown before this branch, so a workflow Function block carrying block references keeps exactly the behavior it has today. The mothership path is unaffected — its tool sets no contextVariables, blockData, or workflowVariables, which is the case this branch exists to fix. Values, not keys, for the params check: `executionParams._context` is set to undefined before the context is built, so a key count reads every execution as carrying params. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ddc1c1a commit 754e37c

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

apps/sim/app/api/function/execute/route.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,40 @@ describe('Function Execute API Route', () => {
890890
)
891891
})
892892

893+
it('keeps a binary export unknown when resolved context variables reach the sandbox', async () => {
894+
envFlagsMock.isRemoteSandboxEnabled = true
895+
mockExecuteInSandbox.mockResolvedValueOnce({
896+
result: 'done',
897+
stdout: '',
898+
sandboxId: 'sandbox-123',
899+
exportedFiles: { '/home/user/small.jpg': '/9j/4AAQ' },
900+
})
901+
902+
const response = await POST(
903+
createMockRequest('POST', {
904+
code: 'print("done")',
905+
language: 'python',
906+
workspaceId: 'workspace-1',
907+
// Resolved upstream block output — the route has no catalog to classify it.
908+
contextVariables: { upstreamValue: 'could-be-anything' },
909+
outputs: {
910+
files: [
911+
{
912+
path: 'files/small.jpg',
913+
sandboxPath: '/home/user/small.jpg',
914+
mimeType: 'image/jpeg',
915+
},
916+
],
917+
},
918+
})
919+
)
920+
921+
expect(response.status).toBe(200)
922+
expect(mockWriteWorkspaceFileByPath).toHaveBeenCalledWith(
923+
expect.objectContaining({ secretProvenance: { status: 'unknown' } })
924+
)
925+
})
926+
893927
it('keeps a binary export unknown when files were mounted without a provenance envelope', async () => {
894928
envFlagsMock.isRemoteSandboxEnabled = true
895929
mockExecuteInSandbox.mockResolvedValueOnce({

apps/sim/app/api/function/execute/route.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,12 @@ interface FunctionRouteExecutionContext {
990990
outputSecretPlaintextsByName: Map<string, string>
991991
mountedFileSecretProvenanceScanner?: MountedFileSecretProvenanceScanner
992992
hasMountedSandboxFiles: boolean
993+
/**
994+
* Whether the serialized runtime payload carried `params` or `contextVariables` into the sandbox.
995+
* Those are resolved block outputs and workflow variables — the route has no catalog for them, so
996+
* it cannot tell a secret-bearing one from an ordinary value.
997+
*/
998+
hasUnclassifiedRuntimeInputs: boolean
993999
}
9941000

9951001
type ResolvedSecretNamesMetadataType =
@@ -1219,9 +1225,15 @@ function activateOutputSecretProvenance(
12191225
* are unclassifiable rather than clean: absence of an envelope is absence of evidence, not evidence
12201226
* the mount carried nothing. Those fail closed here so the classification can never be stronger
12211227
* than what the caller actually attested to.
1228+
*
1229+
* The serialized runtime payload counts too. It carries `params` and `contextVariables` — resolved
1230+
* block outputs and workflow variables — into the sandbox as a private-input file, and the route
1231+
* has no catalog to tell a secret-bearing one from an ordinary value. Only an execution with
1232+
* nothing at all in scope earns an exact-empty binary.
12221233
*/
12231234
function hasSecretMaterialInScope(context: FunctionRouteExecutionContext): boolean {
12241235
if (context.outputSecretPlaintextsByName.size > 0) return true
1236+
if (context.hasUnclassifiedRuntimeInputs) return true
12251237
const scanner = context.mountedFileSecretProvenanceScanner
12261238
return scanner ? scanner.hasSecrets : context.hasMountedSandboxFiles
12271239
}
@@ -2031,6 +2043,12 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
20312043
outputSecretPlaintextsByName: new Map(),
20322044
mountedFileSecretProvenanceScanner,
20332045
hasMountedSandboxFiles: (_sandboxFiles?.length ?? 0) > 0,
2046+
// Values, not keys: `executionParams._context` is explicitly set to undefined just above,
2047+
// so a key count would read every execution as carrying params. Raised below once the
2048+
// resolved context variables are known.
2049+
hasUnclassifiedRuntimeInputs: Object.values(executionParams).some(
2050+
(value) => value !== undefined
2051+
),
20342052
}
20352053
for (const [name, plaintext] of Object.entries(envVars)) {
20362054
if (!plaintext) continue
@@ -2074,6 +2092,11 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
20742092
...codeResolution.contextVariables,
20752093
...preResolvedContextVariables,
20762094
}
2095+
// Resolved block outputs and workflow variables ride into the sandbox inside the runtime
2096+
// payload, so an output file can carry them even with nothing mounted and no env secret.
2097+
if (Object.keys(contextVariables).length > 0) {
2098+
routeContext.hasUnclassifiedRuntimeInputs = true
2099+
}
20772100
const compilation = await compileCodePlaceholders({
20782101
code: codeResolution.resolvedCode,
20792102
language: lang,

0 commit comments

Comments
 (0)