LCOV - code coverage report
Current view: directory - js/xpconnect/wrappers - AccessCheck.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 247 15 6.1 %
Date: 2012-06-02 Functions: 19 4 21.1 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=4 sw=4 et tw=99 ft=cpp:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is mozilla.org code, released
      18                 :  * June 24, 2010.
      19                 :  *
      20                 :  * The Initial Developer of the Original Code is
      21                 :  *    The Mozilla Foundation
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *    Andreas Gal <gal@mozilla.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/Util.h"
      41                 : 
      42                 : #include "AccessCheck.h"
      43                 : 
      44                 : #include "nsJSPrincipals.h"
      45                 : #include "nsIDOMWindow.h"
      46                 : #include "nsIDOMWindowCollection.h"
      47                 : #include "nsContentUtils.h"
      48                 : 
      49                 : #include "XPCWrapper.h"
      50                 : #include "XrayWrapper.h"
      51                 : #include "FilteringWrapper.h"
      52                 : #include "WrapperFactory.h"
      53                 : 
      54                 : #include "jsfriendapi.h"
      55                 : 
      56                 : using namespace mozilla;
      57                 : using namespace js;
      58                 : 
      59                 : namespace xpc {
      60                 : 
      61                 : nsIPrincipal *
      62         1579142 : GetCompartmentPrincipal(JSCompartment *compartment)
      63                 : {
      64         1579142 :     return nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
      65                 : }
      66                 : 
      67                 : bool
      68               0 : AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b)
      69                 : {
      70               0 :     nsIPrincipal *aprin = GetCompartmentPrincipal(a);
      71               0 :     nsIPrincipal *bprin = GetCompartmentPrincipal(b);
      72                 : 
      73                 :     // If either a or b doesn't have principals, we don't have enough
      74                 :     // information to tell. Seeing as how this is Gecko, we are default-unsafe
      75                 :     // in this case.
      76               0 :     if (!aprin || !bprin)
      77               0 :         return true;
      78                 : 
      79                 :     bool equals;
      80               0 :     nsresult rv = aprin->EqualsIgnoringDomain(bprin, &equals);
      81               0 :     if (NS_FAILED(rv)) {
      82               0 :         NS_ERROR("unable to ask about equality");
      83               0 :         return false;
      84                 :     }
      85                 : 
      86               0 :     return equals;
      87                 : }
      88                 : 
      89                 : bool
      90               0 : AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper)
      91                 : {
      92                 :     // Location objects are parented to the outer window for which they
      93                 :     // were created. This gives us an easy way to determine whether our
      94                 :     // object is same origin with the current inner window:
      95                 : 
      96                 :     // Grab the outer window...
      97               0 :     JSObject *obj = js::GetObjectParent(js::UnwrapObject(wrapper));
      98               0 :     if (!js::GetObjectClass(obj)->ext.innerObject) {
      99                 :         // ...which might be wrapped in a security wrapper.
     100               0 :         obj = js::UnwrapObject(obj);
     101               0 :         JS_ASSERT(js::GetObjectClass(obj)->ext.innerObject);
     102                 :     }
     103                 : 
     104                 :     // Now innerize it to find the *current* inner window for our outer.
     105               0 :     obj = JS_ObjectToInnerObject(cx, obj);
     106                 : 
     107                 :     // Which lets us compare the current compartment against the old one.
     108                 :     return obj &&
     109                 :            (isSameOrigin(js::GetObjectCompartment(wrapper),
     110               0 :                          js::GetObjectCompartment(obj)) ||
     111               0 :             documentDomainMakesSameOrigin(cx, obj));
     112                 : }
     113                 : 
     114                 : bool
     115          353233 : AccessCheck::isChrome(JSCompartment *compartment)
     116                 : {
     117          353233 :     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     118          353233 :     if (!ssm) {
     119               0 :         return false;
     120                 :     }
     121                 : 
     122                 :     bool privileged;
     123          353233 :     nsIPrincipal *principal = GetCompartmentPrincipal(compartment);
     124          353233 :     return NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) && privileged;
     125                 : }
     126                 : 
     127                 : nsIPrincipal *
     128         1225564 : AccessCheck::getPrincipal(JSCompartment *compartment)
     129                 : {
     130         1225564 :     return GetCompartmentPrincipal(compartment);
     131                 : }
     132                 : 
     133                 : #define NAME(ch, str, cases)                                                  \
     134                 :     case ch: if (!strcmp(name, str)) switch (propChars[0]) { cases }; break;
     135                 : #define PROP(ch, actions) case ch: { actions }; break;
     136                 : #define RW(str) if (JS_FlatStringEqualsAscii(prop, str)) return true;
     137                 : #define R(str) if (!set && JS_FlatStringEqualsAscii(prop, str)) return true;
     138                 : #define W(str) if (set && JS_FlatStringEqualsAscii(prop, str)) return true;
     139                 : 
     140                 : // Hardcoded policy for cross origin property access. This was culled from the
     141                 : // preferences file (all.js). We don't want users to overwrite highly sensitive
     142                 : // security policies.
     143                 : static bool
     144               0 : IsPermitted(const char *name, JSFlatString *prop, bool set)
     145                 : {
     146                 :     size_t propLength;
     147                 :     const jschar *propChars =
     148               0 :         JS_GetInternedStringCharsAndLength(JS_FORGET_STRING_FLATNESS(prop), &propLength);
     149               0 :     if (!propLength)
     150               0 :         return false;
     151               0 :     switch (name[0]) {
     152               0 :         NAME('D', "DOMException",
     153                 :              PROP('c', RW("code"))
     154                 :              PROP('m', RW("message"))
     155                 :              PROP('n', RW("name"))
     156                 :              PROP('r', RW("result"))
     157                 :              PROP('t', R("toString")))
     158               0 :         NAME('E', "Error",
     159                 :              PROP('m', R("message")))
     160               0 :         NAME('H', "History",
     161                 :              PROP('b', R("back"))
     162                 :              PROP('f', R("forward"))
     163                 :              PROP('g', R("go")))
     164               0 :         NAME('L', "Location",
     165                 :              PROP('h', W("hash") W("href"))
     166                 :              PROP('r', R("replace")))
     167               0 :         NAME('W', "Window",
     168                 :              PROP('b', R("blur"))
     169                 :              PROP('c', R("close") R("closed"))
     170                 :              PROP('f', R("focus") R("frames"))
     171                 :              PROP('h', R("history"))
     172                 :              PROP('l', RW("location") R("length"))
     173                 :              PROP('o', R("opener"))
     174                 :              PROP('p', R("parent") R("postMessage"))
     175                 :              PROP('s', R("self"))
     176                 :              PROP('t', R("top"))
     177                 :              PROP('w', R("window")))
     178                 :     }
     179               0 :     return false;
     180                 : }
     181                 : 
     182                 : #undef NAME
     183                 : #undef RW
     184                 : #undef R
     185                 : #undef W
     186                 : 
     187                 : static bool
     188               0 : IsFrameId(JSContext *cx, JSObject *obj, jsid id)
     189                 : {
     190               0 :     XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
     191               0 :     if (!wn) {
     192               0 :         return false;
     193                 :     }
     194                 : 
     195               0 :     nsCOMPtr<nsIDOMWindow> domwin(do_QueryWrappedNative(wn));
     196               0 :     if (!domwin) {
     197               0 :         return false;
     198                 :     }
     199                 : 
     200               0 :     nsCOMPtr<nsIDOMWindowCollection> col;
     201               0 :     domwin->GetFrames(getter_AddRefs(col));
     202               0 :     if (!col) {
     203               0 :         return false;
     204                 :     }
     205                 : 
     206               0 :     if (JSID_IS_INT(id)) {
     207               0 :         col->Item(JSID_TO_INT(id), getter_AddRefs(domwin));
     208               0 :     } else if (JSID_IS_STRING(id)) {
     209               0 :         nsAutoString str(JS_GetInternedStringChars(JSID_TO_STRING(id)));
     210               0 :         col->NamedItem(str, getter_AddRefs(domwin));
     211                 :     } else {
     212               0 :         return false;
     213                 :     }
     214                 : 
     215               0 :     return domwin != nsnull;
     216                 : }
     217                 : 
     218                 : static bool
     219               0 : IsWindow(const char *name)
     220                 : {
     221               0 :     return name[0] == 'W' && !strcmp(name, "Window");
     222                 : }
     223                 : 
     224                 : static bool
     225               0 : IsLocation(const char *name)
     226                 : {
     227               0 :     return name[0] == 'L' && !strcmp(name, "Location");
     228                 : }
     229                 : 
     230                 : static nsIPrincipal *
     231               0 : GetPrincipal(JSObject *obj)
     232                 : {
     233               0 :     NS_ASSERTION(!IS_SLIM_WRAPPER(obj), "global object is a slim wrapper?");
     234               0 :     if (!IS_WN_WRAPPER(obj)) {
     235               0 :         NS_ASSERTION(!(~js::GetObjectClass(obj)->flags &
     236                 :                        (JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE)),
     237                 :                      "bad object");
     238                 :         nsCOMPtr<nsIScriptObjectPrincipal> objPrin =
     239               0 :             do_QueryInterface((nsISupports*)xpc_GetJSPrivate(obj));
     240               0 :         NS_ASSERTION(objPrin, "global isn't nsIScriptObjectPrincipal?");
     241               0 :         return objPrin->GetPrincipal();
     242                 :     }
     243                 : 
     244               0 :     nsIXPConnect *xpc = nsXPConnect::GetRuntimeInstance()->GetXPConnect();
     245               0 :     return xpc->GetPrincipal(obj, true);
     246                 : }
     247                 : 
     248                 : bool
     249               0 : AccessCheck::documentDomainMakesSameOrigin(JSContext *cx, JSObject *obj)
     250                 : {
     251               0 :     JSObject *scope = nsnull;
     252               0 :     JSStackFrame *fp = nsnull;
     253               0 :     JS_FrameIterator(cx, &fp);
     254               0 :     if (fp) {
     255               0 :         while (!JS_IsScriptFrame(cx, fp)) {
     256               0 :             if (!JS_FrameIterator(cx, &fp))
     257               0 :                 break;
     258                 :         }
     259                 : 
     260               0 :         if (fp)
     261               0 :             scope = JS_GetGlobalForFrame(fp);
     262                 :     }
     263                 : 
     264               0 :     if (!scope)
     265               0 :         scope = JS_GetGlobalForScopeChain(cx);
     266                 : 
     267                 :     nsIPrincipal *subject;
     268                 :     nsIPrincipal *object;
     269                 : 
     270                 :     {
     271               0 :         JSAutoEnterCompartment ac;
     272                 : 
     273               0 :         if (!ac.enter(cx, scope))
     274               0 :             return false;
     275                 : 
     276               0 :         subject = GetPrincipal(scope);
     277                 :     }
     278                 : 
     279               0 :     if (!subject)
     280               0 :         return false;
     281                 : 
     282                 :     {
     283               0 :         JSAutoEnterCompartment ac;
     284                 : 
     285               0 :         if (!ac.enter(cx, obj))
     286               0 :             return false;
     287                 : 
     288               0 :         object = GetPrincipal(JS_GetGlobalForObject(cx, obj));
     289                 :     }
     290                 : 
     291                 :     bool subsumes;
     292               0 :     return NS_SUCCEEDED(subject->Subsumes(object, &subsumes)) && subsumes;
     293                 : }
     294                 : 
     295                 : bool
     296               0 : AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid id,
     297                 :                                           Wrapper::Action act)
     298                 : {
     299               0 :     if (!XPCWrapper::GetSecurityManager())
     300               0 :         return true;
     301                 : 
     302               0 :     if (act == Wrapper::CALL)
     303               0 :         return true;
     304                 : 
     305               0 :     JSObject *obj = Wrapper::wrappedObject(wrapper);
     306                 : 
     307                 :     const char *name;
     308               0 :     js::Class *clasp = js::GetObjectClass(obj);
     309               0 :     NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here");
     310               0 :     if (clasp->ext.innerObject)
     311               0 :         name = "Window";
     312                 :     else
     313               0 :         name = clasp->name;
     314                 : 
     315               0 :     if (JSID_IS_STRING(id)) {
     316               0 :         if (IsPermitted(name, JSID_TO_FLAT_STRING(id), act == Wrapper::SET))
     317               0 :             return true;
     318                 :     }
     319                 : 
     320               0 :     if (IsWindow(name) && IsFrameId(cx, obj, id))
     321               0 :         return true;
     322                 : 
     323                 :     // We only reach this point for cross origin location objects (see
     324                 :     // SameOriginOrCrossOriginAccessiblePropertiesOnly::check).
     325               0 :     if (!IsLocation(name) && documentDomainMakesSameOrigin(cx, obj))
     326               0 :         return true;
     327                 : 
     328                 :     return (act == Wrapper::SET)
     329                 :            ? nsContentUtils::IsCallerTrustedForWrite()
     330               0 :            : nsContentUtils::IsCallerTrustedForRead();
     331                 : }
     332                 : 
     333                 : bool
     334               0 : AccessCheck::isSystemOnlyAccessPermitted(JSContext *cx)
     335                 : {
     336               0 :     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     337               0 :     if (!ssm) {
     338               0 :         return true;
     339                 :     }
     340                 : 
     341                 :     JSStackFrame *fp;
     342               0 :     nsIPrincipal *principal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp);
     343               0 :     if (!principal) {
     344               0 :         return false;
     345                 :     }
     346                 : 
     347               0 :     if (!fp) {
     348               0 :         if (!JS_FrameIterator(cx, &fp)) {
     349                 :             // No code at all is running. So we must be arriving here as the result
     350                 :             // of C++ code asking us to do something. Allow access.
     351               0 :             return true;
     352                 :         }
     353                 : 
     354                 :         // Some code is running, we can't make the assumption, as above, but we
     355                 :         // can't use a native frame, so clear fp.
     356               0 :         fp = NULL;
     357               0 :     } else if (!JS_IsScriptFrame(cx, fp)) {
     358               0 :         fp = NULL;
     359                 :     }
     360                 : 
     361                 :     bool privileged;
     362               0 :     if (NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) &&
     363                 :         privileged) {
     364               0 :         return true;
     365                 :     }
     366                 : 
     367                 :     // Allow any code loaded from chrome://global/ to touch us, even if it was
     368                 :     // cloned into a less privileged context.
     369                 :     static const char prefix[] = "chrome://global/";
     370                 :     const char *filename;
     371               0 :     if (fp &&
     372               0 :         (filename = JS_GetScriptFilename(cx, JS_GetFrameScript(cx, fp))) &&
     373               0 :         !strncmp(filename, prefix, ArrayLength(prefix) - 1)) {
     374               0 :         return true;
     375                 :     }
     376                 : 
     377               0 :     return NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) && privileged;
     378                 : }
     379                 : 
     380                 : bool
     381               0 : AccessCheck::needsSystemOnlyWrapper(JSObject *obj)
     382                 : {
     383               0 :     if (!IS_WN_WRAPPER(obj))
     384               0 :         return false;
     385                 : 
     386               0 :     XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(js::GetObjectPrivate(obj));
     387               0 :     return wn->NeedsSOW();
     388                 : }
     389                 : 
     390                 : bool
     391              46 : AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper)
     392                 : {
     393              46 :     JS_ASSERT(js::IsWrapper(wrapper));
     394                 : 
     395                 :     unsigned flags;
     396              46 :     JSObject *obj = js::UnwrapObject(wrapper, true, &flags);
     397                 : 
     398                 :     // If the wrapper indicates script-only access, we are done.
     399              46 :     if (flags & WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG) {
     400               0 :         if (flags & WrapperFactory::SOW_FLAG)
     401               0 :             return !isSystemOnlyAccessPermitted(cx);
     402                 : 
     403               0 :         if (flags & WrapperFactory::PARTIALLY_TRANSPARENT)
     404               0 :             return !XrayUtils::IsTransparent(cx, wrapper);
     405                 : 
     406               0 :         nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     407               0 :         if (!ssm)
     408               0 :             return true;
     409                 : 
     410                 :         // Bypass script-only status if UniversalXPConnect is enabled.
     411                 :         bool privileged;
     412               0 :         return !NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) ||
     413               0 :                !privileged;
     414                 :     }
     415                 : 
     416                 :     // In addition, chrome objects can explicitly opt-in by setting .scriptOnly to true.
     417              46 :     if (js::GetProxyHandler(wrapper) ==
     418                 :         &FilteringWrapper<CrossCompartmentSecurityWrapper,
     419                 :         CrossOriginAccessiblePropertiesOnly>::singleton) {
     420               0 :         jsid scriptOnlyId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_SCRIPTONLY);
     421                 :         jsval scriptOnly;
     422               0 :         if (JS_LookupPropertyById(cx, obj, scriptOnlyId, &scriptOnly) &&
     423               0 :             scriptOnly == JSVAL_TRUE)
     424               0 :             return true; // script-only
     425                 :     }
     426                 : 
     427                 :     // Allow non-script access to same-origin location objects and any other
     428                 :     // objects.
     429              46 :     return WrapperFactory::IsLocationObject(obj) && !isLocationObjectSameOrigin(cx, wrapper);
     430                 : }
     431                 : 
     432                 : void
     433               0 : AccessCheck::deny(JSContext *cx, jsid id)
     434                 : {
     435               0 :     if (id == JSID_VOID) {
     436               0 :         JS_ReportError(cx, "Permission denied to access object");
     437                 :     } else {
     438                 :         jsval idval;
     439               0 :         if (!JS_IdToValue(cx, id, &idval))
     440               0 :             return;
     441               0 :         JSString *str = JS_ValueToString(cx, idval);
     442               0 :         if (!str)
     443               0 :             return;
     444               0 :         const jschar *chars = JS_GetStringCharsZ(cx, str);
     445               0 :         if (chars)
     446               0 :             JS_ReportError(cx, "Permission denied to access property '%hs'", chars);
     447                 :     }
     448                 : }
     449                 : 
     450                 : enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
     451                 : 
     452                 : static bool
     453               0 : Deny(JSContext *cx, jsid id, Wrapper::Action act)
     454                 : {
     455                 :     // Refuse to perform the action and just return the default value.
     456               0 :     if (act == Wrapper::GET)
     457               0 :         return true;
     458                 :     // If its a set, deny it and throw an exception.
     459               0 :     AccessCheck::deny(cx, id);
     460               0 :     return false;
     461                 : }
     462                 : 
     463                 : bool
     464               0 : PermitIfUniversalXPConnect(JSContext *cx, jsid id, Wrapper::Action act,
     465                 :                            ExposedPropertiesOnly::Permission &perm)
     466                 : {
     467                 :     // If UniversalXPConnect is enabled, allow access even if __exposedProps__ doesn't
     468                 :     // exists.
     469               0 :     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     470               0 :     if (!ssm) {
     471               0 :         return false;
     472                 :     }
     473                 :     bool privileged;
     474               0 :     if (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &privileged)) &&
     475                 :         privileged) {
     476               0 :         perm = ExposedPropertiesOnly::PermitPropertyAccess;
     477               0 :         return true; // Allow
     478                 :     }
     479                 : 
     480                 :     // Deny
     481               0 :     return Deny(cx, id, act);
     482                 : }
     483                 : 
     484                 : bool
     485               0 : ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act,
     486                 :                              Permission &perm)
     487                 : {
     488               0 :     JSObject *wrappedObject = Wrapper::wrappedObject(wrapper);
     489                 : 
     490               0 :     if (act == Wrapper::CALL) {
     491               0 :         perm = PermitObjectAccess;
     492               0 :         return true;
     493                 :     }
     494                 : 
     495               0 :     perm = DenyAccess;
     496                 : 
     497               0 :     jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS);
     498                 : 
     499               0 :     JSBool found = false;
     500               0 :     JSAutoEnterCompartment ac;
     501               0 :     if (!ac.enter(cx, wrappedObject) ||
     502               0 :         !JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found))
     503               0 :         return false;
     504                 : 
     505                 :     // Always permit access to "length" and indexed properties of arrays.
     506               0 :     if (JS_IsArrayObject(cx, wrappedObject) &&
     507               0 :         ((JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) ||
     508               0 :          (JSID_IS_STRING(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length")))) {
     509               0 :         perm = PermitPropertyAccess;
     510               0 :         return true; // Allow
     511                 :     }
     512                 : 
     513                 :     // If no __exposedProps__ existed, deny access.
     514               0 :     if (!found) {
     515                 :         // For now, only do this on functions.
     516               0 :         if (!JS_ObjectIsFunction(cx, wrappedObject)) {
     517               0 :             perm = PermitPropertyAccess;
     518               0 :             return true;
     519                 :         }
     520               0 :         return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
     521                 :     }
     522                 : 
     523               0 :     if (id == JSID_VOID) {
     524                 :         // This will force the caller to call us back for individual property accesses.
     525               0 :         perm = PermitPropertyAccess;
     526               0 :         return true;
     527                 :     }
     528                 : 
     529                 :     jsval exposedProps;
     530               0 :     if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps))
     531               0 :         return false;
     532                 : 
     533               0 :     if (JSVAL_IS_VOID(exposedProps) || JSVAL_IS_NULL(exposedProps)) {
     534               0 :         return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
     535                 :     }
     536                 : 
     537               0 :     if (!JSVAL_IS_OBJECT(exposedProps)) {
     538               0 :         JS_ReportError(cx, "__exposedProps__ must be undefined, null, or an Object");
     539               0 :         return false;
     540                 :     }
     541                 : 
     542               0 :     JSObject *hallpass = JSVAL_TO_OBJECT(exposedProps);
     543                 : 
     544               0 :     Access access = NO_ACCESS;
     545                 : 
     546                 :     JSPropertyDescriptor desc;
     547               0 :     if (!JS_GetPropertyDescriptorById(cx, hallpass, id, JSRESOLVE_QUALIFIED, &desc)) {
     548               0 :         return false; // Error
     549                 :     }
     550               0 :     if (desc.obj == NULL || !(desc.attrs & JSPROP_ENUMERATE)) {
     551               0 :         return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
     552                 :     }
     553                 : 
     554               0 :     if (!JSVAL_IS_STRING(desc.value)) {
     555               0 :         JS_ReportError(cx, "property must be a string");
     556               0 :         return false;
     557                 :     }
     558                 : 
     559               0 :     JSString *str = JSVAL_TO_STRING(desc.value);
     560                 :     size_t length;
     561               0 :     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
     562               0 :     if (!chars)
     563               0 :         return false;
     564                 : 
     565               0 :     for (size_t i = 0; i < length; ++i) {
     566               0 :         switch (chars[i]) {
     567                 :         case 'r':
     568               0 :             if (access & READ) {
     569               0 :                 JS_ReportError(cx, "duplicate 'readable' property flag");
     570               0 :                 return false;
     571                 :             }
     572               0 :             access = Access(access | READ);
     573               0 :             break;
     574                 : 
     575                 :         case 'w':
     576               0 :             if (access & WRITE) {
     577               0 :                 JS_ReportError(cx, "duplicate 'writable' property flag");
     578               0 :                 return false;
     579                 :             }
     580               0 :             access = Access(access | WRITE);
     581               0 :             break;
     582                 : 
     583                 :         default:
     584               0 :             JS_ReportError(cx, "properties can only be readable or read and writable");
     585               0 :             return false;
     586                 :         }
     587                 :     }
     588                 : 
     589               0 :     if (access == NO_ACCESS) {
     590               0 :         JS_ReportError(cx, "specified properties must have a permission bit set");
     591               0 :         return false;
     592                 :     }
     593                 : 
     594               0 :     if ((act == Wrapper::SET && !(access & WRITE)) ||
     595               0 :         (act != Wrapper::SET && !(access & READ))) {
     596               0 :         return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
     597                 :     }
     598                 : 
     599               0 :     perm = PermitPropertyAccess;
     600               0 :     return true; // Allow
     601                 : }
     602                 : 
     603                 : }

Generated by: LCOV version 1.7