Skip to content

fix(core): return Dates from the single VM as Dates - #98

Merged
frontegg-david merged 1 commit into
feat/core-sandbox-adapter-optionfrom
fix/core-single-vm-builtin-results
Oct 4, 2026
Merged

frontegg-david merged 1 commit into
feat/core-sandbox-adapter-optionfrom
fix/core-single-vm-builtin-results

Conversation

@frontegg-david

@frontegg-david frontegg-david commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Stack 6/10: main ← #92 ← #93 ← #94 ← #95 ← #96 ← this ← #99 ← #100 ← #101 ← #102

Follow-up to #94, which made the double VM return a script's Date as a Date but left the single VM (doubleVm: { enabled: false }) returning {}.

Root cause

The single VM's membrane (libs/core/src/secure-proxy.ts) wraps every Date a script constructs: the construct trap returns proxyWithDepth(instance). A proxy has no [[DateValue]] slot, so the sanitizer's builtin check from #94 correctly refused it and copied it as a plain object ({}). Maps and Sets are in NON_PROXYABLE_TYPES and were never wrapped, so only Dates (and RegExps, which validated AgentScript can't create) were affected.

What I checked on each adapter:

double VM single VM before single VM after
script returns { date: new Date(0), nested: [new Date(1)] } Dates { date: {}, nested: [{}] } Dates
callTool('t', { when: new Date(2) }), string bridge ISO string ISO string ISO string
same, direct bridge — Date Date

Fix

  • unwrapSecureProxy(value) in secure-proxy.ts returns the object a membrane proxy wraps, from the existing proxyToTarget map. It's not exported from the package.
  • sanitizeValue takes an unwrap option. The unwrapped object is used only to recognize and read builtins (Date, Map, Set, RegExp) with the captured host methods, and the result is a new host value (new Date(time)). Plain objects and arrays are still read through the proxy as before, and nothing unwrapped goes back to the sandbox.
  • VmAdapter passes unwrap: unwrapSecureProxy when it sanitizes the script result.

Not changed: the worker_threads adapter

It has its own result path: worker-script.ts:413 runs sanitizeObject (safe-deserialize.ts:175) and sends the result as a JSON string. A Date comes back as {}, and a BigInt result fails the run with Do not know how to serialize a BigInt (core's sanitizer returns '10'). Fixed on top of this stack in #100.

Red → green

libs/core/src/__tests__/enclave.result-sanitization.spec.ts: the end-to-end cases now run on the double VM and the single VM, plus 3 unit tests for unwrapSecureProxy and the unwrap option. With vm-adapter.ts reverted: the single VM's returns a Date as a Date fails (Received value: {}). With the fix: 9 passed.

Verification

  • core: jest -c libs/core/jest.config.ts --testPathIgnorePatterns /perf/: 66 suites, 2359 passed, 1 skipped; perf suite: 48 passed
  • nx lint core, nx build core, prettier: pass

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

Next included review available in 17 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 3 included reviews currently available. Your 43 included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: agentfront/enclave/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 4b832e83-8fd0-43b1-b68f-3177491b8cc6
📥 Commits

Reviewing files that changed from the base of the PR and between 09a9518 and 31581b4.

📒 Files selected for processing (4)
  • libs/core/src/__tests__/enclave.result-sanitization.spec.ts
  • libs/core/src/adapters/vm-adapter.ts
  • libs/core/src/secure-proxy.ts
  • libs/core/src/value-sanitizer.ts
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

With the double VM off, a script's `new Date(0)` still came back as {}.
The single VM's membrane wraps every Date a script constructs in a
secure proxy, and a proxy has no [[DateValue]] slot, so the sanitizer's
builtin check (#94) refused it and copied it as a plain object.

The membrane now exports unwrapSecureProxy (from its existing
proxy-to-target map; not part of the package API), and sanitizeValue
takes an unwrap option. The single VM passes it for the script result:
the sanitizer recognizes and reads builtins (Date, Map, Set, RegExp)
from the unwrapped target with the captured host builtins and returns
a new host Date. Everything else is still read through the proxy, and
nothing unwrapped is handed back to the sandbox.

Tool arguments were not affected (Dates already arrive as ISO strings,
or as Dates with the direct bridge).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frontegg-david
frontegg-david force-pushed the fix/core-single-vm-builtin-results branch from 72aa52d to 31581b4 Compare October 4, 2026 00:23
@frontegg-david
frontegg-david merged commit 7c819fb into main Oct 4, 2026
8 checks passed
@frontegg-david
frontegg-david deleted the fix/core-single-vm-builtin-results branch October 4, 2026 10:55
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.

2 participants