summaryrefslogtreecommitdiffstats
path: root/dom/docs/scriptSecurity
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/docs/scriptSecurity
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.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.pngbin0 -> 208057 bytes
-rw-r--r--dom/docs/scriptSecurity/images/computing-a-wrapper.pngbin0 -> 282979 bytes
-rw-r--r--dom/docs/scriptSecurity/images/cross-compartment-wrapper.pngbin0 -> 53671 bytes
-rw-r--r--dom/docs/scriptSecurity/images/cross-origin-wrapper.pngbin0 -> 56232 bytes
-rw-r--r--dom/docs/scriptSecurity/images/opaque-wrapper.pngbin0 -> 55336 bytes
-rw-r--r--dom/docs/scriptSecurity/images/principal-relationships.pngbin0 -> 147630 bytes
-rw-r--r--dom/docs/scriptSecurity/images/same-origin-wrapper.pngbin0 -> 56603 bytes
-rw-r--r--dom/docs/scriptSecurity/images/xray-wrapper.pngbin0 -> 55418 bytes
-rw-r--r--dom/docs/scriptSecurity/index.rst318
-rw-r--r--dom/docs/scriptSecurity/xray_vision.rst411
10 files changed, 729 insertions, 0 deletions
diff --git a/dom/docs/scriptSecurity/images/compartments.png b/dom/docs/scriptSecurity/images/compartments.png
new file mode 100644
index 0000000000..c80ea166ca
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/compartments.png
Binary files differ
diff --git a/dom/docs/scriptSecurity/images/computing-a-wrapper.png b/dom/docs/scriptSecurity/images/computing-a-wrapper.png
new file mode 100644
index 0000000000..1221ff4a00
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/computing-a-wrapper.png
Binary files differ
diff --git a/dom/docs/scriptSecurity/images/cross-compartment-wrapper.png b/dom/docs/scriptSecurity/images/cross-compartment-wrapper.png
new file mode 100644
index 0000000000..ba31355d44
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/cross-compartment-wrapper.png
Binary files differ
diff --git a/dom/docs/scriptSecurity/images/cross-origin-wrapper.png b/dom/docs/scriptSecurity/images/cross-origin-wrapper.png
new file mode 100644
index 0000000000..14b7022c3d
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/cross-origin-wrapper.png
Binary files differ
diff --git a/dom/docs/scriptSecurity/images/opaque-wrapper.png b/dom/docs/scriptSecurity/images/opaque-wrapper.png
new file mode 100644
index 0000000000..67cd87ba5b
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/opaque-wrapper.png
Binary files differ
diff --git a/dom/docs/scriptSecurity/images/principal-relationships.png b/dom/docs/scriptSecurity/images/principal-relationships.png
new file mode 100644
index 0000000000..0a4e7014f6
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/principal-relationships.png
Binary files differ
diff --git a/dom/docs/scriptSecurity/images/same-origin-wrapper.png b/dom/docs/scriptSecurity/images/same-origin-wrapper.png
new file mode 100644
index 0000000000..ec966afa1b
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/same-origin-wrapper.png
Binary files differ
diff --git a/dom/docs/scriptSecurity/images/xray-wrapper.png b/dom/docs/scriptSecurity/images/xray-wrapper.png
new file mode 100644
index 0000000000..43316d9089
--- /dev/null
+++ b/dom/docs/scriptSecurity/images/xray-wrapper.png
Binary files differ
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"