-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
attempt to define brand check #30
Conversation
terminology.md
Outdated
#### Definition: | ||
|
||
Brand check ("brand" as in a mark, or a brand made with a branding iron) is a term used by the TC39 | ||
to describe a check against a unique datatype whose createion is controlled by a piece of code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spelling -- createion -> creation (typing too fast!)
terminology.md
Outdated
However, this is not limited to datatypes that are implemented as a part of JavaScript. Brand checks | ||
are possible in user space as long as there is a way to identify that the object is unique. | ||
|
||
Imagine a library that implements dom queries and returns a `query` object. The author of this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dom -> DOM
terminology.md
Outdated
|
||
set query(queryString) { | ||
// generate a query object | ||
const query = performQuery(queryString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing a this.
terminology.md
Outdated
class Query { | ||
// ... | ||
|
||
performQuery(queryString { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing paren
This looks awesome! I'm not sure if the And I believe that after ES6 added So all of that confusing detail might make it not the most helpful example for a newcomer. |
terminology.md
Outdated
#### Example: | ||
|
||
One example of this is built in JavaScript datatypes, which are unique and cannot be made in user | ||
space. For example, `toString` can be used on the `new Date` object, and this is a unique identifier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this example might not be great post-ES6, because of Symbol.toStringTag.
Array.isArray
is a brand check, and Date.prototype.valueOf.call(date)
will throw if date
isn't a date object (iow it contains a brand check).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super! thanks for the examples!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah Array.isArray
is a nice and more accessible example.
terminology.md
Outdated
|
||
#### Sources | ||
[esdiscuss comment](https://esdiscuss.org/topic/tostringtag-spoofing-for-null-and-undefined#content-3) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should link to forgery definition ?
I think it would be helpful, as an intuition, to say something like "this is what you may think At least for me, the way I encountered this (and things like In the context of TC39, this also seems to come up for similar reasons. |
@chancancode interesting, how was this described to you? then we can add that to the definition as well, because that sounds rather unintuitive |
(I'm in the middle of typing a long comment but we gotta go, will finish this tonight) |
Note, that the actual brand mechanism for Date and most other builtins is the existence if a specific internal slot. The check is the invocation of any built in method that access the internal. It might be worth mentioning is that branding in JS can't be reliability based upon inheritance-hierarchy tests such as instanceof |
This is probably TMI and needs to be condensed, but this is how I think about it (and I could be wrong). Since JavaScript is a dynamically typed language, sometimes you may need to confirm that a value you received is of a particular type at runtime. In order to do that, we first have to define what "of a particular type" means. On one end of the spectrum, maybe all you care is that the value conforms to the interface you expect (i.e. it has the right properties and methods that you need for your usage). This is sometimes called a "structural" type check. For example: if (typeof value.toString === 'function') {
// ...now we know value is a "ToString-able"
} On the other end of the spectrum, sometimes that is not sufficient. You may need to know that the value is exactly what you expect. For example, you may have a method that should only work on arrays (the built-in type in JavaScript), not "array-like" objects such as an This is sometimes called a "nominal" type check. A "brand check" is an abstract operation that allows you to reliably determine that. Why would you need to do that? Here are a few possible reasons:
For the first use case, let's say you want to implement function prettyPrint(v) {
if (v instanceof Array) {
let entries = v.map(prettyPrint);
return `[${entries}]`;
} else if (typeof v === "object") {
let entries = Object.entries(v).map(([key, value]) => {
return `${prettyKey(key)}: ${prettyValue(value)}`;
}).join(", ");
return `{${entries}}`;
} else if (typeof v === "string") {
return `"${v}"`;
} else {
return String(v);
}
} However, the sameOriginIframe.contentWindow.eval("[]") instanceof Array // => false The reason here is that sameOriginIframe.contentWindow.Array === Array // => false ...therefore the check fails. A more reliable way to perform this check is One way to understand how this works is that it is implemented as a brand check: every time an array is created, it is "stamped" with an internal "brand" that only the engine has access to. Seeing how this could be implemented in user-land may help. Suppose you want to implement a type that and you want to provide the same guarantee that it will work across iframes. (Another reason to care is that in Node it's pretty common to have multiple copies of the same library loaded, so an This is one way you could implement that with a brand check instead of relying on // public constructor
export function moment(v) {
if (isMoment(v)) {
// already wrapped
return v;
} else {
return new Moment(v);
}
}
export function isMoment(v) {
return typeof v === "object" && v._isMoment === true;
}
// internal
class Moment {
constructor(v) {
this._isMoment = true;
// ...
}
} In this case, For the second use case, here is an alternative example that (I think) drives home the point a bit more directly. Let's say we want to re-implement the // internal
const callbackId = 0;
const callbacks = [];
function registerCallback(callback) {
let id = callbackId++;
callbacks[id] = callback;
return id;
}
function unregisterCallback(id) {
callbacks[id] = null;
}
export function setTimeout(callback) {
return registerCallback(callback);
}
export function clearTimeout(id) {
unregisterCallback(id);
}
export function setInterval(callback) {
return registerCallback(callback);
}
export function clearInterval(id) {
unregisterCallback(id);
} As you can see, the public API is that However, in our implementation, the unique identity happens to be a number. Furthermore, To avoid these problems, you can return a branded object from For use case number 3, let's say we want to mark a certain value as "trusted". For example: export function htmlSafe(value) {
// ...return a branded wrapper
}
export function isHTMLSafe(value) {
// ...check for the brand
}
export function render(value) {
if (isHTMLSafe(value)) {
document.body.innerHTML = value.toString();
} else {
document.body.innerText = value.toString();
}
} The key here is that you want to remember whether you have previous marked a particular value as "trusted". It's probably important to point out that since "brand check" is an abstract operation, there are numerous ways to implement that, ranging from Each of these offers different guarantees, and depending on the context it came up and the motivations for implementing the brand check, some of these may not be appropriate. For example, if moment only cares about interop between execution contexts, they may not have to worry forgeability (if you intentionally do weird things like passing On the other hand, if it security/privacy is a goal, you would have to define your threat model clearly and evaluate which implementation strategy is appropriate. For example, it's probably not acceptable to implement the In the context of TC39, the term may come up for a number of reasons. In the most narrow sense (and perhaps the most "formal" sense), it usually describes an internal, cross-execution-context way to check for certain built-in types. This could be useful for defining user-facing APIs, such as However, "branding" could also come up in other discussion context in a broader, more abstract sense. For example, it may come up when describing one of the aforementioned userland patterns (e.g. "How do developers solve X problem today? They use a brand check.") or discussing userland polyfills. In these cases, the concrete mechanism may or may not be important. Once again, I think this is probably too long for the actual text you are writing, but hopefully it can be a useful input into that. |
@chancancode thanks for the write up! I will link to your comment in the definition and create a short summary of what you said, but if someone wants more context then your comment will be really useful. |
updated |
Co-Authored-By: codehag <[email protected]>
Co-Authored-By: codehag <[email protected]>
Thanks to David for helping me understand this a bit better. Would be great to get comments to improve this definition.