diff options
Diffstat (limited to 'Source/WebCore/bindings/js/JSLocationCustom.cpp')
-rw-r--r-- | Source/WebCore/bindings/js/JSLocationCustom.cpp | 228 |
1 files changed, 90 insertions, 138 deletions
diff --git a/Source/WebCore/bindings/js/JSLocationCustom.cpp b/Source/WebCore/bindings/js/JSLocationCustom.cpp index c99f904d8..57eb752da 100644 --- a/Source/WebCore/bindings/js/JSLocationCustom.cpp +++ b/Source/WebCore/bindings/js/JSLocationCustom.cpp @@ -23,31 +23,23 @@ #include "config.h" #include "JSLocation.h" -#include "Location.h" +#include "JSDOMBinding.h" +#include "JSDOMBindingSecurity.h" +#include "JSDOMExceptionHandling.h" +#include "RuntimeApplicationChecks.h" #include <runtime/JSFunction.h> +#include <runtime/Lookup.h> using namespace JSC; namespace WebCore { -static EncodedJSValue nonCachingStaticReplaceFunctionGetter(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName propertyName) +bool JSLocation::getOwnPropertySlotDelegate(ExecState* state, PropertyName propertyName, PropertySlot& slot) { - return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 1, propertyName.publicName(), jsLocationPrototypeFunctionReplace)); -} - -static EncodedJSValue nonCachingStaticReloadFunctionGetter(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName propertyName) -{ - return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 0, propertyName.publicName(), jsLocationPrototypeFunctionReload)); -} + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); -static EncodedJSValue nonCachingStaticAssignFunctionGetter(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName propertyName) -{ - return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 1, propertyName.publicName(), jsLocationPrototypeFunctionAssign)); -} - -bool JSLocation::getOwnPropertySlotDelegate(ExecState* exec, PropertyName propertyName, PropertySlot& slot) -{ - Frame* frame = impl().frame(); + Frame* frame = wrapped().frame(); if (!frame) { slot.setUndefined(); return true; @@ -59,56 +51,55 @@ bool JSLocation::getOwnPropertySlotDelegate(ExecState* exec, PropertyName proper // Our custom code is only needed to implement the Window cross-domain scheme, so if access is // allowed, return false so the normal lookup will take place. String message; - if (shouldAllowAccessToFrame(exec, frame, message)) + if (BindingSecurity::shouldAllowAccessToFrame(*state, *frame, message)) return false; - // Check for the few functions that we allow, even when called cross-domain. - // Make these read-only / non-configurable to prevent writes via defineProperty. - const HashEntry* entry = JSLocationPrototype::info()->propHashTable(exec)->entry(exec, propertyName); - if (entry && (entry->attributes() & JSC::Function)) { - if (entry->function() == jsLocationPrototypeFunctionReplace) { - slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticReplaceFunctionGetter); - return true; - } else if (entry->function() == jsLocationPrototypeFunctionReload) { - slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticReloadFunctionGetter); - return true; - } else if (entry->function() == jsLocationPrototypeFunctionAssign) { - slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticAssignFunctionGetter); - return true; - } + // https://html.spec.whatwg.org/#crossorigingetownpropertyhelper-(-o,-p-) + if (propertyName == state->propertyNames().toStringTagSymbol || propertyName == state->propertyNames().hasInstanceSymbol || propertyName == state->propertyNames().isConcatSpreadableSymbol) { + slot.setValue(this, ReadOnly | DontEnum, jsUndefined()); + return true; } - // FIXME: Other implementers of the Window cross-domain scheme (Window, History) allow toString, - // but for now we have decided not to, partly because it seems silly to return "[Object Location]" in - // such cases when normally the string form of Location would be the URL. + // We only allow access to Location.replace() cross origin. + if (propertyName == state->propertyNames().replace) { + slot.setCustom(this, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsLocationInstanceFunctionReplace, 1>); + return true; + } - printErrorMessageForFrame(frame, message); + // Getting location.href cross origin needs to throw. However, getOwnPropertyDescriptor() needs to return + // a descriptor that has a setter but no getter. + if (slot.internalMethodType() == PropertySlot::InternalMethodType::GetOwnProperty && propertyName == state->propertyNames().href) { + auto* entry = JSLocation::info()->staticPropHashTable->entry(propertyName); + CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, nullptr, entry->propertyPutter()); + slot.setCustomGetterSetter(this, DontEnum | CustomAccessor, customGetterSetter); + return true; + } + + throwSecurityError(*state, scope, message); slot.setUndefined(); return true; } -bool JSLocation::putDelegate(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +bool JSLocation::putDelegate(ExecState* state, PropertyName propertyName, JSValue, PutPropertySlot&, bool& putResult) { - Frame* frame = impl().frame(); + putResult = false; + + Frame* frame = wrapped().frame(); if (!frame) return true; - if (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf) + // Silently block access to toString and valueOf. + if (propertyName == state->propertyNames().toString || propertyName == state->propertyNames().valueOf) return true; - bool sameDomainAccess = shouldAllowAccessToFrame(exec, frame); - - const HashEntry* entry = JSLocation::info()->propHashTable(exec)->entry(exec, propertyName); - if (!entry) { - if (sameDomainAccess) - JSObject::put(this, exec, propertyName, value, slot); - return true; - } + // Always allow assigning to the whole location. + // However, alllowing assigning of pieces might inadvertently disclose parts of the original location. + // So fall through to the access check for those. + if (propertyName == state->propertyNames().href) + return false; - // Cross-domain access to the location is allowed when assigning the whole location, - // but not when assigning the individual pieces, since that might inadvertently - // disclose other parts of the original location. - if (entry->propertyPutter() != setJSLocationHref && !sameDomainAccess) + // Block access and throw if there is a security error. + if (!BindingSecurity::shouldAllowAccessToFrame(state, frame, ThrowSecurityError)) return true; return false; @@ -118,7 +109,7 @@ bool JSLocation::deleteProperty(JSCell* cell, ExecState* exec, PropertyName prop { JSLocation* thisObject = jsCast<JSLocation*>(cell); // Only allow deleting by frames in the same origin. - if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame())) + if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), ThrowSecurityError)) return false; return Base::deleteProperty(thisObject, exec, propertyName); } @@ -127,128 +118,89 @@ bool JSLocation::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned p { JSLocation* thisObject = jsCast<JSLocation*>(cell); // Only allow deleting by frames in the same origin. - if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame())) + if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), ThrowSecurityError)) return false; return Base::deletePropertyByIndex(thisObject, exec, propertyName); } -void JSLocation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +// https://html.spec.whatwg.org/#crossoriginproperties-(-o-) +static void addCrossOriginPropertyNames(ExecState& state, PropertyNameArray& propertyNames) { - JSLocation* thisObject = jsCast<JSLocation*>(object); - // Only allow the location object to enumerated by frames in the same origin. - if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame())) - return; - Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode); + static const Identifier* const properties[] = { &state.propertyNames().href, &state.propertyNames().replace }; + for (auto* property : properties) + propertyNames.add(*property); } -bool JSLocation::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) +// https://html.spec.whatwg.org/#crossoriginownpropertykeys-(-o-) +static void addCrossOriginOwnPropertyNames(ExecState& state, PropertyNameArray& propertyNames) { - if (descriptor.isAccessorDescriptor() && (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf)) - return false; - return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); -} + addCrossOriginPropertyNames(state, propertyNames); -void JSLocation::setHref(ExecState* exec, JSValue value) -{ - String href = value.toString(exec)->value(exec); - if (exec->hadException()) - return; - impl().setHref(href, activeDOMWindow(exec), firstDOMWindow(exec)); + propertyNames.add(state.propertyNames().toStringTagSymbol); + propertyNames.add(state.propertyNames().hasInstanceSymbol); + propertyNames.add(state.propertyNames().isConcatSpreadableSymbol); } -void JSLocation::setProtocol(ExecState* exec, JSValue value) +void JSLocation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - String protocol = value.toString(exec)->value(exec); - if (exec->hadException()) + JSLocation* thisObject = jsCast<JSLocation*>(object); + if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), DoNotReportSecurityError)) { + if (mode.includeDontEnumProperties()) + addCrossOriginOwnPropertyNames(*exec, propertyNames); return; - ExceptionCode ec = 0; - impl().setProtocol(protocol, activeDOMWindow(exec), firstDOMWindow(exec), ec); - setDOMException(exec, ec); + } + Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode); } -void JSLocation::setHost(ExecState* exec, JSValue value) +bool JSLocation::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) { - String host = value.toString(exec)->value(exec); - if (exec->hadException()) - return; - impl().setHost(host, activeDOMWindow(exec), firstDOMWindow(exec)); -} + JSLocation* thisObject = jsCast<JSLocation*>(object); + if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), ThrowSecurityError)) + return false; -void JSLocation::setHostname(ExecState* exec, JSValue value) -{ - String hostname = value.toString(exec)->value(exec); - if (exec->hadException()) - return; - impl().setHostname(hostname, activeDOMWindow(exec), firstDOMWindow(exec)); + if (descriptor.isAccessorDescriptor() && (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf)) + return false; + return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); } -void JSLocation::setPort(ExecState* exec, JSValue value) +bool JSLocation::setPrototype(JSObject*, ExecState* exec, JSValue, bool shouldThrowIfCantSet) { - String port = value.toWTFString(exec); - if (exec->hadException()) - return; - impl().setPort(port, activeDOMWindow(exec), firstDOMWindow(exec)); -} + auto scope = DECLARE_THROW_SCOPE(exec->vm()); -void JSLocation::setPathname(ExecState* exec, JSValue value) -{ - String pathname = value.toString(exec)->value(exec); - if (exec->hadException()) - return; - impl().setPathname(pathname, activeDOMWindow(exec), firstDOMWindow(exec)); -} + if (shouldThrowIfCantSet) + throwTypeError(exec, scope, ASCIILiteral("Cannot set prototype of this object")); -void JSLocation::setSearch(ExecState* exec, JSValue value) -{ - String pathname = value.toString(exec)->value(exec); - if (exec->hadException()) - return; - impl().setSearch(pathname, activeDOMWindow(exec), firstDOMWindow(exec)); + return false; } -void JSLocation::setHash(ExecState* exec, JSValue value) +JSValue JSLocation::getPrototype(JSObject* object, ExecState* exec) { - String hash = value.toString(exec)->value(exec); - if (exec->hadException()) - return; - impl().setHash(hash, activeDOMWindow(exec), firstDOMWindow(exec)); -} + JSLocation* thisObject = jsCast<JSLocation*>(object); + if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), DoNotReportSecurityError)) + return jsNull(); -JSValue JSLocation::replace(ExecState* exec) -{ - String urlString = exec->argument(0).toString(exec)->value(exec); - if (exec->hadException()) - return jsUndefined(); - impl().replace(urlString, activeDOMWindow(exec), firstDOMWindow(exec)); - return jsUndefined(); + return Base::getPrototype(object, exec); } -JSValue JSLocation::reload(ExecState* exec) +bool JSLocation::preventExtensions(JSObject*, ExecState* exec) { - impl().reload(activeDOMWindow(exec)); - return jsUndefined(); -} + auto scope = DECLARE_THROW_SCOPE(exec->vm()); -JSValue JSLocation::assign(ExecState* exec) -{ - String urlString = exec->argument(0).toString(exec)->value(exec); - if (exec->hadException()) - return jsUndefined(); - impl().assign(urlString, activeDOMWindow(exec), firstDOMWindow(exec)); - return jsUndefined(); + throwTypeError(exec, scope, ASCIILiteral("Cannot prevent extensions on this object")); + return false; } -JSValue JSLocation::toStringFunction(ExecState* exec) +String JSLocation::toStringName(const JSObject* object, ExecState* exec) { - Frame* frame = impl().frame(); - if (!frame || !shouldAllowAccessToFrame(exec, frame)) - return jsUndefined(); - - return jsStringWithCache(exec, impl().toString()); + auto* thisObject = jsCast<const JSLocation*>(object); + if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), DoNotReportSecurityError)) + return ASCIILiteral("Object"); + return ASCIILiteral("Location"); } -bool JSLocationPrototype::putDelegate(ExecState* exec, PropertyName propertyName, JSValue, PutPropertySlot&) +bool JSLocationPrototype::putDelegate(ExecState* exec, PropertyName propertyName, JSValue, PutPropertySlot&, bool& putResult) { + putResult = false; return (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf); } |