@@toStringTag spoofing for null and undefined

Brendan Eich brendan at mozilla.org
Tue Jan 20 19:20:15 PST 2015

Mark S. Miller wrote:
>     Hmm, maybe -- but does Self have a reference-identity
>     equivalence-relation operator that can't be spoofed? Might help to
>     ask David, but to abstract from that particular SPLASH 2011 Q&A,
>     obviously we won't be enabling such fakery in JS.
> I don't get it. What are you proposing to change? It seems we have 
> agreement on the following integrity invariants:

Relax, I'm not proposing yet, just discussing. The current sub-thread 
started with the "nominal types bad!" assertion. I found that 
provocative claim inspiring; it reminded me of the SPLASH talk Tom gave. 
So I asked whether we can ever extend JS to allow meta-programming such 
that any type could be proxied.

If so, we'd have no nominal types, but we all agree that some cases need 
a trademark or brand integrity test that can't be subverted. In the 
limit, JS would have branding when needed on top of structural types, 
with branding based on an identity test (David showed the mirror one for 
Self) that cannot be proxied.

> * The object state invariants that were first codified in ES5 and 
> further refined in the ES6 text, and that Direct Proxies were designed 
> to enforce.


> * typeof x === "number" and similar, for all the typeof strings 
> defined in ES5, as reliable but coarse brands. typeof x === "function" 
> does not mean that x is not a proxy, but only if its target is a 
> function (or a proxy whose target...)

Right. I think this takes in the (typeof x == typeof y && x == y <=>  x 
=== y) two-way implication.

> * Object.prototype.toString.call(x) === "[object Date]" and similar, 
> but only those, since some legacy ES5 code depends on the integrity of 
> those tests. For example, compromising this would introduce security 
> holes into some Caja code. These are less coarse than typeof, still 
> string-based and non-extensible as a branding mechanism.

The details for this item are at issue.

> Array.isArray(x), where true does not mean that is is not a proxy, but 
> only if its target is an array (or a proxy whose target...)

And Array is a nominal type, right? More below.

> * === itself
> * WeakMap key lookup, since this follows from preserving the integrity 
> of ===
> * A proxy's target cannot be mutated, though a revocable proxy's 
> target can be dropped (by revocation)
> So, given that we're keeping all the above, what are you proposing to 
> weaken?

I'm not proposing to weaken anything.

The questions raised in this thread swirl around why people continue to 
use Object.prototype.toString.call(x) to query some 
larger-than-we'd-like set of nominal types. If that set can be 
ring-fenced, condemned as bad legacy ("bad!"), and obsoleted over deep 
time, then great -- but in that future, the evolved language must not 
enable integrity bugs based on some brand test or other that replaces 

If we could have a more definite idea of the rules then, we could do a 
better job finalizing toStringTag in ES6.

But this still seems too speculative right now. I agree that the bullet 
list you give above, _sans_ the O.p.toString.call item, *should* be 
enough. Yet there is room for doubt. Consider: we just amended 
Array.isArray to return true for a proxy whose (ultimate) target is an 
Array instance. Array.isArray was added to ES5 because instanceof is 
realm-specific because (prototype) object identity-specific.

For now it still seems that nominal types exposed via built-ins still 
matter in ways that can't be modeled by object identity. The answer in 
the future is branding, which cannot use weak maps or === or other 
object (reference) identity tests. ISTM we need more definite consensus 
on branding to finish off toStringTag in ES6.


More information about the es-discuss mailing list