Skip to content

Five web builtins return undefined for a prototype-method value-read, and URL.prototype.toString silently resolves to the wrong function #10808

Description

@proggeramlug

Reading a prototype method as a value returns undefined for five web builtins, and returns the wrong function for a sixth. Confirmed empirically, not inferred.

These are the remaining members of the deliberate #10555 group in crates/perry-runtime/src/object/global_this/proto_methods.rs, whose arms install only a Symbol.toStringTag descriptor. The seventh member, URLSearchParams, is fixed by #10759 / PR #10807; this issue covers the other six, audited while fixing it.

Results

builtin value-read of a prototype method verdict
AbortController typeof undefined same defect
AbortSignal typeof undefined same defect
CustomEvent typeof undefined same defect
Event typeof undefined same defect
EventTarget typeof undefined same defect
URL.prototype.toJSON typeof undefined same defect
URL.prototype.toString resolves to a callable — the wrong one worse; see below

Why URL.prototype.toString is the more dangerous case

The other failures are loud: you read a method, get undefined, and the next .call() throws. URL.prototype.toString instead resolves to the inherited generic Object.prototype.toString, which is callable and returns "[object URL]" rather than the URL string.

So it does not throw. It silently returns a plausible-looking wrong value. Any code that reads toString as a value — a serializer, a logger, a template helper, anything doing String(x) through an indirection — gets "[object URL]" where Node gives the href. That is a correctness bug that no crash will surface.

Mechanism

The #10555 group's arms were written on the premise that these builtins' methods are reached by type-directed static dispatch or handle tables, so reified prototype closures were unnecessary — each arm installs only the toStringTag descriptor for reflection. That premise holds for x.method() and has no answer for a method read as a value: the read finds nothing installed and falls through to undefined, or in URL's case to an inherited generic.

This is a gap in a deliberate design rather than a set of forgotten entries, which matters for the fix: the question is what a statically-dispatched builtin should do when one of its methods is read as a value, not why six entries were missed.

Note on scope

Filed as an audit result, deliberately not fixed alongside #10759 — that PR fixes one member and this issue records the other six with evidence. Whoever takes this should decide whether the right remedy is six more arms or a general answer for the group, and URL.prototype.toString should be handled first: it is the only one that fails silently.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindings

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions