LCOV - app.info - js/xpconnect/wrappers/AccessCheck.cpp

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-04-21 | 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         1600592 : GetCompartmentPrincipal(JSCompartment *compartment)
          63                 : {
          64         1600592 :     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          380426 : AccessCheck::isChrome(JSCompartment *compartment)
         116                 : {
         117          380426 :     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
         118          380426 :     if (!ssm) {
         119               0 :         return false;
         120                 :     }
         121                 :
         122                 :     bool privileged;
         123          380426 :     nsIPrincipal *principal = GetCompartmentPrincipal(compartment);
         124          380426 :     return NS_SUCCEEDED(ssm->IsSystemPrincipal(principal, &privileged)) && privileged;
         125                 : }
         126                 :
         127                 : nsIPrincipal *
         128         1219821 : AccessCheck::getPrincipal(JSCompartment *compartment)
         129                 : {
         130         1219821 :     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