diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /dom/docs/scriptSecurity | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream/115.8.0esr.tar.xz firefox-esr-upstream/115.8.0esr.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/docs/scriptSecurity')
-rw-r--r-- | dom/docs/scriptSecurity/images/compartments.png | bin | 0 -> 208057 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/images/computing-a-wrapper.png | bin | 0 -> 282979 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/images/cross-compartment-wrapper.png | bin | 0 -> 53671 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/images/cross-origin-wrapper.png | bin | 0 -> 56232 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/images/opaque-wrapper.png | bin | 0 -> 55336 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/images/principal-relationships.png | bin | 0 -> 147630 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/images/same-origin-wrapper.png | bin | 0 -> 56603 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/images/xray-wrapper.png | bin | 0 -> 55418 bytes | |||
-rw-r--r-- | dom/docs/scriptSecurity/index.rst | 318 | ||||
-rw-r--r-- | dom/docs/scriptSecurity/xray_vision.rst | 411 |
10 files changed, 729 insertions, 0 deletions
diff --git a/dom/docs/scriptSecurity/images/compartments.png b/dom/docs/scriptSecurity/images/compartments.png Binary files differnew file mode 100644 index 0000000000..c80ea166ca --- /dev/null +++ b/dom/docs/scriptSecurity/images/compartments.png diff --git a/dom/docs/scriptSecurity/images/computing-a-wrapper.png b/dom/docs/scriptSecurity/images/computing-a-wrapper.png Binary files differnew file mode 100644 index 0000000000..1221ff4a00 --- /dev/null +++ b/dom/docs/scriptSecurity/images/computing-a-wrapper.png diff --git a/dom/docs/scriptSecurity/images/cross-compartment-wrapper.png b/dom/docs/scriptSecurity/images/cross-compartment-wrapper.png Binary files differnew file mode 100644 index 0000000000..ba31355d44 --- /dev/null +++ b/dom/docs/scriptSecurity/images/cross-compartment-wrapper.png diff --git a/dom/docs/scriptSecurity/images/cross-origin-wrapper.png b/dom/docs/scriptSecurity/images/cross-origin-wrapper.png Binary files differnew file mode 100644 index 0000000000..14b7022c3d --- /dev/null +++ b/dom/docs/scriptSecurity/images/cross-origin-wrapper.png diff --git a/dom/docs/scriptSecurity/images/opaque-wrapper.png b/dom/docs/scriptSecurity/images/opaque-wrapper.png Binary files differnew file mode 100644 index 0000000000..67cd87ba5b --- /dev/null +++ b/dom/docs/scriptSecurity/images/opaque-wrapper.png diff --git a/dom/docs/scriptSecurity/images/principal-relationships.png b/dom/docs/scriptSecurity/images/principal-relationships.png Binary files differnew file mode 100644 index 0000000000..0a4e7014f6 --- /dev/null +++ b/dom/docs/scriptSecurity/images/principal-relationships.png diff --git a/dom/docs/scriptSecurity/images/same-origin-wrapper.png b/dom/docs/scriptSecurity/images/same-origin-wrapper.png Binary files differnew file mode 100644 index 0000000000..ec966afa1b --- /dev/null +++ b/dom/docs/scriptSecurity/images/same-origin-wrapper.png diff --git a/dom/docs/scriptSecurity/images/xray-wrapper.png b/dom/docs/scriptSecurity/images/xray-wrapper.png Binary files differnew file mode 100644 index 0000000000..43316d9089 --- /dev/null +++ b/dom/docs/scriptSecurity/images/xray-wrapper.png diff --git a/dom/docs/scriptSecurity/index.rst b/dom/docs/scriptSecurity/index.rst new file mode 100644 index 0000000000..482c5e888c --- /dev/null +++ b/dom/docs/scriptSecurity/index.rst @@ -0,0 +1,318 @@ +Script Security +=============== + +.. container:: summary + + This page provides an overview of the script security architecture in + Gecko. + +Like any web browser, Gecko can load JavaScript from untrusted and +potentially hostile web pages and run it on the user's computer. The +security model for web content is based on the `same-origin policy +<https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy>`__, +in which code +gets full access to objects from its origin but highly restricted access +to objects from a different origin. The rules for determining whether an +object is same-origin with another, and what access is allowed +cross-origin, are now mostly standardized across browsers. + +Gecko has an additional problem, though: while its core is written in +C++, the front-end code is written in JavaScript. This JavaScript code, +which is commonly referred to as c\ *hrome code*, runs with system +privileges. If the code is compromised, the attacker can take over the +user's computer. Legacy SDK extensions also run with chrome privileges. + +Having the browser front end in JavaScript has benefits: it can be much +quicker to develop in JavaScript than in C++, and contributors do not +need to learn C++. However, JavaScript is a highly dynamic, malleable +language, and without help it's difficult to write system-privileged +code that interacts safely with untrusted web content. From the point of +view of chrome code, the script security model in Gecko is intended to +provide that help to make writing secure, system-privileged JavaScript a +realistic expectation. + +.. _Security_policy: + +Security policy +--------------- + +Gecko implements the following security policy: + +- **Objects that are same-origin** are able to access each other + freely. For example, the objects associated with a document served + from *https://example.org/* can access each other, and they can also + access objects served from *https://example.org/foo*. +- **Objects that are cross-origin** get highly restricted access to + each other, according to the same-origin policy. + For example, code served from *https://example.org/* trying to access + objects from *https://somewhere-else.org/* will have restricted + access. +- **Objects in a privileged scope** are allowed complete access to + objects in a less privileged scope, but by default they see a + `restricted view <#privileged-to-unprivileged-code>`__ + of such objects, designed to prevent them from being tricked by the + untrusted code. An example of this scope is chrome-privileged + JavaScript accessing web content. +- **Objects in a less privileged scope** don't get any access to + objects in a more privileged scope, unless the more privileged scope + `explicitly clones those objects <#unprivileged-to-privileged-code>`__. + An example of this scope is web content accessing objects in a + chrome-privileged scope. + +.. _Compartments: + +Compartments +------------ + +Compartments are the foundation for Gecko's script security +architecture. A compartment is a specific, separate area of memory. In +Gecko, there's a separate compartment for every global object. This +means that each global object and the objects associated with it live in +their own region of memory. + +.. image:: images/compartments.png + +Normal content windows are globals, of course, but so are chrome +windows, sandboxes, workers, the ``ContentFrameMessageManager`` in a frame +script, and so on. + +Gecko guarantees that JavaScript code running in a given compartment is +only allowed to access objects in the same compartment. When code from +compartment A tries to access an object in compartment B, Gecko gives it +a *cross-compartment wrapper*. This is a proxy in compartment A for the +real object, which lives in compartment B. + +.. image:: images/cross-compartment-wrapper.png + +Inside the same compartment, all objects share a global and are +therefore same-origin with each other. Therefore there's no need for any +security checks, there are no wrappers, and there is no performance +overhead for the common case of objects in a single window interacting +with each other. + +Whenever cross-compartment access happens, the wrappers enable us to +implement the appropriate security policy. Because the wrapper we choose +is specific to the relationship between the two compartments, the +security policy it implements can be static: when the caller uses the +wrapper, there's no need to check who is making the call or where it is +going. + +.. _Cross-compartment_access: + +Cross-compartment access +------------------------ + +.. _Same-origin: + +Same-origin +~~~~~~~~~~~ + +As we've already seen, the most common scenario for same-origin access +is when objects belonging to the same window object interact. This all +takes place within the same compartment, with no need for security +checks or wrappers. + +When objects share an origin but not a global - for example two web +pages from the same protocol, port, and domain - they belong to two +different compartments, and the caller gets a *transparent wrapper* to +the target object. + +.. image:: images/same-origin-wrapper.png + +Transparent wrappers allow access to all the target's properties: +functionally, it's as if the target is in the caller's compartment. + +.. _Cross-origin: + +Cross-origin +~~~~~~~~~~~~ + +If the two compartments are cross-origin, the caller gets a +*cross-origin wrapper*. + +.. image:: images/cross-origin-wrapper.png + +This denies access to all the object's properties, except for a few +properties of Window and Location objects, as defined by +the `same-origin +policy <https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#cross-origin_script_api_access>`__. + +.. _Privileged_to_unprivileged_code: + +Privileged to unprivileged code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The most obvious example of this kind of security relation is between +system-privileged chrome code and untrusted web content, but there are +other examples in Gecko. The Add-on SDK runs content scripts in +sandboxes, which are initialized with an `expanded +principal <#expanded-principal>`__, +giving them elevated privileges with respect to the web content they +operate on, but reduced privileges with respect to chrome. + +If the caller has a higher privilege than the target object, the caller +gets an *Xray wrapper* for the object. + +.. image:: images/xray-wrapper.png + +Xrays are designed to prevent untrusted code from confusing trusted code +by redefining objects in unexpected ways. For example, privileged code +using an Xray to a DOM object sees only the original version of the DOM +object. Any expando properties are not visible, and if any native DOM properties have been +redefined, they are not visible in the Xray. + +The privileged code is able to waive Xrays if it wants unfiltered access to the untrusted object. + +See `Xray vision <xray_vision.html>`__ for much more information on Xrays. + +.. _Unprivileged_to_privileged_code: + +Unprivileged to privileged code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the caller has lower privileges than the target object, then the +caller gets an *opaque wrapper.* + +.. image:: images/opaque-wrapper.png + +An opaque wrapper denies all access to the target object. + +However, the privileged target is able to copy objects and functions +into the less privileged scope using the ``exportFunction()`` and +``cloneInto()`` functions, and the less privileged scope is then able +to use them. + +.. _Security_checks: + +Security checks +--------------- + +To determine the security relation between two compartments, Gecko uses +two concepts: *security principals* and the act of *subsuming*. To +establish the security relationship between two compartments A and B, +Gecko asks: + +*Does the security principal for compartment A subsume the security +principal for compartment B, and vice versa?* + +.. _Subsumes: + +Subsumes +~~~~~~~~ + ++-----------------------------------+-----------------------------------+ +| *A subsumes B* | A has all of the privileges of B, | +| | and possibly more, and therefore | +| | A is allowed to see and do | +| | anything that B can see and do. | ++-----------------------------------+-----------------------------------+ +| *A Subsumes B &&* *B Subsumes A* | A and B are same-origin. | ++-----------------------------------+-----------------------------------+ +| *A Subsumes B && B !Subsumes A* | A is more privileged than B. | +| | | +| | A gets access to all of B, by | +| | default with Xray vision, which | +| | it may choose to waive. | +| | | +| | B gets no access to A, although A | +| | may choose to export objects to | +| | B. | ++-----------------------------------+-----------------------------------+ +| *A !Subsumes B && B !Subsumes A* | A and B are cross-origin. | ++-----------------------------------+-----------------------------------+ + +.. _Security_principals: + +Security principals +~~~~~~~~~~~~~~~~~~~ + +There are four types of security principal: the system principal, +content principals, expanded principals, and the null principal. + +.. _System_principal: + +System principal +^^^^^^^^^^^^^^^^ + +The system principal passes all security checks. It subsumes itself and +all other principals. Chrome code, by definition, runs with the system +principal, as do frame scripts. + +.. _Content_principal: + +Content principal +^^^^^^^^^^^^^^^^^ + +A content principal is associated with some web content and is defined +by the origin +of the content. For example, a normal DOM window has a content principal +defined by the window's origin. A content principal subsumes only other +content principals with the same origin. It is subsumed by the system +principal, any expanded principals that include its origin, and any +other content principals with the same origin. + +.. _Expanded_principal: + +Expanded principal +^^^^^^^^^^^^^^^^^^ + +An expanded principal is specified as an array of origins: + +.. code:: + + ["http://mozilla.org", "http://moz.org"] + +The expanded principal subsumes every content principal it contains. The +content principals do not subsume the expanded principal, even if the +expanded principal only contains a single content principal. + +Thus ``["http://moz.org"]`` subsumes ``"http://moz.org"`` but not vice +versa. The expanded principal gets full access to the content principals +it contains, with Xray vision by default, and the content principals get +no access to the expanded principal. + +This also enables the script security model to treat compartments that +have expanded principals more like part of the browser than like web +content. This means, for example, that it can run when JavaScript is +disabled for web content. + +Expanded principals are useful when you want to give code extra +privileges, including cross-origin access, but don't want to give the +code full system privileges. For example, expanded principals are used +in the Add-on SDK to give content scripts cross-domain privileges for a predefined set of +domains, +and to protect content scripts from access by untrusted web content, +without having to give content scripts system privileges. + +.. _Null_principal: + +Null principal +^^^^^^^^^^^^^^ + +The null principal fails almost all security checks. It has no +privileges and can't be accessed by anything but itself and chrome. It +subsumes no other principals, even other null principals. (This is what +is used when HTML5 and other specs say "origin is a globally unique +identifier".) + +.. _Principal_relationships: + +Principal relationships +~~~~~~~~~~~~~~~~~~~~~~~ + +The diagram below summarizes the relationships between the different +principals. The arrow connecting principals A and B means "A subsumes +B". (A is the start of the arrow, and B is the end.) + +.. image:: images/principal-relationships.png + +.. _Computing_a_wrapper: + +Computing a wrapper +------------------- + +The following diagram shows the factors that determine the kind of +wrapper that compartment A would get when trying to access an object in +compartment B. + +.. image:: images/computing-a-wrapper.png diff --git a/dom/docs/scriptSecurity/xray_vision.rst b/dom/docs/scriptSecurity/xray_vision.rst new file mode 100644 index 0000000000..8a8a093201 --- /dev/null +++ b/dom/docs/scriptSecurity/xray_vision.rst @@ -0,0 +1,411 @@ +Xray Vision +=========== + +.. container:: summary + + Xray vision helps JavaScript running in a privileged security context + safely access objects created by less privileged code, by showing the + caller only the native version of the objects. + +Gecko runs JavaScript from a variety of different sources and at a +variety of different privilege levels. + +- The JavaScript code that along with the C++ core, implements the + browser itself is called *chrome code* and runs using system + privileges. If chrome-privileged code is compromised, the attacker + can take over the user's computer. +- JavaScript loaded from normal web pages is called *content code*. + Because this code is being loaded from arbitrary web pages, it is + regarded as untrusted and potentially hostile, both to other websites + and to the user. +- As well as these two levels of privilege, chrome code can create + sandboxes. The security principal defined for the sandbox determines + its privilege level. If an + Expanded Principal is used, the sandbox is granted certain privileges + over content code and is protected from direct access by content + code. + +| The security machinery in Gecko ensures that there's asymmetric access + between code at different privilege levels: so for example, content + code can't access objects created by chrome code, but chrome code can + access objects created by content. +| However, even the ability to access content objects can be a security + risk for chrome code. JavaScript's a highly malleable language. + Scripts running in web pages can add extra properties to DOM objects + (also known as expando properties) + and even redefine standard DOM objects to do something unexpected. If + chrome code relies on such modified objects, it can be tricked into + doing things it shouldn't. +| For example: ``window.confirm()`` is a DOM + API that's supposed to ask the user to confirm an action, and return a + boolean depending on whether they clicked "OK" or "Cancel". A web page + could redefine it to return ``true``: + +.. code:: JavaScript + + window.confirm = function() { + return true; + } + +Any privileged code calling this function and expecting its result to +represent user confirmation would be deceived. This would be very naive, +of course, but there are more subtle ways in which accessing content +objects from chrome can cause security problems. + +| This is the problem that Xray vision is designed to solve. When a + script accesses an object using Xray vision it sees only the native + version of the object. Any expandos are invisible, and if any + properties of the object have been redefined, it sees the original + implementation, not the redefined version. +| So in the example above, chrome code calling the content's + ``window.confirm()`` would get the original version of ``confirm()``, + not the redefined version. + +.. note:: + + It's worth emphasizing that even if content tricks chrome into + running some unexpected code, that code does not run with chrome + privileges. So this is not a straightforward privilege escalation + attack, although it might lead to one if the chrome code is + sufficiently confused. + +.. _How_you_get_Xray_vision: + +How you get Xray vision +----------------------- + +Privileged code automatically gets Xray vision whenever it accesses +objects belonging to less-privileged code. So when chrome code accesses +content objects, it sees them with Xray vision: + +.. code:: JavaScript + + // chrome code + var transfer = gBrowser.contentWindow.confirm("Transfer all my money?"); + // calls the native implementation + +.. note:: + + Note that using window.confirm() would be a terrible way to implement + a security policy, and is only shown here to illustrate how Xray + vision works. + +.. _Waiving_Xray_vision: + +Waiving Xray vision +------------------- + +| Xray vision is a kind of security heuristic, designed to make most + common operations on untrusted objects simple and safe. However, there + are some operations for which they are too restrictive: for example, + if you need to see expandos on DOM objects. In cases like this you can + waive Xray protection, but then you can no longer rely on any + properties or functions being, or doing, what you expect. Any of them, + even setters and getters, could have been redefined by untrusted code. +| To waive Xray vision for an object you can use + Components.utils.waiveXrays(object), + or use the object's ``wrappedJSObject`` property: + +.. code:: JavaScript + + // chrome code + var waivedWindow = Components.utils.waiveXrays(gBrowser.contentWindow); + var transfer = waivedWindow.confirm("Transfer all my money?"); + // calls the redefined implementation + +.. code:: JavaScript + + // chrome code + var waivedWindow = gBrowser.contentWindow.wrappedJSObject; + var transfer = waivedWindow.confirm("Transfer all my money?"); + // calls the redefined implementation + +Waivers are transitive: so if you waive Xray vision for an object, then +you automatically waive it for all the object's properties. For example, +``window.wrappedJSObject.document`` gets you the waived version of +``document``. + +To undo the waiver again, call Components.utils.unwaiveXrays(waivedObject): + +.. code:: JavaScript + + var unwaived = Components.utils.unwaiveXrays(waivedWindow); + unwaived.confirm("Transfer all my money?"); + // calls the native implementation + +.. _Xrays_for_DOM_objects: + +Xrays for DOM objects +--------------------- + +The primary use of Xray vision is for DOM objects: that is, the +objects that represent parts of the web page. + +In Gecko, DOM objects have a dual representation: the canonical +representation is in C++, and this is reflected into JavaScript for the +benefit of JavaScript code. Any modifications to these objects, such as +adding expandos or redefining standard properties, stays in the +JavaScript reflection and does not affect the C++ representation. + +The dual representation enables an elegant implementation of Xrays: the +Xray just directly accesses the C++ representation of the original +object, and doesn't go to the content's JavaScript reflection at all. +Instead of filtering out modifications made by content, the Xray +short-circuits the content completely. + +This also makes the semantics of Xrays for DOM objects clear: they are +the same as the DOM specification, since that is defined using the +`WebIDL <http://www.w3.org/TR/WebIDL/>`__, and the WebIDL also defines +the C++ representation. + +.. _Xrays_for_JavaScript_objects: + +Xrays for JavaScript objects +---------------------------- + +Until recently, built-in JavaScript objects that are not part of the +DOM, such as +``Date``, ``Error``, and ``Object``, did not get Xray vision when +accessed by more-privileged code. + +Most of the time this is not a problem: the main concern Xrays solve is +with untrusted web content manipulating objects, and web content is +usually working with DOM objects. For example, if content code creates a +new ``Date`` object, it will usually be created as a property of a DOM +object, and then it will be filtered out by the DOM Xray: + +.. code:: JavaScript + + // content code + + // redefine Date.getFullYear() + Date.prototype.getFullYear = function() {return 1000}; + var date = new Date(); + +.. code:: JavaScript + + // chrome code + + // contentWindow is an Xray, and date is an expando on contentWindow + // so date is filtered out + gBrowser.contentWindow.date.getFullYear() + // -> TypeError: gBrowser.contentWindow.date is undefined + +The chrome code will only even see ``date`` if it waives Xrays, and +then, because waiving is transitive, it should expect to be vulnerable +to redefinition: + +.. code:: JavaScript + + // chrome code + + Components.utils.waiveXrays(gBrowser.contentWindow).date.getFullYear(); + // -> 1000 + +However, there are some situations in which privileged code will access +JavaScript objects that are not themselves DOM objects and are not +properties of DOM objects. For example: + +- the ``detail`` property of a CustomEvent fired by content could be a JavaScript + Object or Date as well as a string or a primitive +- the return value of ``evalInSandbox()`` and any properties attached to the + ``Sandbox`` object may be pure JavaScript objects + +Also, the WebIDL specifications are starting to use JavaScript types +such as ``Date`` and ``Promise``: since WebIDL definition is the basis +of DOM Xrays, not having Xrays for these JavaScript types starts to seem +arbitrary. + +So, in Gecko 31 and 32 we've added Xray support for most JavaScript +built-in objects. + +Like DOM objects, most JavaScript built-in objects have an underlying +C++ state that is separate from their JavaScript representation, so the +Xray implementation can go straight to the C++ state and guarantee that +the object will behave as its specification defines: + +.. code:: JavaScript + + // chrome code + + var sandboxScript = 'Date.prototype.getFullYear = function() {return 1000};' + + 'var date = new Date(); '; + + var sandbox = Components.utils.Sandbox("https://example.org/"); + Components.utils.evalInSandbox(sandboxScript, sandbox); + + // Date objects are Xrayed + console.log(sandbox.date.getFullYear()); + // -> 2014 + + // But you can waive Xray vision + console.log(Components.utils.waiveXrays(sandbox.date).getFullYear()); + // -> 1000 + +.. note:: + + To test out examples like this, you can use the Scratchpad in + browser context + for the code snippet, and the Browser Console to see the expected + output. + + Because code running in Scratchpad's browser context has chrome + privileges, any time you use it to run code, you need to understand + exactly what the code is doing. That includes the code samples in + this article. + +.. _Xray_semantics_for_Object_and_Array: + +Xray semantics for Object and Array +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The exceptions are ``Object`` +and ``Array``: their interesting state is in JavaScript, not C++. This +means that the semantics of their Xrays have to be independently +defined: they can't simply be defined as "the C++ representation". + +The aim of Xray vision is to make most common operations simple and +safe, avoiding the need to access the underlying object except in more +involved cases. So the semantics defined for ``Object`` and ``Array`` +Xrays aim to make it easy for privileged code to treat untrusted objects +like simple dictionaries. + +Any value properties +of the object are visible in the Xray. If the object has properties +which are themselves objects, and these objects are same-origin with the +content, then their value properties are visible as well. + +There are two main sorts of restrictions: + +- First, the chrome code might expect to rely on the prototype's + integrity, so the object's prototype is protected: + + - the Xray has the standard ``Object`` or ``Array`` prototype, + without any modifications that content may have done to that + prototype. The Xray always inherits from this standard prototype, + even if the underlying instance has a different prototype. + - if a script has created a property on an object instance that + shadows a property on the prototype, the shadowing property is not + visible in the Xray + +- Second, we want to prevent the chrome code from running content code, + so functions and accessor properties + of the object are not visible in the Xray. + +These rules are demonstrated in the script below, which evaluates a +script in a sandbox, then examines the object attached to the sandbox. + +.. note:: + + To test out examples like this, you can use the Scratchpad in + browser context for the code snippet, and the Browser Console + to see the expected output. + + Because code running in Scratchpad's browser context has chrome + privileges, any time you use it to run code, you need to understand + exactly what the code is doing. That includes the code samples in + this article. + +.. code:: JavaScript + + /* + The sandbox script: + * redefines Object.prototype.toSource() + * creates a Person() constructor that: + * defines a value property "firstName" using assignment + * defines a value property which shadows "constructor" + * defines a value property "address" which is a simple object + * defines a function fullName() + * using defineProperty, defines a value property on Person "lastName" + * using defineProperty, defines an accessor property on Person "middleName", + which has some unexpected accessor behavior + */ + + var sandboxScript = 'Object.prototype.toSource = function() {'+ + ' return "not what you expected?";' + + '};' + + 'function Person() {' + + ' this.constructor = "not a constructor";' + + ' this.firstName = "Joe";' + + ' this.address = {"street" : "Main Street"};' + + ' this.fullName = function() {' + + ' return this.firstName + " " + this.lastName;'+ + ' };' + + '};' + + 'var me = new Person();' + + 'Object.defineProperty(me, "lastName", {' + + ' enumerable: true,' + + ' configurable: true,' + + ' writable: true,' + + ' value: "Smith"' + + '});' + + 'Object.defineProperty(me, "middleName", {' + + ' enumerable: true,' + + ' configurable: true,' + + ' get: function() { return "wait, is this really a getter?"; }' + + '});'; + + var sandbox = Components.utils.Sandbox("https://example.org/"); + Components.utils.evalInSandbox(sandboxScript, sandbox); + + // 1) trying to access properties in the prototype that have been redefined + // (non-own properties) will show the original 'native' version + // note that functions are not included in the output + console.log("1) Property redefined in the prototype:"); + console.log(sandbox.me.toSource()); + // -> "({firstName:"Joe", address:{street:"Main Street"}, lastName:"Smith"})" + + // 2) trying to access properties on the object that shadow properties + // on the prototype will show the original 'native' version + console.log("2) Property that shadows the prototype:"); + console.log(sandbox.me.constructor); + // -> function() + + // 3) value properties defined by assignment to this are visible: + console.log("3) Value property defined by assignment to this:"); + console.log(sandbox.me.firstName); + // -> "Joe" + + // 4) value properties defined using defineProperty are visible: + console.log("4) Value property defined by defineProperty"); + console.log(sandbox.me.lastName); + // -> "Smith" + + // 5) accessor properties are not visible + console.log("5) Accessor property"); + console.log(sandbox.me.middleName); + // -> undefined + + // 6) accessing a value property of a value-property object is fine + console.log("6) Value property of a value-property object"); + console.log(sandbox.me.address.street); + // -> "Main Street" + + // 7) functions defined on the sandbox-defined object are not visible in the Xray + console.log("7) Call a function defined on the object"); + try { + console.log(sandbox.me.fullName()); + } + catch (e) { + console.error(e); + } + // -> TypeError: sandbox.me.fullName is not a function + + // now with waived Xrays + console.log("Now with waived Xrays"); + + console.log("1) Property redefined in the prototype:"); + console.log(Components.utils.waiveXrays(sandbox.me).toSource()); + // -> "not what you expected?" + + console.log("2) Property that shadows the prototype:"); + console.log(Components.utils.waiveXrays(sandbox.me).constructor); + // -> "not a constructor" + + console.log("3) Accessor property"); + console.log(Components.utils.waiveXrays(sandbox.me).middleName); + // -> "wait, is this really a getter?" + + console.log("4) Call a function defined on the object"); + console.log(Components.utils.waiveXrays(sandbox.me).fullName()); + // -> "Joe Smith" |