Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 175 additions & 13 deletions apps/sim/app/api/function/execute/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ describe('Function Execute API Route', () => {
createMockRequest(
'POST',
{
code: 'return environmentVariables.API_KEY',
code: 'return {{API_KEY}}',
envVars: { API_KEY: 'secret-at-the-end' },
workflowId: 'workflow-1',
workspaceId: 'workspace-1',
Expand Down Expand Up @@ -686,7 +686,7 @@ describe('Function Execute API Route', () => {
createMockRequest(
'POST',
{
code: 'print("done")',
code: 'print("{{API_KEY}}")',
language: 'python',
workspaceId: 'workspace-1',
envVars: { API_KEY: 'secret-value' },
Expand Down Expand Up @@ -821,6 +821,54 @@ describe('Function Execute API Route', () => {
expect(mockExecuteInSandbox).not.toHaveBeenCalled()
})

it('runs with authenticated incomplete mount provenance and marks exported bytes unknown', async () => {
envFlagsMock.isRemoteSandboxEnabled = true
mockExecuteInSandbox.mockResolvedValueOnce({
result: 'raw result',
stdout: '',
sandboxId: 'sandbox-123',
exportedFiles: { '/home/user/output.txt': 'raw output' },
})

const response = await POST(
createMockRequest(
'POST',
{
code: 'print("done")',
language: 'python',
workspaceId: 'workspace-1',
outputs: {
files: [
{
path: 'files/output.txt',
sandboxPath: '/home/user/output.txt',
mimeType: 'text/plain',
},
],
},
[PRIVATE_SECRET_PROVENANCE_FIELD]: {
version: 1,
complete: false,
selections: [],
},
},
{ [PRIVATE_SECRET_PROVENANCE_HEADER]: PRIVATE_SECRET_PROVENANCE_BUNDLE_V1 }
)
)

expect(response.status).toBe(200)
expect((await response.json()).output.result).toEqual(
expect.objectContaining({ fileId: 'wf_output_txt', vfsPath: 'files/output.txt' })
)
expect(mockExecuteInSandbox).toHaveBeenCalledOnce()
expect(mockWriteWorkspaceFileByPath).toHaveBeenCalledWith(
expect.objectContaining({
buffer: Buffer.from('raw output'),
secretProvenance: { status: 'unknown' },
})
)
})

it('does not rewrite a static export path that happens to equal a resolved secret', async () => {
envFlagsMock.isRemoteSandboxEnabled = true
mockExecuteInSandbox.mockResolvedValueOnce({
Expand Down Expand Up @@ -853,6 +901,7 @@ describe('Function Execute API Route', () => {
expect(mockWriteWorkspaceFileByPath).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({ path: 'files/report-secret-value.txt' }),
secretProvenance: { status: 'exact', entries: [] },
})
)
expect(JSON.stringify(data)).toContain('files/report-secret-value.txt')
Expand Down Expand Up @@ -890,7 +939,7 @@ describe('Function Execute API Route', () => {
)
})

it('keeps a binary export unknown when files were mounted without a provenance envelope', async () => {
it('classifies a binary export exact-empty when ordinary files were mounted without secret provenance', async () => {
envFlagsMock.isRemoteSandboxEnabled = true
mockExecuteInSandbox.mockResolvedValueOnce({
result: 'done',
Expand Down Expand Up @@ -919,7 +968,7 @@ describe('Function Execute API Route', () => {

expect(response.status).toBe(200)
expect(mockWriteWorkspaceFileByPath).toHaveBeenCalledWith(
expect.objectContaining({ secretProvenance: { status: 'unknown' } })
expect.objectContaining({ secretProvenance: { status: 'exact', entries: [] } })
)
})

Expand Down Expand Up @@ -987,7 +1036,7 @@ describe('Function Execute API Route', () => {

const response = await POST(
createMockRequest('POST', {
code: 'print("done")',
code: 'print("{{API_KEY}}")',
language: 'python',
workspaceId: 'workspace-1',
envVars: { API_KEY: 'secret-value' },
Expand Down Expand Up @@ -2002,6 +2051,117 @@ describe('Function Execute API Route', () => {
expect(Object.values(request.contextVariables)).not.toContain('must-not-bind')
})

it('does not infer provenance from an unused low-entropy environment value', async () => {
mockExecuteInIsolatedVM.mockResolvedValueOnce({ result: 'Box eSign', stdout: '' })

const response = await POST(
createMockRequest(
'POST',
{
code: 'return "Box eSign"',
envVars: { SERVICENOW_PASSWORD: 'x' },
},
{ 'x-sim-request-private-tool-metadata': 'resolved-secret-names-v1' }
)
)
const data = await response.json()

expect(response.status).toBe(200)
expect(data.output.result).toBe('Box eSign')
expect(data.__resolvedSecretNames).toEqual([])
})

it('does not build provenance matchers for unused oversized environment values', async () => {
mockExecuteInIsolatedVM.mockResolvedValueOnce({ result: 'safe', stdout: '' })

const response = await POST(
createMockRequest(
'POST',
{
code: 'return "safe"',
envVars: { UNUSED: 'x'.repeat(65 * 1024) },
},
{ 'x-sim-request-private-tool-metadata': 'resolved-secret-names-v1' }
)
)

expect(response.status).toBe(200)
expect((await response.json()).__resolvedSecretNames).toEqual([])
})

it('conservatively reports only compiled secrets when bounded output classification is exceeded', async () => {
const result = Array.from({ length: 100_001 }, () => 'ordinary')
mockExecuteInIsolatedVM.mockResolvedValueOnce({ result, stdout: '' })

const response = await POST(
createMockRequest(
'POST',
{
code: 'const key = {{API_KEY}}; return params.items',
params: { items: result },
envVars: { API_KEY: 'secret-value', UNUSED: 'x' },
},
{ 'x-sim-request-private-tool-metadata': 'resolved-secret-names-v1' }
)
)
const data = await response.json()

expect(response.status).toBe(200)
expect(response.headers.get('x-sim-private-tool-metadata')).toBe('resolved-secret-names-v1')
expect(data.output.result).toHaveLength(100_001)
expect(data.output.result[0]).toBe('ordinary')
expect(data.__resolvedSecretNames).toEqual(['API_KEY'])
})

it('conservatively reports a compiled secret whose value exceeds matcher capacity', async () => {
mockExecuteInIsolatedVM.mockResolvedValueOnce({ result: 'ordinary', stdout: '' })

const response = await POST(
createMockRequest(
'POST',
{
code: 'const key = {{OVERSIZED_SECRET}}; return "ordinary"',
envVars: { OVERSIZED_SECRET: 's'.repeat(64 * 1024 + 1), UNUSED: 'x' },
},
{ 'x-sim-request-private-tool-metadata': 'resolved-secret-names-v1' }
)
)
const data = await response.json()

expect(response.status).toBe(200)
expect(data.output.result).toBe('ordinary')
expect(data.__resolvedSecretNames).toEqual(['OVERSIZED_SECRET'])
})

it('tracks only compiled names when configured secrets share the same value', async () => {
mockExecuteInIsolatedVM.mockResolvedValueOnce({ result: 'true', stdout: '' })
const oneResponse = await POST(
createMockRequest(
'POST',
{
code: 'return {{SECOND}}',
envVars: { FIRST: 'true', SECOND: 'true' },
},
{ 'x-sim-request-private-tool-metadata': 'resolved-secret-names-v1' }
)
)

mockExecuteInIsolatedVM.mockResolvedValueOnce({ result: 'true', stdout: '' })
const bothResponse = await POST(
createMockRequest(
'POST',
{
code: 'const first = {{FIRST}}; return {{SECOND}}',
envVars: { FIRST: 'true', SECOND: 'true' },
},
{ 'x-sim-request-private-tool-metadata': 'resolved-secret-names-v1' }
)
)

expect((await oneResponse.json()).__resolvedSecretNames).toEqual(['SECOND'])
expect((await bothResponse.json()).__resolvedSecretNames).toEqual(['FIRST', 'SECOND'])
})

it('lowers missing shell placeholders while preserving comments and heredoc delimiters', async () => {
envFlagsMock.isRemoteSandboxEnabled = true
const response = await POST(
Expand Down Expand Up @@ -2134,7 +2294,7 @@ describe('Function Execute API Route', () => {
expect(mockExecuteInSandbox).not.toHaveBeenCalled()
})

it('reports exact secret values returned through placeholders and the environment map', async () => {
it('reports exact secret values returned through placeholders without inferring direct environment reads', async () => {
mockExecuteInIsolatedVM.mockResolvedValueOnce({
result: 'secret-valueother-secret',
stdout: '',
Expand Down Expand Up @@ -2171,14 +2331,15 @@ describe('Function Execute API Route', () => {
const directData = await directResponse.json()

expect(envData.__resolvedSecretNames).toEqual(['ENV_ONLY', 'SHARED'])
expect(directData.__resolvedSecretNames).toEqual(['API_KEY'])
expect(directData.output.result).toBe('secret-value')
expect(directData.__resolvedSecretNames).toEqual([])
})

it.each([
{ name: 'numeric', secret: '123', result: 123 },
{ name: 'boolean', secret: 'true', result: true },
])(
'records provenance for a typed $name secret returned through direct environment access',
'preserves a typed $name value returned through legacy direct environment access without inferred provenance',
async ({ secret, result }) => {
mockExecuteInIsolatedVM.mockResolvedValueOnce({ result, stdout: '' })

Expand All @@ -2197,11 +2358,11 @@ describe('Function Execute API Route', () => {
const data = await response.json()

expect(data.output.result).toBe(result)
expect(data.__resolvedSecretNames).toEqual(['API_KEY'])
expect(data.__resolvedSecretNames).toEqual([])
}
)

it('reports shell substitutions and exact secret output from direct environment access', async () => {
it('reports placeholder output without inferring provenance from legacy shell environment access', async () => {
envFlagsMock.isRemoteSandboxEnabled = true
mockExecuteShellInSandbox.mockResolvedValueOnce({
result: null,
Expand Down Expand Up @@ -2245,7 +2406,8 @@ describe('Function Execute API Route', () => {
const directData = await directResponse.json()

expect(referencedData.__resolvedSecretNames).toEqual(['API_KEY'])
expect(directData.__resolvedSecretNames).toEqual(['API_KEY'])
expect(directData.output.stdout).toBe('secret-value')
expect(directData.__resolvedSecretNames).toEqual([])
})

it('returns nonzero shell stderr as a visible 422 error and diagnostic output', async () => {
Expand Down Expand Up @@ -2289,8 +2451,8 @@ describe('Function Execute API Route', () => {
)

expect(response.status).toBe(200)
expect((await response.json()).__resolvedSecretNames).toBeUndefined()
expect(response.headers.get('x-sim-private-tool-metadata')).toBeNull()
expect((await response.json()).__resolvedSecretNames).toEqual([])
expect(response.headers.get('x-sim-private-tool-metadata')).toBe('resolved-secret-names-v1')
expect(mockExecuteInIsolatedVM).toHaveBeenCalled()
})

Expand Down
Loading
Loading