Skip to content

Bug: workerdDebugPort fetcher fails requireAllowsTransfer() when used inside Proxy-based WorkerEntrypoint#6074

Closed
penalosa wants to merge 2 commits intomainfrom
penalosa/bug-debug-port-rpc-proxy
Closed

Bug: workerdDebugPort fetcher fails requireAllowsTransfer() when used inside Proxy-based WorkerEntrypoint#6074
penalosa wants to merge 2 commits intomainfrom
penalosa/bug-debug-port-rpc-proxy

Conversation

@penalosa
Copy link
Contributor

@penalosa penalosa commented Feb 13, 2026

Bug Report: .apply() on JsRpcProperty silently chains through wildcard property

Summary

JsRpcProperty (returned by wildcard property access on Fetcher, e.g. fetcher.ping) has JSG_CALLABLE + JSG_WILDCARD_PROPERTY. Because it's a callable object (via SetCallAsFunctionHandler) rather than a v8::Function, it does not inherit Function.prototype.apply/call/bind.

Accessing .apply on a JsRpcProperty hits the wildcard handler instead, creating a new JsRpcProperty for the path ping.apply. Calling that makes an RPC call, serializing all arguments — including any Fetcher passed as this.

This is a footgun: typeof method === 'function' returns true, but method.apply(...) does something completely different from what you'd expect.

Reproduction

samples/debug-port-rpc-repro/ — a Proxy-based WorkerEntrypoint that forwards arbitrary RPC methods via a debug port fetcher.

The broken pattern:

const method = fetcher[methodName];        // JsRpcProperty
return method.apply(fetcher, args);        // NOT Function.prototype.apply!
// → fetcher.ping.apply(fetcher, args)     // wildcard chains to 'ping.apply'
// → serializes fetcher as RPC arg         // Fetcher::serialize() → requireAllowsTransfer() → 💥

The fix:

return fetcher[methodName](...args);       // direct call, no chaining

Impact

Any code that does typeof x === 'function' and then uses .apply() or .call() will silently do the wrong thing on JsRpcProperty. This is particularly likely in proxy/forwarding patterns (miniflare's ExternalServiceProxy, generic RPC forwarders).

Possible improvements

  1. Documentation — note that JsRpcProperty / RPC stubs are callable objects, not Functions
  2. Guard .apply/.call/.bind on JsRpcProperty — intercept these names in the wildcard handler and either throw a clear error or delegate to Function.prototype
  3. Consider making JsRpcProperty a proper Function — so instanceof Function and .apply() work as expected

…zing the Fetcher

JsRpcProperty has JSG_WILDCARD_PROPERTY, so fetcher.ping.apply creates
another JsRpcProperty for path 'ping.apply'. Calling that serializes
the fetcher argument, hitting requireAllowsTransfer().

Fix: use fetcher[methodName](...args) instead of method.apply(fetcher, args).
@penalosa penalosa closed this Feb 13, 2026
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