tc39 / proposal-array-is-template-object

TC39 proposal to identify tagged template string array objects
https://tc39.es/proposal-array-is-template-object/
MIT License
39 stars 7 forks source link

Return false in cross-Realm case? #3

Closed littledan closed 5 years ago

littledan commented 5 years ago

In my alternate suggestion https://github.com/tc39/ecma262/pull/1350 for a proposal in this space, I suggested adding an internal slot on template objects to serve as the identifying tag. The observable difference here is that, in your spec text in this repository, a cross-Realm template will return false. Was this the intention? If so, would it make sense to document the rationale in the README?

mikesamuel commented 5 years ago

We should definitely document that.

Does the below resonate?


TT is interested in strengthening arguments like:

We trust the author of TrustedHTMLfoo to mark "foo" as trusted because we trust them to write HTML that runs in the current realm.

More generally,

We trust the author of x to write code that runs in the current realm so it's safe to use f(x, ...) to ...

Most realms that can interact are probably in the same origin, but if frozen realms succeeds in carving out a niche for untrusted code to run alongside trusted realms it may not.

<iframe>s are one of the most common cases where realms interact. One frame's DOM content is separate from others in the same origin unless JS code goes out of its way to introduce them.


@koto @xtofian Thoughts?

littledan commented 5 years ago

It's not so hard for same-origin iframes to find each other, but at the same time, if they are the same origin, I am not sure what sense it makes to keep them separate. So I don't understand the purpose of returning false for this case.

koto commented 5 years ago

I believe we want to assert that a template literal object was part of the js code that is running in a given realm, and not one that was created elsewhere, e.g. in a different frame. That, for example, allows us define a runtime enforcement that's closer to what we can verify statically (e.g. if a document includes a single script that passed linter/compiler checks).

littledan commented 5 years ago

Why is enforcement matching static verification a goal? I'd imagine that static verification is somewhat complementary to what this proposal achieves.

I'd be curious to learn more about how this relates to web platform security in a more broad way. To what extent are we trying to defend against same-origin frames? cc @mkwst

ljharb commented 5 years ago

A cross-realm slot check would be most consistent with the language precedent.

If you also want to check same-realm, then because it’s frozen, you can use instanceof Array.

mikesamuel commented 5 years ago

@ljharb

Thanks. I hadn't thought of that.

I now agree that there's no need to limit w.r.t. realm, and making a realm agnostic version could be more generally useful.

// Use case realm agnostic

const trustedRealmsArrays = new Set([Array.prototype, ...otherRealmsArrayProtos]);

function sensitiveTag(staticBits, ...dynamicBits) {
  if (!Array.isTempalteObject(staticBits) && trustedRealmsArray.has(Object.getPrototypeOf(staticBits))) {
    throw new Error(...);
  }
  ...
}

Unless anyone objects, I'll draft a realm agnostic version of the abstraction with a non-normative note about the Realminess and instanceof Array trick.

littledan commented 5 years ago

Not sure what you're getting at with instanceof Array; the semantics are still a bit different due to @@hasInstance.

ljharb commented 5 years ago

@littledan yes but on an object that passes the cross-realm brand check, it will be a frozen array, with no Symbol.hasInstance, so const isSameRealmTemplateObject = isTemplateObject(obj) && obj instanceof Array will be reliable.

littledan commented 5 years ago

OK. I'm not sure how that's relevant to @koto 's earlier point (but I also don't quite understand that argument).

ljharb commented 5 years ago

I believe we want to assert that a template literal object was part of the js code that is running in a given realm, and not one that was created elsewhere,

That's precisely what my code snippet above asserts, without needing to violate precedent by having a same-realm slot check.

mikesamuel commented 5 years ago

Ok. So instead of having an IsTemplateObject abstraction that mirrors GetTemplateObject, I'm going to modify GetTemplateObject somewhere between

  1. Let template be ! ArrayCreate(count).

and

  1. Perform SetIntegrityLevel(template, "frozen").

to add a step like

  1. Set template.[[TemplateObject]] to true.

and then the IsTemplateObject abstraction would just check that there is such a property and its value is true.

littledan commented 5 years ago

I would recommend instead creating the template with the internal slot, and leaving it as undefined, as I did in https://github.com/tc39/ecma262/pull/1350

ljharb commented 5 years ago

It seems much cleaner to me to have a slot with a value that’s checked, then to pivot on the mere presence of a slot.