Skip to content

fix(chat): stop classifying secret-free binary sandbox exports as unknown - #6349

Merged
icecrasher321 merged 6 commits into
stagingfrom
staging-v28
Aug 7, 2026
Merged

fix(chat): stop classifying secret-free binary sandbox exports as unknown#6349
icecrasher321 merged 6 commits into
stagingfrom
staging-v28

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

Binary outputs from sandboxes need to be trusted in the right manner.

Type of Change

  • Bug fix

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 7, 2026 12:00am

Request Review

@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes secret provenance classification for binary exports and when provenance envelopes are emitted—security-sensitive boundaries that could mislabel files if logic drifts from mount attestation.

Overview
Binary sandbox exports no longer default to unknown secret provenance. The function execute route classifies them as exact-empty when no environment secrets, attested mounted-file secrets, or unattested mounts were in scope; otherwise they stay unknown. Mounted-file provenance scanners expose hasSecrets so attested secrets still fail closed even when plaintext cannot be scanned.

Copilot function execution only attaches the mounted-files provenance bundle when every sandbox mount came from this resolver. Pre-existing _sandboxFiles from another path omit the envelope so the route does not over-attest the full mount set.

VFS reads clamp offset/limit against the actual split line count (not self-reported totalLines) and skip windowing for attachment results so labels stay intact.

Also fixes an embeddings import (chunkArray from @sim/utils/helpers) and adds tests across route, handler, mounted provenance, and VFS.

Reviewed by Cursor Bugbot for commit 287995f. Configure here.

@icecrasher321 icecrasher321 changed the title fix(execution): stop classifying secret-free binary sandbox exports as unknown fix(chat): stop classifying secret-free binary sandbox exports as unknown Aug 6, 2026
@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR allows secret-free binary sandbox exports to receive exact-empty provenance while retaining unknown provenance whenever mounted secrets cannot be ruled out.

  • Tracks whether mounted-file provenance contains secret material.
  • Fails closed when mounted files lack a complete provenance envelope.
  • Emits whole-mount provenance for resolved Copilot inputs, including tables.
  • Adds regression coverage for binary export classification and mount-envelope handling.
  • Corrects VFS read windowing and generated/import artifacts included in the branch.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/app/api/function/execute/route.ts Classifies binary exports as exact-empty only when environment and mounted-file provenance establish that no cataloged secret material was in scope.
apps/sim/lib/copilot/tools/handlers/function-execute.ts Couples resolved mounts with a whole-set provenance envelope and deliberately omits it when preserved mounts cannot be attested.
apps/sim/lib/execution/mounted-file-secret-provenance.ts Exposes envelope-level secret presence independently from whether entries produce scannable plaintext.
apps/sim/lib/copilot/tools/handlers/vfs.ts Applies line windows using actual content length while preserving attachment labels.
apps/sim/lib/embeddings/client.ts Updates the utility import to the established helpers subpath.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Sandbox execution exports binary file] --> B{Environment secrets in scope?}
  B -->|Yes| U[Persist unknown provenance]
  B -->|No| C{Mounted files present?}
  C -->|No| E[Persist exact-empty provenance]
  C -->|Yes| D{Complete mount envelope available?}
  D -->|No| U
  D -->|Yes| F{Envelope contains secret entries?}
  F -->|Yes| U
  F -->|No| E
Loading

Reviews (7): Last reviewed commit: "Revert "fix(execution): count the runtim..." | Re-trigger Greptile

Comment thread apps/sim/app/api/function/execute/route.ts
icecrasher321 and others added 2 commits August 6, 2026 16:18
…ce envelope

The binary classifier read an absent mounted-file scanner as "no mounted
secrets". That is absence of evidence, not evidence of absence: the request
contract permits _sandboxFiles without the provenance envelope, so a caller
that mounts secret-bearing bytes and omits the envelope would have a derived
binary persisted as provably secret-free.

Not reachable today — the route is internal-JWT-only and its one file-mounting
caller always emits the envelope — but the classification rested on an
invariant nothing enforced.

- the copilot handler emits the envelope on the same condition that produces
  the mount, so tables ship one too and the two cannot drift apart
- a mount with no verified scanner now counts as secret material in scope, so
  the classification is never stronger than what the caller attested to

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/execution/mounted-file-secret-provenance.ts Outdated
Comment thread apps/sim/lib/copilot/tools/handlers/function-execute.ts Outdated
…nknown

Two ways the envelope could read as stronger evidence than it was.

The copilot handler preserved `_sandboxFiles` that arrived on the params and
then exported provenance from `mountedRegistry`, which knows only about the
files it resolved itself. The route would have read that partial envelope as a
complete attestation over every mounted byte. The envelope now covers the whole
mounted set or is not emitted at all, and a mount with no envelope already
fails closed.

`hasSecrets` was derived from whether entries produced scannable literals, so
an envelope listing entries that all failed to decrypt reported false and let a
derived binary be marked exact-empty. It now reflects what the envelope
attested to: entries that yield no plaintext make the mount less classifiable,
not more.

Neither was reachable — `_sandboxFiles` is absent from the copilot tool schema,
so nothing can populate the preserved-mount branch — but both had the
classification resting on a property nothing enforced.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 14ca769. Configure here.

Comment thread apps/sim/app/api/function/execute/route.ts
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 14ca769. Configure here.

`bun run tool-metadata:check` fails on origin/staging as well as here, so this
is not from this branch — #6317 landed the artifact generated from a factory
that still built a per-provider apiKey description, and the source was later
genericized without regenerating.

Regenerating changes exactly the five embeddings entries' apiKey description to
the text `tools/embeddings/factory.ts:74` actually produces. The per-provider
strings appear nowhere in source. Included here only because the gate is red on
every branch cut from staging until someone lands it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit ddc1c1a. Configure here.

Comment thread apps/sim/app/api/function/execute/route.ts
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>
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 754e37c. Configure here.

…in scope"

This reverts commit 754e37c.

The classifier's secret catalog is the Secrets feature and nothing else:
`outputSecretNamesByScanLiteral` and `outputSecretPlaintextsByName` are built
only from `envVars`, and mounted-file entries trace back to the same place.
`contextVariables`, `blockData`, and `workflowVariables` are ordinary workflow
data — resolved block outputs the user already sees in logs — and the text
export path does not scan them either.

Treating their mere presence as secret material was a heuristic, not a security
property, and it created exactly the asymmetry rejected two rounds earlier: a
binary derived from a context variable would be `unknown` while a text export of
the same bytes stays exact-empty. Stricter than the text path for the same
content is not a boundary.

It was also nearly inert. `scopeEnvironmentVariables` returns every workspace
secret when scope is `all` (the default), so any workflow Function block with
secrets configured already trips the env branch. The only slice it changed was
executions with no env vars at all, where the workspace has no secret for a
context variable to carry.

A Secret resolved into an upstream block's output and arriving here through
blockData is a real gap, but it is pre-existing, identical for text exports, and
belongs at the executor -> route boundary as a provenance envelope for params —
not as a presence check in this classifier.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 287995f. Configure here.

@icecrasher321
icecrasher321 merged commit 5ad820d into staging Aug 7, 2026
50 of 51 checks passed
@waleedlatif1
waleedlatif1 deleted the staging-v28 branch August 7, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant